Lit Window Productions

Documentation
Download
simpler UI Coding
Articles
How it works
Rapid UI development
Data Adapters 1
Donating

RSS Feed
Whats this?

Contact
Privacy

Fast, easy dialog editing and code generation with Anthemion’s Dialog Editor for wxWidgets...

[Articles] [How it works] [Rapid UI development] [Data Adapters 1]
More articles:

RapidUI – a generic mediator object

Summary

In my first two articles I talked about the motivation behind the “Lit Window Library”, introduced the “data abstraction layer” and wrote about how “rules” can make UI coding a lot easier. I learned from your comments that these two concepts are straightforward and easy to understand. But they also obscured the more subtle and far-reaching ideas behind “The Lit Window Library”.

I want to continue the introduction of this new, faster approach to GUI coding and describe the generic mediator object that encapsules GUI behavior, then finish my presentation with another article about GUI design patterns and how they will really save GUI programmers a lot of time. These features of the library are less mature and thus more difficult to see.

The mediator design pattern

A mediator is a basic OO design pattern. Google it for details. Basically it encapsules “behavior” or “interactions” between objects. “Between” is the keyword here and “between” is exactly where huge code savings are possible.

If you have ever written an “OnSomething” method that updates one widget based on the properties of another, you have used the mediator pattern. But it is extremely difficult to write reusable “OnSomething” methods for anything other than very simple operations. The interactions between widgets usually require highly specific code that has to be rewritten for every application. That is why the interactions “between” widgets require such a large amount of work and time.

RapidUI – a generic mediator object

This is where the “RapidUI” class comes in. A RapidUI object takes three parameters as input:

  • a set of data objects
  • a set of widgets
  • a set of rules

The rules describe the interactions and replace the traditional “OnSomething” methods. Algorithmical languages such as C++ are not very well suited to describe such dependencies. You have to propagate events from senders to receivers, keep track of dependencies and do all of this manually in your source code. If you have ever added code to an already complex “web” of dependencies you know how tricky this can be.

Rules are a much more natural way to describe UI requirements and dependencies between widgets. It is also a lot easier to write reusable rules. More on that in a minute.

With RapidUI, you write your data definitions and design your widget layout as usual. But for the interactions you write a set of rules, instead of a bunch of “OnSomething” methods. Finally you instantiate the RapidUI object, pass data, widgets and ruleset and activate it.

After activation the RapidUI object takes over. Whenever a widget or a data object changes, the RapidUI mechanism handles input validation, looks up the dependencies, solves the rules and updates dependent widgets and data so that all rules again evaluate to true. It keeps track of all dependencies and does the bookkeeping. This is how it works in detail:

Implicit and explicit databinding – connecting widgets and data

Databinding is a well known concept so I’ll keep it brief. If you have a combo box and want to bind an integer variable to the currently selected index, the rule is

“ComboBox.Selected = IntVariable”

This is a two-way rule, meaning that if IntVariable changes, the “Selected” property of the combo box will be updated and vice versa. The libraries “data abstraction layer” allows very powerful data binding constructs.

ComboBox.Items = MyContainer

binds all elements of the container to the combo box, regardless of the type of the container. The container could be a C vector, an STL::list<> or even an ODBC database table.

Let me repeat this, because this is a key feature and huge time saver. The rule “ComboBox.Items = MyContainer” automatically detects the type of the container. If you start out with STL::vector<string> and later want to use an ODBC table, you simply have to change the definition from vector<string> to wxODBC. The RapidUI mechanism will still show the contents of the container in the combo box, except that the elements now come from a database. You don’t have to rewrite any code, and its all done in pure C++.

Explicit databinding lets you specify which widget should be bound to what data object. You can use C++ like expressions to create complex binding statements.

Frame.Title = “Headline: “ + List.Current.m_headline

will display the currently selected headline prepended by the constant string “Headline: “ in the title bar. And the headline will update itself automatically whenever the user chooses a new element from the listbox “List” . This one rule is all that is necessary. One rule to bind them all ;)

Implicit databinding automatically binds widgets and members having the same name. It saves a lot of time when you need only a simple 1:1 binding between widgets and variables, which is very common for all dialogs. Give a widget the same name as the member variable you want to bind it to and RapidUI does the rest. Here is an example:

struct Settings {
   vector<string> m_listOfStrings;
   double m_someDouble;
};

If you construct a dialog with a list box and name the list box “m_listOfStrings”, RapidUI constructs a two-way rule “listbox.Items = data.m_listOfStrings”. The strings will be shown in the list box and you didn’t have to write any code.

If you include a text control and name it “m_someDouble”, it will show the double value ‘m_someDouble’. Whats more, the data abstraction layer automatically detects the type of the variable and RapidUI ensures that a user enters a valid number.

If your UI framework does not allow strings as widget identifiers but uses numerical Ids only for widgets you have to include some kind of mapping mechanism that maps an ID to a name. But this can easily be extracted from the #define header file.

Reuse ruleset – save time

Rules use symbolic names to reference widgets and data. Linking rules with widgets and data objects is done dynamically at runtime. This lets you describe UI behavior independently of the actual widgets and data and is the prerequisite for a library of reusable rules.

A ruleset is a “set” of rules, much like a function is an “ordered sequence” of statements. Just as reusing functions from a library saves a lot of time, reusing rulesets does, too. Rulesets become stock objects that can be used out of the box, maybe dragged and dropped with a designer tool. Just like widgets. Lets have a look at a very common user interface requirement.

“Add/Modify/Delete”: the user shall be able to add some data to a list of elements, modify an existing element or remove one from the list.

To implement this requirement you need the following ingredients:

  • A list of data elements. This can be a container from the STL, a simple C vector or a table from a database.
  • A form that shows a single element from that list. This is usually a panel with a lot of widgets, one widget per “member variable” of the element class.
  • A list widget where the user can select which element from the list should be shown in the form. This could be a dropdown combo box, a list control or a folder view.
  • Action elements such as buttons or a (popup) menu so the user can actually choose “Add” and “Delete”.

The RapidUI mechanism lets you create a ruleset for the “Add/Modify/Delete” requirement and put it in a library. If you have to implement this requirement again for a different set of data, you can reuse the “Add/Modify/Delete” ruleset, pass it to a RapidUI object along with the widget layout and data variables and you are done.

Over time this library grow to a large collection of rulesets for all common user interface patterns. I will talk about this more in my next article.

Add/Modify/Delete ruleset

I will start with the “Modify” action. The “List.Current” property is bound to a data container, just as I explained in the paragraph about data binding. To modify an element of that container a user selects it in the list whereupon its details are shown in the form. The requirement reads: “the form shall always display the element selected in the list”. In RapidUI rule-speak this becomes:

Form.Contents = List.Current

Note! This is the actual rule you have to write for RapidUI for this requirement. Not some kind of short-hand I use for the article, but the verbatim code!

This is what the RapidUI object will do when it sees this rule:

  • Every time the user selects a different element, “List.Current” changes. RapidUI searches the data storage for objects that depend on “List.Current”. It finds “Form.Contents” and will update the contents of the form with the values of the newly selected object.
  • The “Form.Contents” itself is actually a group of rules that bind each widget within the form to its counterpart member variable. This is databinding in its most simple form. Assigning “List.Current” to “Form.Contents” copies all the values to the form.
  • The reverse happens when the user changes a widget in the form. RapidUI searches for objects that depend on this widget. “Form.Contents” depends on all its children widget and RapidUI will update “Form.Contents”, thereby changing it. RapidUI then continues the dependency search and finds that “List.Current” depends on “Form.Contents”. So it will update “List.Current” as well. The users modifications are copied back to the original list element.

This one rule “Form.Contents = List.Current” handles the entire logic necessary for letting the user modify an entry from an existing list of objects. And this rule is reusable. It is completely independent from the type of data or from the particular widgets you choose. Whether you exchange the list box with a grid control, add a new member variable to your data class definition or decide to use an ODBC table instead of an STL::vector, the rule and the RapidUI implementation stay the same, courtesy of Lit Window’s data abstraction layer and constraints solver.

Action – decoupling actions from the user interface

Up until now, everything I have talked about has already been implemented. You can download a working example and study the source code. The next topics are still in planning and are a bit more vague. They involve “Action-Rules”, which haven’t been designed yet. But the concept is simple and actually very old.

“Delete” is an action. It can be triggered by different means: the user might click on a button or select an item from a popup menu. It might even be triggered by an operating system event, perhaps by a notification when a file was deleted from the network or a task finishes and disappears from the task list.

To delete the currently selected element from the list the user interface needs to contain an element capable of triggering: a button or popup menu. The “Delete” rule then simply connects the Delete action with the trigger element, or a list of elements:

“List.Current.Delete = TriggerElement”

This is what RapidUI will do when the element sends a trigger event:

  • It searches its list of data and widgets for “List”, then calls the “Delete” method of the “Current” property of the List object. The “Delete” method will delete the current element. Depending on the delete strategy it will then either select the next element, the previous element or a <null> element as the current element. Since “List.Current” is bound to a container, the “Delete” method will remove the element from the container as well.
  • Selecting the next/previous/<null> element will cause the GUI to be updated just as if the user selected a different element.

So now we have Modify and Delete and had to write just a few simple lines of code for them to work.

The “Add” action is a bit more complicated. For example, you’ll have to decide where the new element shall be constructed. Do you want RapidUI to construct a temporary element, let the user edit it and insert it into the container only when the user clicks ‘OK’? Or do you want the element added to the container immediately , then let the user edit it? Should the new element be shown in the same form where existing elements are shown? Or do you want a popup window so that the user is made aware of the fact that this is a new element?

But the basic procedure is the same. You create an action rule, set parameters such as the insertion strategy and connect a trigger. The important thing is that, again, the rule is independent of the actual widgets or data.

Rulesets library

A ruleset is a set of rules. It is a set, because no order is implied and rules must not be duplicated. Like invariants all rules evaluate to true all of the time. The RapidUI mechanism takes care of that and will alert the user or programmer when values cause a conflict.

BEGIN_RULES(“AddModifyDelete”)
RULE(“List.Current = Form.Contents”)
RULE(“List.Current.Delete = ActionTrigger”)
END_RULES()

Over time, as more and more rulesets are written and implemented, the Lit Window Library will help user interface programmers much like class frameworks and libraries helped us with containers, internationalization and other common problems: the library will reduce code, reduce programming errors and save time.

continued...

[Home] [Products] [Shop] [Knowhow] [Library] [About]

© 2004, Hajo Kirchhoff, last modified Mar 06, 2008

back to top