Selene's infrastructure

Introduction

In this writeup, I will try to elaborate on the basic infrastructure driving Selene and her frontends. We will start at the very bottom, the mining, and work up to the top. Along the way we will reveal the inner workings of Miners, Converters, Presenters and Validators. This document is meant to serve as reference for future developers and patch-writers, and should be kept up to date as we approach a stable API for 1.0

Mining our data

In order to know what kind of fields we are to present foremost, information needs to be obtained about the type we are going to save our data to. A miner should inherit from the IControlMiner interface. Initially, this was done using reflection, though a method using Xml-deserialization was added later on. In addition to mining the type and name of the field in the class, these miners should also obtain metadata such as flags and the hierachy described in the following paragraph. Note that if performance is ever to be improved, this part of the code will play an important role, for reflection and disk I/O are not known to be particularly fast.

Hierachy: Manifest, Category, Subcategory and Control

For the purpose of presenting the input widgets in an orderly fashion, Selene provides a hierachy lining out the basic structure of the form. At the very top we have the ControlManifest, which is the root container for everything widget within a presenter. Just below that, there is a class called ControlCategory. Anything put in a different category by the user will probably end up on a seperate page in the presenter. To group things somewhat related within the same category, we have the ControlSubcategory class. Lastly we have the center of it all: Control, representing a single widget.

Converters and their factory

Not only do we mine the hierachy, we also need a way to convert a control into a widget. A converter's role is (1) to take a value and present it in some way usable by the presenters, and (2) convert the representation back to the value when instructed to do so.

A converter is a class provided by the frontend, and the backend is agnostic of which frontend. The backend only inspects the calling frontend and mines the types inheriting from IConverter. To make implementing converters a tad bit easier, we have provided the abstract ConverterBase class. Additionally, there are two special base classes called EnumBase and ListViewerBase (to display enums and arrays, respectively).

Some converters are ridiculously easy to implement, others can take a while to get right, mostly because of the different permutations allowed by the control override. Also: each widget is accompanied by its own converter. It is therefore possible to store state information about the widget in the class's fields.

Presenters, Modal and Non-modal

The presenters are the most visible part of Selene. In essence though, they are glorified arrangers of widgets. Their role is to implement one of two base classes, accept a ControlManifest and start placing the widgets in a certain way. For the purpose of code reuse, auxiliary classes to assist in layout are encouraged. With some inheritance tricks, the code for presenters can be kept clean and simple

Due to limitations in certain frontends, one can choose between inheriting from ModalPresenterBase or NonModalPresenterBase. The difference is their way of notifying the caller when they are completed. A modal presenter will return a boolean value from its "Run" method to indicate success or failure (OK or Cancel), while a non-modal presenter will use a caller-supplied delegate to inform upon completion. It is common for implementations of wizard dialogs to inherit from NonModalPresenterBase.

Validators to safeguard your input

The frosting on our cake definitely consists of validators. Although at the time of writing they are solely used by wizard dialogs, they present the versatility of our framework. Validators do exactly what their name implies: they validate stuff. Depending on their result, the presenter may choose to forbid further input until the correct input is given.

Dialogs allowing validation should implement the IValidatable interface. The user is then free to implement a class inheriting from either IValidator or ValidatorBase. The difference between the two is that the latter allows for an enum to be used instead of a number indicating the current page.