A lot of applications are based on the forms, composed of controls, groups of controls, and different graphical objects. Though the developers try to design these forms in the best possible way, “the best” from their point is often not the best from the users’ point of view. It’s not only the conflict between the designers and the users, but each user may have (and often has) personal opinion about the “would be the best” design.
There are two popular types of solutions, which are used to deal with this problem: adaptive interface and dynamic layout. There are numerous ideas implemented under the flag of adaptive interface, which give users the chance to select one or another variant, but the main feature is that somewhere deep inside any application there is the predetermined reaction on any users’ choice, so all the applications with the adaptive interface are still designer-driven. The programs with the dynamic layout are also designer-driven: they are adaptable to the changes of the form’s size or the used font, but the reaction (changes) are predetermined at the design stage.
The only way to move from designer-driven to user-driven applications is to give users the full control of the whole inner view of the applications. To do this, all the inner elements must be turned into moveable / resizable, and the users must receive an easy instrument to rearrange the view at any moment, while the application is running. By an easy way I mean an implementation of two rules.
- Any object can be moved by any inner point.
- Any resizable object can be resized by any border point.
The moving / resizing operations must be easy to implement (this is crucial for the programmers) and they must be absolutely natural from the users’ point of view. That’s why everything must be done simply with a mouse (press-and-move) without any extra keyboard involvement, so the users don’t need to learn any new rules or read the manuals. The only thing users need to know is that everything is moveable.
The word everything is crucial for the user-driven applications. There were some programs before, in which the objects of one or another class were made moveable. In each case it required the development of a special algorithm for each particular class; the applications became better, but generally they were not different from all other programs. Turning everything into moveable / resizable converts applications into another world. It’s impossible to understand and estimate the difference simply by reading an article, but the demonstration program explains everything. The four samples in this small program are the parts of the much bigger one, which was developed for demonstration of many different situations and solutions. At www.sourceforge.net there is the
MoveableGraphics project (name is case-sensitive!), in which there is a set of useful documents and programs.
Let’s start with some terms. The main idea of the algorithm is to cover an object with the set of sensitive areas, in which moving or resizing can be started. The whole set of such areas for any object constitutes a cover (class Cover); each area itself belongs to the CoverNode class. As an object must be moved by any point, then the whole object’s area must be completely covered by the set of nodes; the overlapping of nodes is possible and not a problem at all.
An object of the Mover class supervises the whole moving / resizing process for the registered objects (objects, which are included into its List). It doesn’t matter how many objects are there in the form and how different they are; usually it’s enough to have one Mover object per form (dialog), though it’s not mandatory and several movers can be used instead of one. The cover for any object can be organized in many different ways; even the objects of the same class can have different covers. The important thing is the order, in which the objects are registered with the Mover, as they are picked up for moving / resizing according with their order in the List. This is important, as with all the objects becoming moveable, they can easily overlap.
The algorithm was especially designed to be used with the graphical objects of an arbitrary shape, but this article is focused on the forms’ customization problem, so I’ll write here mostly about controls and groups of controls.
The two rules, mentioned above, are absolutely correct and essential for all the graphical objects, but must be changed a bit for the controls. For them the reaction on any inner click is predetermined by the controls’ designer and must not be changed; instead they can be moved and resized “by the border”, thus the border of a control is divided between the cover nodes for moving and resizing. Certainly, if you need the non-resizable control, then it can be moved by any border point. All standard controls have a rectangular shape, so the covers for all of them will be similar and only depend on the needed type of resizing. The resizing can be done by any corner of a control; there are also additional nodes for resizing in the middle of those sides, in which direction a control can change its size. All other parts (segments) of the border are used for moving a control.
The first sample I want to show is the well-known Calculator program (figure 1) that sits on every computer with any version of the Windows system, so this program is used around the world by millions of people. I don’t know your personal feelings about the design of this program; maybe you are quite happy with it. But somehow I think that you simply got used to it and never even thought about the possibility of changing it.
With me it is different. I use this program very rarely (once in two or three months), when I have to divide two big numbers. On those rare occasions I can find the buttons with the needed digits after some time, but the location of these buttons looks a bit strange to me. I also never use memory operations, so there are redundant buttons, which are more obvious (because of the red color) and attract my attention, keeping it from the digits that I need to find. I would like to get rid of those redundant buttons, but I can’t, because the design is fixed. But maybe I can do something.
In the demonstration program there is a similar Calculator (menu position Calculator) with the default view absolutely equal to standard Calculator (figure 1). The only addition is that all these controls are moveable, so in a minute I rearrange the view to what I really need (figure 2). I simply change the size of the form and move any button by grabbing it with a mouse at any point of the button’s border. The buttons that I don’t need are moved out of the view across the form’s border. This is not one of the predetermined views of my Calculator; there is simply no such thing as predefined views here. The view is automatically saved on closing the application; the next time it will be the same, as I left it. If I want to move the controls anywhere else, I can do it at any moment. Those relocations do not affect the work of the Calculator, as the positions of all these controls do not affect the code, linked with any click.
And here is the code, which turned the standard Calculator into fully ruled by users.
mover = new Mover (this);
foreach (Control control in Controls)
mover .Add (control);
private void OnMouseDown (object sender, MouseEventArgs e)
mover .Catch (e .Location, e .Button);
private void OnMouseUp (object sender, MouseEventArgs e)
mover .Release ();
private void OnMouseMove (objectsender, MouseEventArgs e)
(mover .Move (e .Location))
There is a single Mover object in the form; all the controls are registered with this mover; these are all the things that have to be done. The controls in this sample are not resizable, but you can easily change it by setting the appropriate values for MinimumSize and MaximumSize properties of the controls, which you want to become resizable.
Technical note. To avoid the screen flicker, don’t forget to switch ON the double-buffering in any form, where you use the moving / resizing algorithm. It has nothing to do with the described technique, but it is simply a nice feature from Visual Studio.
The pure controls are the simplest elements, used for forms’ design. A bit more complicated is a pair “control + text”, where the text is some kind of comment or explanation to the control. The most widely used case of such a pair is a TextBox with one or two words on the side (figure 3), explaining which parameter must be typed in or shown in this control. For the standard forms’ design, the text is often organized as another control (Label), but the use of moveable elements gives you several different interesting solutions. For all of them instead of the Label the painted text is used. For the CommentedControlLTP class it allows to move the pair by pressing the mouse at any point of the text’s area. For the CommentedControl class it allows to put the text in the arbitrary position in relation to the control and to turn the text on any angle.
Suppose that you need to design a form, in which the personal data must be shown and edited. The exact set of controls depends on the purpose of application; the view of the form depends on the designer. The default view of the Form_PersonalInformation.cs (menu Personal info) doesn’t pretend for being better than many others; it simply shows the set of TextBox controls, which is common for such type of forms (figure 4).
The demonstrated placement of the controls is good enough for some western countries, but definitely not good for other parts of the world, in which the same data is expected to be shown in the different order. There are countries, in which:
- The family name always goes first.
- The day always precedes the month in any form of the date.
- The address is written in the opposite order: ZIP code, country, town, and so on.
In this sample all the pairs “TextBox + comment” are organized into the CommentedControl objects, so each user can rearrange the form’s view to whatever he wants, needs, and prefers. To allow this, first the CommentedControl objects are organized.
comctrls  = new CommentedControl
(this, ctrls ,
-(sizefStr  .Width / 2 + 4),
0.5, str );
Then all these objects are registered with the mover.
for (int i = comctrls .Length - 1; i >= 0; i--)
comctrls [i] .IntoMover (mover, 0);
The code for three mouse events is nearly the same, as in Calculator sample. I don’t need to show one or another view, into which this form can be changed; the number of possible variants is infinitive.
The forms are often designed on the basis of not only single controls, but the groups of controls; the best representative of such objects is the GroupBox. The standard GroupBox can be easily turned into moveable / resizable exactly in the same way as any other control, but I think that it is a primitive and bad solution. The problem is that such group can’t be moved around by any inner point, but only “by the frame”, and even this has one negative aspect, which is described in the detailed explanation. I prefer to use couple of other classes, which look similar, but behave much better. The use of several classes of moveable / resizable groups are demonstrated in the Test_MoveGraphLibrary.exe; for better comparison I organized their demonstration there in a single form. Here I want to show two of those classes.
The Group class looks very similar to the GroupBox, but the dashed parts of the frame makes it obvious in which direction the group can be stretched or squeezed. The group can be moved by any inner point and resized by any border (frame) point. The size and position of the inner elements are determined according with the frame’s changes. The Form_DataSelection.cs demonstrates the design of a form on the basis of such elements (figure 5). This is a classical sample of a popular task, when some part of data must be selected for further processing. Usually the full set of data is shown in one list; the selected items are included into another list. The code work perfectly; you can select, add, and exclude the items without problems. There are no questions about these operations; the problem is in design: half of the users want to see the full list of items on the left and the selected items on the right; another half of users prefers those lists to be positioned in the opposite way. There is also some percent of those, who would like to see both lists in one column; they have the right to demand such positioning.
As a developer of such program, you can either position those groups in the way you prefer without any explanations, or you can try to explain to the users, why your vision of this simple interface is better than any other. It doesn’t matter, what you prefer and what you fixed in the code: there are always those users that have different opinion. So here is an easy way to design this form so that EVERYONE will position the lists in the way he personally prefers.
You declare a Mover and two objects of the Group class.
Group groupAll, groupSelected;
Then you initialize the group.
groupAll = new Group (this, rc, new RectRange (sizeTitles  .Width + 20, 180,
100, 500), titles , MoveAll);
Among the parameters is the range of the group’s resizing and the method, describing the position of the inner elements, when the frame is moved or resized. The groups and other elements of the form (there is an OK button) are registered with the mover; the code of the three mouse events is exactly the same, as I showed for Calculator.
mover .Add (groupAll);
mover .Insert (0, groupSelected);
mover .Insert (0, btnOK);
There can be another (opposite) idea for organizing the group. In this case the inner elements are moved and resized in an arbitrary way and the frame is automatically placed around all the inner elements. I think that it can be the most interesting and promising way of group design. The work of such objects is demonstrated in the Form_DependentFrames.cs; one of the possible views is shown at figure 6. It is another sample of working with the personal data.
The only new thing here is the construction of the
DependentFrame objects. For example, for the Birth group it is done in such a way.
CommentedControl cc_month = new CommentedControl (this, textMonth, 0.5,
-sizefStr  .Height / 2, str );
CommentedControl cc_day = new CommentedControl (this, textDay, 0.5,
-sizefStr  .Height / 2, str );
CommentedControl cc_year = new CommentedControl (this, textYear, 0.5,
-sizefStr  .Height / 2, str );
List<ElementInsideDependentFrame> elemsBirth = new List<ElementInsideDependentFrame> ();
elemsBirth .Add (new ElementInsideDependentFrame (cc_month));
elemsBirth .Add (new ElementInsideDependentFrame (cc_day));
elemsBirth .Add (new ElementInsideDependentFrame (cc_year));
dfBirth = new DependentFrame (this, elemsBirth, spaces, "Birth",
StringAlignment .Near, 4);
In the form, shown at figure 6, all the individual controls and the groups are moveable and resizable. This is the basis of the unique type of customization. The main feature of the user-driven applications is that the user and only the user determines the whole view. The developer is responsible only for functionality of the program and for the instrument that allows to rearrange the working application in an arbitrary way. Certainly, any application has to have some good view by default, but the users are not forced to agree with the designer’s aesthetic view. The moving / resizing of the elements does not interfere with the functionality of the program, but it allows to avoid the annoying interface, as any user can change the view of any application to whatever he personally likes.
This article is concentrated on the problems of controls and groups of controls, used for forms’ design. Usually any form is populated both with controls and different graphical objects. To achieve the best results, to develop a really user-driven application, all the elements must be turned into moveable / resizable; the mentioned algorithm is designed to work with all types of elements. For better understanding, download the descriptions and demonstration programs from the
MoveableGraphics project (name is case-sensitive!) at www.sourceforge.net. All the standard controls have the rectangular shape, so the covers for them are similar and fairly simple. The graphical objects can be of an arbitrary shape and there can be really interesting covers. A lot of different samples are demonstrated in
Test_MoveGraphLibrary and described in Moveable_Resizable_Objects.doc.