Click here to Skip to main content
Click here to Skip to main content

Elastic Groups - The Groups Which Can Be Arbitrarily Changed by Users

, 28 May 2013
Rate this:
Please Sign up or sign in to vote.
Elastic groups that can be arbitrarily changed by users

Introduction

Let us start with a bit of history on user-interface. I’ll try to do it in a quick way so I’ll omit some stages which were interesting but which I consider not so important for the main item of this article. I hope to be back on track in three or four paragraphs.

The first prototype of graphical user interface was demonstrated nearly 50 years ago (Douglas Engelbart, 1964). The first products which can be called personal computers were on the market in 1974-77 but for a number of years the interface was absolutely different from what we have now. I wonder how many readers of this article have their own experience of working, for example, under MS-DOS. The first PC with graphical interface appeared in 1983- it was Apple’s Lisa. However, that computer already had some features which are familiar to everyone today: the drop-down menus and icons. Two years later, in 1985, the Microsoft’s Windows appeared.

For the last 30 years, there is only one dominant idea in interface design: adaptive interface. For the first 20 years of this period, the adaptive interface flourished and produced an immense number of ideas; I think there were hundreds of books and thousands of articles. Surprisingly, but then this whole area of ideas was squeezed to one general line - the dynamic layout. This happened mostly for “wise” decisions in Microsoft and the dominant role of this company on the market. (Three years ago, in a private discussion with excellent developers from Microsoft, I was told that they were working under the strict orders from their marketing department. I have big doubts that the staff in that marketing department is wiser than the world-class developers, but it’s their local problem inside Microsoft.)

To some people, the development of a program under the idea of dynamic layout looks easier and thus cheaper, but from my point of view it is a real disaster for interfaces. It’s like a construction of the new highway into the dead-end. Before the era of PC, the interface was one for all users of a program; later users got at least a chance to select. It was a selection only between the variants which were considered good enough by developer and that is why they were coded, but at least users had a choice. Obvious and expected improvement would be to give users the full control over the programs. Instead, we have a 180 degrees turn and now, with the dynamic layout in every program, there are no choices at all. There is only one view for everyone and in addition there is a demand from developer that you have to like what you get. (What about one car model on the market and the same demand? Sounds really nice… for that car maker.)

Several years ago, I formulated the rules of user-driven applications and began to demonstrate the work of such applications. There is a book World of Movable Objects with the accompanying program which includes more than 170 examples. A lot of those examples were developed only to demonstrate one or another feature in the best way but some of the examples are the real programs working in different areas. The book together with its accompanying demo project (all codes in C# are available) and several other very helpful files can be downloaded from here. The main idea of user-driven applications is that each user can easily change any working program; the program continues to work according to its main purpose but the view can be changed by anyone at any moment. Certainly, no one can put into code all variants that dozens (hundreds, thousands, millions) of users can imagine, so this has nothing to do with the adaptive interface. The idea and implementation is absolutely different. Our programs consist of graphical objects and controls. Not long ago, I published an article about simple graphical objects (What can be simpler than graphical primitives?). Then there was an article about complex objects (Movable elements: from primitive to complex objects) and another one about controls (Movable controls). The user-driven applications can be constructed only on the basis of movable elements and in that last article, I demonstrated the standard groups turned into movable. But that was only the first step of group’s evolution; now is the time to explore how the ideas of user-driven applications absolutely change the idea and the design of the groups.

It’s not a rare situation when several screen objects need to be organized into a group. These objects are positioned next to each other and there is some kind of visual element that informs users that these objects work together on some subtask. Throughout the years, there were only two ways to organize such groups: either to use a Panel object or a GroupBox. In both cases, the design starts with organizing a container and then the inner elements are added. The container plays the main role and defines the place and the sizes of the group. If a Panel is used, then only on very rare and really special occasions, the border of such panel is hidden; the standard situation is that it is perfectly seen. In the GroupBox, there is a perfectly seen frame.

Any actions which user wants and needs to do are done in the inner elements of the group. The container can make these actions a bit easier (for example, the selection among radio buttons is limited to the set of buttons inside the particular group) but the main purpose of such container is the visual tip. The border of the group does not play any significant role; it is only a decoration.

When the dynamic layout became the dominant idea in interface design, it was applied to all objects. When the form is resized, the objects of the form are changed, for example, according to the anchoring which was defined at the stage of design by the developer of this form. If there is a panel among those objects, then the objects on this panel are changed according to the anchoring rules applied by the same developer. This is a standard way of design which you can see in many programs.

The user-driven applications have very few rules but the main idea is the full users’ control over the screen elements. For solitary controls, it means that any control can be moved and resized by user at any moment. Two mentioned classes of groups – Panel and GroupBox – are also controls, so they are easily turned into movable and resizable as any other control. (See article Movable controls) It is not a problem to make any panel movable and resizable, but what to do with the elements on the panel when this panel is resized? To use the ideas of dynamic layout? It looks natural for nearly everyone but it is definitely against the rules of user-driven applications. Developer can think out and fix in code any scenario, but for user-driven applications any such scenario is wrong simply because it is thought out not by user. Can a developer predict and put into code all the innumerable variants that users can think out? Certainly not. Is there any other solution to this problem? Yes, and it is obvious. Simply look at this problem from another point.

Users need an easy way to change the configuration of the inner elements of a group. Well, this is easy to organize as any element is movable and resizable. What about the frame of the group? The frame must adjust its size and position in such a way as to show at any moment the combined area of all those inner elements (plus some additional space around them). In this way, the frame behaves like an elastic string around all inner elements and from this behaviour the name of the class was born. The idea of the group is turned upside down.

  • The idea of the dynamic layout is to project the change of the outer border into the change of sizes and positions of the inner elements. User can change only one thing – general size – and all the inner elements are affected. It is very easy and looks very powerful as one change affects everything, but everything comes with a price. Whatever is going to happen is simply predetermined by the developer and user is simply stripped of all his power; the full control over application is in the developer’s hands.
  • In the groups of the new type user can arbitrary change any inner element, so the full control is in user’s hands.

I started by turning the standard Panel and GroupBox objects into movable / resizable (you can see such examples in the article Movable controls) and eventually came to the ElasticGroup class in which inner elements are movable / resizable and the set of these elements determines the position of the frame. What can be used as elements of such group? Either controls or controls with a primitive and easily described graphical addition – a control with comment. If you need to include into such group any other graphical objects, then there is similar in behaviour ArbitraryGroup class. (Maybe I’ll write about it in another article.) Here is the full list of elements allowed inside any ElasticGroup object:

  • SolitaryControl objects
  • CommentedControl objects
  • CommentedControlLTP objects
  • DominantControl objects
  • ElasticGroup objects

First three classes of this list are demonstrated in the article Movable controls. An object of the DominantControl class consists of several controls (at least two) of which one plays the dominant role and others are subordinates to that one; the details of their movements will be shown a bit further. The last position in the list declares that a group can be an element of another group; such recursion allows to design the groups with any level of complexity. The next example demonstrates the group with the elements of all five types.

File:                     Form_MeetElements.cs

Menu position:   Meet the elements

ElasticGroup objects can be used in many different situations and the content of such group can vary. To make the design of such group easier, the ElasticGroup class has around 50 different constructors.

The first example of this article has to demonstrate all five types of allowed inner elements, so each variant is represented by an element of the specified class and then all these elements are transformed into the ElasticGroupElement objects.

    ctrls = new Control [] {listviewSolitary,
                            textBox_CommentedControl,
                            textBox_CommentedControlLTP, 
                            listviewDominant, button1, button2, button3, button4, 
                            textboxDominant, btnClr, btnFont, btnAdd };
    private void DefaultView ()
    {
        SolitaryControl sc = new SolitaryControl (ctrls [0]);
        CommentedControl cc = new CommentedControl (this, ctrls [1], Side .N,
                                        SideAlignment .Right, "CommentedControl");
        CommentedControlLTP ccLTP = new CommentedControlLTP (this, ctrls [2],
                             Side .N, SideAlignment .Left, "CommentedControlLTP");
        DominantControl dc = new DominantControl (new Control [] { ctrls [3],
                                    ctrls [4], ctrls [5], ctrls [6], ctrls [7] });
        ElasticGroup eg = new ElasticGroup (this, new ElasticGroupElement [] { 
                new ElasticGroupElement (new DominantControl (new Control [] {
                                ctrls [8], ctrls [9], ctrls [10], ctrls [11]})) },
                                            12, "ElasticGroup");
        groupFiveElements = new ElasticGroup (this,
                     new ElasticGroupElement [] {new ElasticGroupElement (sc),
                                                 new ElasticGroupElement (cc),
                                                 new ElasticGroupElement (ccLTP),
                                                 new ElasticGroupElement (dc),
                                                 new ElasticGroupElement (eg)}, 
                                              "Five types of elements");
    }
Text Box: Fig.1 This group demonstrates all five variants of elements inside the ElasticGroup.

Figure 1 shows the default view of the Form_MeetElements.cs. There are five inner elements which represent all five possible types.

The ListView in the top left corner (listviewSolitary) is wrapped into SolitaryControl object. The values for MinimumSize and MaximumSize of this control allow to resize this control both horizontally and vertically. The resizing of the control can be started near the corners of the control or the middles of the sides while any other point near the border can be used to start the moving of the control. There are more details in the article Movable controls.

       SolitaryControl sc = new SolitaryControl (ctrls [0]);

The TextBox in the top right corner (textBox_CommentedControl) together with the associated comment are turned into the CommentedControl object. The control itself can be moved and resized in exactly the same way as the previous control; on any such movement the comment retains its relative position to the control. Comment can be rotated around its central point; press it with the right button and start turning around. Comment can be relocated to nearly any other position. Only if you try to release the comment when it is entirely closed from view by the associated control, then the comment is forced outside that rectangular area. There are more details in the article Movable controls.

CommentedControl cc = new CommentedControl (this, ctrls [1], Side .N,
		SideAlignment .Right, "CommentedControl");

Another TextBox in the middle of the left side (textBox_CommentedControlLTP) together with the associated comment are turned into the CommentedControlLTP object. Though the pair looks similar to the previous one, the rules of their movement are partly different. The control itself can be moved and resized; on any such movement the comment retains its relative position to the control. Comment cannot be rotated and if you try to move the comment, the whole pair will move synchronously. Comment has 12 allowed positions – three on each side of the associated control; the selection is organized through a small auxiliary form which can be called via the context menu on the comment. Because the objects of the CommentedControl and CommentedControlLTP classes look identical but behave differently, I recommend not to use them together in any form. There are more details in the article Movable controls.

CommentedControlLTP ccLTP = new CommentedControlLTP (this, ctrls [2],
	Side .N, SideAlignment .Left, "CommentedControlLTP");

The big ListView and several smaller buttons nearby in the bottom right corner are organized into the DominantControl object. The ListView control plays the dominant role while the buttons are its subordinates. Three small buttons are nonresizable; the fourth button is resizable. When the dominant control is moved, all subordinates move synchronously; when the dominant control is resized, all subordinates retain their relative positions. Subordinates can be moved and resized (if their properties allow) individually; if any subordinate is released inside the area of dominant control, then this subordinate is returned to the last position outside the dominant control from which the movement started. There are interesting examples with the DominantControl objects in the book World of Movable Objects.

DominantControl dc = new DominantControl (new Control [] { ctrls [3],
ctrls [4], ctrls[5], ctrls [6], ctrls [7] });

The group in the bottom left corner illustrates the fact that ElasticGroup object can be used as an element of another group of the same class. This smaller group consists of four controls which are organized into the DominantControl object with three subordinates. Of them, only one subordinate button is resizable while two others are non-resizable.

ElasticGroup eg = new ElasticGroup (this, new ElasticGroupElement[] {
new ElasticGroupElement (new DominantControl (new Control [] {
ctrls [8], ctrls[9], ctrls [10], ctrls [11]})) },
12, "ElasticGroup");

The Form_MeetElements.cs is not designed to solve one or another real task; such examples are shown further on. The only aim of this example is to demonstrate the set of possible inner elements of the group, the movements of these elements, and some standard commands of tuning.

When I started to design movable groups, I began with an obvious transformation of the standard GroupBox into movable in exactly the same way as I was doing it with any other control. After it, I constructed several classes of movable groups with different features and even some auxiliary elements of visualization; some of those results are described in the book and are demonstrated in its accompanying program. Later I came to the ElasticGroup class which looks like any standard group but behaves absolutely differently. This class turned out to be so powerful and flexible for design of different programs that I stopped using any other classes of groups that were invented on the way. It is interesting to look at some of them and compare their behaviour and view, but there is certainly no question about the best choice for design of real programs.

The ElasticGroup class, as you can see it now, is very flexible, but at the same time it is extremely simple in design. The sizes of any group can be changed only as a result of moving / resizing of inner elements. The group by itself is not resizable but only movable. The shape of the frame is always rectangular, so, to provide its movability, it would be enough to have a cover consisting of a single rectangular node. Well, in reality there is one more movable element which I didn’t mention yet: the title of such group can be moved along the upper line of the frame and can be positioned in any place of that line. Thus, the cover of any ElasticGroup object consists of two nodes: one provides the movement of the title, another – the movement of the whole group.

In the user-driven applications all the parameters of visualization are controlled by users; the ElasticGroup class has a whole set of such parameters that can be modified, for example, with the help of an auxiliary tuning form. I opened this tuning form and in several seconds transformed the group from its default view into what is shown at figure 2.

Text Box: Fig.2 Several seconds of tuning can change the view in any way you want.

At figure 2, the elements of the CommentedControlLTP and CommentedControl classes are positioned side by side and perfectly demonstrate the similarities and difference in view. For comments of both classes, two parameters are the subjects of tuning: font and color. On both comments, the context menus can be called and then through the commands of these menus the standard dialogs can be called, but there is a small difference in calling those menus. Everything starts with the identification of the pressed element and in this process I rely on the mover. Mover easily identifies the class of the pressed element and each movable element has its personal ID because this is one of the parameters of each GraphicalObject from which all the classes of movable / resizable objects are derived. In further examples with multiple elements of the same class, you will see the work of this identification process but here in the Form_MeetElements.cs, we have only one representative for each class of controls with comments so it is enough to detect the class. Yet, there is a small trick.

An object of the CommentedControl class, which is on the right at figure 2, is a complex object consisting of two different parts. When the mouse is pressed anywhere near the border of its control, the mover identifies the pressed object as the CommentedControl element, but if the mouse is pressed anywhere on comment, then the pressed object is identified as a CommentToRect element. An object of the CommentedControlLTP class, which is on the left at figure 2, is not a complex object and, regardless of whether the mouse is pressed near its control or on comment, the pressed object is identified as a CommentedControlLTP element.

    private void OnMouseUp(object sender, MouseEventArgs e)
    {
        ptMouse_Up = e .Location;
        double fDist = Auxi_Geometry .Distance (ptMouse_Down, ptMouse_Up);
        int iWasObject, iWasNode;
        if (mover .Release (out iWasObject, out iWasNode))
        {
            GraphicalObject grobj = mover .WasCaughtSource;
            if (e .Button == MouseButtons .Left)
            {
                … …
            }
            else if (e .Button == MouseButtons.Right)
            {
                if(fDist <= 3)
                {
                    if(grobj is CommentToRect)
                    {
                        ContextMenuStrip = menuOnComment;
                    }
                    else if (grobj is CommentedControlLTP && iWasNode == 9)
                    {
                        ContextMenuStrip = menuOnCommentLTP;
                    }
                    else if (grobj is ElasticGroup)
                    {
                        (grobj as ElasticGroup).ParametersDialog (this,
                                  ParametersChanged, PointToScreen (ptMouse_Up));
                    }
                }
            }
        … …

In the above shown code for the case of the CommentedControlLTP class, there is an additional checking of the number of the pressed node. This helps to call the menu only if the comment of this object is pressed (node number nine). If you think that the same menu must be called when the mouse is pressed also near the control of this object, then the additional checking of the node number must be removed.

Text Box: Fig.3 A tuning form for any ElasticGroup object.
The ElasticGroup class has around 15 parameters to tune, so the tuning of such objects is organized via the tuning form. Figure 3 demonstrates the view of this form when it is called on the big group of the current example. As you can see from this figure, the tuning form contains four groups; three of them are the ElasticGroup objects themselves and only the left group does not belong to this class.

Elements of each group in this tuning form allow to deal with the different sets of parameters: the left group is used to change the background color of the group and transparency, the middle group is used to set the colors and fonts of some parts and elements; the right group is used to set the spaces between the inner elements and the group’s frame, while the group at the bottom is for tuning the title. Certainly, the word left, middle, or right group can be applied only to their positions on this figure; the groups are movable and you can change the view of the form in any way you want.

The left group in this form, though it looks similar to others, cannot be organized as an ElasticGroup object; there is a graphical object in this group and such elements are not allowed inside this class of groups. Instead, the ArbitraryGroup class can be used; groups of this class have similar behaviour but their design is different.

The Form_MeetElements.cs example is used only to make an acquaintance with the ElasticGroup class; in the further examples objects of this class are used in one or another real task.

Selection of Years

File:                     Form_YearsSelection_TwoGroups.cs

Menu position:   Selection of years – Two groups

Let us consider a well known case when you have a limited set of items of which you need to select some subset. Such selection can be implemented for different types of objects; for this example, I decided to stop on the selection of years. The design of such program is simple (figure 4). The years from the predetermined range are represented by the lines of one ListView control. Any number of lines in any combination can be selected in this list. When the needed lines are selected, press the Show button to copy them into another ListView control. If you need to exclude some of the previously chosen years, then highlight them in the second list and press the Remove button. I do not think that anyone would have any problems with the implementation of such task.

Text Box: Fig.4 This variant of years’ selection is based on using two ElasticGroup objects
The developers will have no problems with writing the code for such application, but the questions about the proposed design can be (and they certainly will be) brought up by the users: half of the users will like to see the full list of items on the left side and the selected items on the right; another half of users will prefer those lists to be positioned in the mirror way. There are also those who will like to see both lists in one column; they have the right to demand such positioning. In addition, there is an old problem of the best position for OK button. Three big groups of designers prefer to place it in the right-top corner, in the right-bottom corner, or in the middle at the bottom of a form. Members of each group would never agree with anything else except their opinion.

The task is so primitive that every developer will simply design this form in the way he personally prefers and will ignore any other suggestions. (“The clients will have to like whatever we will give them.”) As a huge respect to the users, the dynamic layout can be applied to this form; the sizes of the lists will be changed according to the used font or resizing of the form. If you have to select the years from bigger range, push the border of the form to increase its height; the height of the lists will also increase so that lists will fill maximum available space inside the groups. I am sure that you can remember either designing similar form or using it in one or another application and there was nothing else except the things that I have mentioned. Such design looks so natural that I don’t expect any public outcry.

But are you sure that this is really the right way to design even such simple programs?

Let us consider the task itself. It is only about the selection of years and nothing else. This selection works correctly regardless of the positions of all the controls and their sizes. As a designer, you are responsible for correct work of all the operations (selection, deselection, and saving). If you will insist that application must look exactly as you would like it to be, then stop talking about the user-friendly interface of your programs. If you will agree that it would be nice to allow each user to change the view of the program in such a way as he personally wants, then ask yourself a very simple question: “What has to be changed to give users the full control of the form’s view?”

The answer is obvious and simple: the screen elements must become movable and resizable by users. In order to be really useful, this movability must work in such a way that user can organize any needed configuration of elements easily and quickly. In the current example, this is provided by two ElasticGroup objects.

   ElasticGroup groupAll = null;
   ElasticGroup groupSelected = null;
   ctrls = new Control [] {
listYearsAll, btnShow, listYearsSelected, btnRemove, btnOK };

Each group consists of two elements: a ListView and much smaller Button. Both elements are resizable and any user will select the optimum sizes for both of them, but I think that in any case the button will be either smaller or much smaller than the List; that is why the pair is united into a DominantControl with the ListView element playing the dominant role.

    private void DefaultView ()
    {
        Spaces spaces = new Spaces (this);
        groupAll = new ElasticGroup (this, new ElasticGroupElement [] {
                          new ElasticGroupElement (new DominantControl (
                                       new Control [] {ctrls [0], ctrls [1]})) },
                                           12, "All years");
        groupAll .Font = fntSelected;
        groupAll .Location = new Point (spaces.FormSideSpace, spaces.FormTopSpace);
        groupSelected = new ElasticGroup (this, new ElasticGroupElement [] {
                          new ElasticGroupElement (new DominantControl (
                                       new Control [] {ctrls [2], ctrls [3]})) },
                                                12, "Selected");
        groupSelected .Font = fntSelected;
        groupSelected .Location = new Point (groupAll .FrameArea .Right +
                             spaces .Hor_betweenFrames, groupAll .FrameArea .Top);
        btnOK .Location = new Point (groupSelected.FrameArea .Right - btnOK .Width,
            Math .Max (groupAll .FrameArea .Bottom - btnOK .Height,
                groupSelected .FrameArea .Bottom + 2 * spaces .Ver_betweenFrames));
        scOK = new SolitaryControl (btnOK);
        ClientSize = new Size (scOK .Right + spaces .FormSideSpace,
                               scOK .Bottom + spaces .FormBtmSpace);
    }

Each group can be easily moved around the screen, so anyone can organize in a couple of seconds the preferable way of selection: from left to right, or from right to left, or in one column, or whatever he wants. All the objects are resizable, so every user can set sizes for all objects which are the best from his point of view.

I used this example for a couple of years and then I found out that in one of my programs I needed the same selection of years not as a stand alone task but only as a part of setting many different parameters. In the big form with many different elements, these two groups could be moved to different places but the logic demanded their positioning next to each other. It’s not a problem to relocate one group and then another, but it would be much easier to move them synchronously. Thus a slightly different version was born.

File:                     Form_YearsSelection_OneGroup.cs Menu position:   Selection of years – One group

In the new version of the years’ selection, two groups from the previous example are united into one; this guarantees the synchronous movement of all the elements. The construction of such group is easy. There are only two elements in the group; each ElasticGroupElement is organized as a DominantControl in which the ListView plays the dominant role with a Button as a subordinate. Below you can see the code for the default view of this group, but pay attention that the view at figure 5 is not the default view; it was received after some moving and tuning of the elements.

    ElasticGroup group = null;
    ctrls = new Control [] { listYearsAll, btnShow, listYearsSelected, btnRemove };
    private void DefaultView ()
    {
        Spaces spaces = new Spaces (this);
        btnShow .Location = new Point (listYearsAll .Left,
                                       listYearsAll .Bottom + 2 * spaces .VerMin);
        int cxL = listYearsAll .Right + 2 * spaces .HorMin;
        listYearsSelected .Location = new Point (cxL, listYearsAll .Top);
        btnRemove .Location = new Point (cxL,
                                   listYearsSelected .Bottom + 2 * spaces .VerMin);
        group = new ElasticGroup (this, new ElasticGroupElement [] {
                      new ElasticGroupElement (new DominantControl (
                                         new Control [] {ctrls [0], ctrls [1]})),
                      new ElasticGroupElement (new DominantControl (
                                         new Control [] {ctrls [2], ctrls [3]}))},
                                  12, "Years");
        group .Location = new Point (spaces .FormSideSpace, spaces .FormTopSpace);
        ClientSize = new Size (group .FrameArea .Right + 2 * spaces .FormSideSpace,
                               group .FrameArea .Bottom + 2 * spaces.FormBtmSpace);
    }

Movability of all the screen elements is only one feature of the user-driven applications. The full users’ control over the applications means the easy change of all visual parameters and also the control over the behaviour of the elements. Tuning of the visual parameters of this group is organized throughout the standard auxiliary form (see figure 3). An easy control over the behaviour of the elements inside the group is the new feature demonstrated in this example.

Text Box: Fig.5 Two groups from the previous example are united into one, but each pair of elements ListView + Button still behaves like a DominantControl object.

Four controls inside the group cover the significant part of its inner area (figure 5). The free area inside the group depends on the sizes and positions of the controls (user defines these things by moving and resizing the controls) and on the spaces between the inner elements and the frame which can be changed in the tuning form. The sensitive frames and nodes near the borders of the controls decrease the free area inside the group and only by pressing somewhere inside the free area the whole group can be moved around the screen. The covers of the inner elements are not visible, so in the normal situation of movable / resizable elements, the areas between the elements look empty but in reality they may be not free because they can be covered by the nodes from the covers of inner elements. Chances are high that while trying to move the whole group, user presses somewhere inside the group but this point can be inside the cover of some inner element and instead of moving the group it starts moving or resizing an inner element. There is an easy way to avoid such accidental movements of inner elements.

User can organize the inner view of the group in the way he really likes and then fix the inner elements via the command of context menu. If the elements are fixed, then their covers are transparent for the mover. In such situation, even if the mouse is pressed anywhere on the node of the inner element, mover skips the cover of this element and looks for the next available object; below any inner element is the cover of the group itself. Thus, whatever looks as free space inside the group is really free. In such situation, there is no chance that instead of moving the whole group user will accidentally move or resize some element. Certainly, if user wants to unfix the elements, he can do it through the same menu at any moment.

These examples for years’ selection demonstrate the easiness of work with the ElasticGroup class but they are too simple to demonstrate the real flexibility and power of this class. Let us try something bigger.

How To Deal With Personal Data?

File:                     Form_Main.cs

From time to time, nearly everyone has to deal with some application which asks us to type in some personal data, stores this data somewhere in database, and can display the stored data later on request. If you have to deal with such an application once in several years, then you have a chance to live through each of those encounters regardless of the interface of such program. But there are people, like HR personal, who have to work with such systems for many hours every day and for those people the design of similar applications and the easiness of their change according to personal taste and current task at any moment are crucial.

In any such program, the core is the database itself, but I want to mention from the beginning that such part does not exist in the next example, so don’t look for it or you can add it yourself. For the users of such program, the problems of storage do not exist at all. The data is somewhere inside and must be shown on request. Users are not interested at all in the coding but are extremely interested in the way the data is shown on the screen, in the simplicity of obtaining only the needed part of information, and in the easiness of going from one task to another with simultaneous change of the data presentation. I am interested in organizing the best possible interface not for average user but for each user at any moment; this is the main topic of this article. Let us imagine that all the controls on the screen show the requested data and let us look at the details of this data presentation.

The computers are capable of storing a fantastic quantity of information and it looks like the designers of systems with personal data try to fill the storage as quickly as possible. In general, the amount of data which is stored about each person is several times or many times bigger than can be shown on the largest possible screen even with the smallest available font. Even if you imagine a screen big enough to show simultaneously all the stored information about the person you are interested in, you will be lost in this information. At any particular moment, the user of such program is interested only in some part of the available information and there must be an easy way to determine the part to be shown.

Good information systems have thousands of users and each of them is interested in different parts of information at one or another moment. There are three ways to organize such system:

  • Design a good enough interface and organize a very aggressive marketing with an obvious and often even openly declared idea “You have to like whatever you are given”. The majority of currently used systems is designed and promoted in such a way.
  • Give users a chance to select between several variants of showing the information. It is a classical adaptive interface implemented in many systems.
  • Give users an easy and quick instrument to change the interface according to the particular task at any moment. This is the idea of user-driven applications.
Text Box: Fig.6 The default view of the group with all the inner elements in view

In the Form_Main.cs, all the available data from our hypothetical database is shown in one big group. Figure 6 demonstrates the default view of this group and shows all the elements which I included into current version. From the point of link between the database and the program, it doesn’t matter where users will prefer to place each inner element, what sizes each element will have, what font anyone is going to use, and in what color each piece of information is going to appear on the screen. Each control is associated with only one particular type of information from the database and this piece of information appears in the particular element regardless of all the parameters of its visibility. As a programmer, I am responsible for showing the correct information (in the real application of such type which is really linked with the database); you, as a user, can rearrange the whole view in any way you want.

There are several obvious blocks of data in my example. In the list below, the position of each block is mentioned according to its place at figure 6.

Block

Design

Position

Current day and time

Two SolitaryControl objects

Top right corner

Name of person

Two CommentedControl objects

Top left corner

Day of birth

ElasticGroup object with three CommentedControl elements

Middle of the upper part

Address

ElasticGroup object with five CommentedControl elements

Middle on the left

Contacts

ElasticGroup object with four CommentedControl elements

Bottom left corner

Professional status

ElasticGroup object with two CommentedControl elements

Middle on the right

Projects

ElasticGroup object with the DominantControl inside

Bottom right corner

Though the outer group – groupData – is not a small one and consists of many elements, the construction of this group is simple enough and obvious. I decided to include into this article only parts of the DefaultView() method as the majority of the inner elements are organized into several ElasticGroup objects and these inner groups are constructed in a similar way.

    private void DefaultView ()
    {
        Spaces spaces = new Spaces (this);
        … …

        textSurname .Location =
                   new Point (textName .Left, textName .Bottom + spaces .VerMin);
        CommentedControl ccName = new CommentedControl (this, ctrls [2],
                                                  Resizing .WE, Side .W, "Name");
        CommentedControl ccSurname = new CommentedControl (this, ctrls [3],
                                               Resizing .WE, Side .W, "Surname");
        LineCommentsOnLeft (new CommentedControl [] { ccName, ccSurname });

        int cy = textDay .Top;
        textMonth .Location =
                       new Point (textDay .Right + spaces .Hor_betweenFrames, cy);
        textYear .Location =
                     new Point (textMonth .Right + spaces .Hor_betweenFrames, cy);
        CommentedControl [] ccsDOB = new CommentedControl [] {
           new CommentedControl (this, ctrls [4], Resizing .WE, Side .N, "Day"),
           new CommentedControl (this, ctrls [5], Resizing .WE, Side .N, "Month"),
           new CommentedControl (this, ctrls [6], Resizing .WE, Side .N, "Year") };
        ElasticGroup groupDOB = new ElasticGroup (this, ccsDOB, "Day of birth");
        groupDOB .Location = new Point (
                   textName .Right + 3 * spaces .Hor_betweenFrames, textName .Top);
        … …
        groupData = new ElasticGroup (this,
                              new ElasticGroupElement [] {
                         new ElasticGroupElement (textDate, Resizing .WE),
                         new ElasticGroupElement (textTime, Resizing .WE),
                         new ElasticGroupElement (ccName),
                         new ElasticGroupElement (ccSurname),
                         new ElasticGroupElement (groupDOB),
                         new ElasticGroupElement (groupContacts),
                         new ElasticGroupElement (groupAddress),
                         new ElasticGroupElement (groupProfessional),
                         new ElasticGroupElement (groupProjects) },
                                      "Personal data");
        … …
    }

The ElasticGroup class is extremely flexible.

  • All parameters of visibility can be changed via the special tuning form (see figure 3).
  • Sizes of all the controls inside can be changed by simple moving of their borders.
  • Positions of all the elements can be changed by their moving.

But this is only a small part of what user can do. There are two more features which make ElasticGroup objects not only flexible but very powerful. Especially when you remember that such groups can be used as inner elements of other groups.

  • Any inner element can be hidden and there are two big pluses in such action. First, this saves a very valuable screen space. Second, currently unneeded elements do not distract user’s attention. When later the hidden elements are needed again, they can be resurrected by a single command from context menu.
  • Inner elements can be fixed or unfixed on demand; this is also done through the commands of context menus. .

Powerful applications with a lot of possibilities for users always have the same defect: users have to learn and remember all those possibilities and the ways to use them. In the case of ElasticGroup class, I try to solve this well known problem by using the same few and simple rules regardless of the elements to which these rules are applied.

  • To change some feature for all the elements of some group, you have to call the context menu of this group and click one command in the opened context menu. For example, this is the way to fix or unfix all the elements in the group.
  • To change some feature for a particular element, you have to call the context menu on this element and select the corresponding command in this menu. In this way, only one thing cannot be done: previously hidden element cannot be unveiled through its own menu because it is impossible to call a menu on the hidden element. For this particular situation, there is one more rule.
  • To unveil the hidden element(s), you need to call the context menu on the parent of this element and then select the command from this menu.

As usual, a context menu is opened when the right button is pressed and released on some object.

    private void OnMouseUp (object sender, MouseEventArgs e)
    {
        ptMouse_Up = e .Location;
        double fDist = Auxi_Geometry .Distance (ptMouse_Down, ptMouse_Up);
        if (e .Button == MouseButtons .Left)
        {
            … …
        }
        else if (e .Button == MouseButtons .Right)
        {
            if (mover .Release ())
            {
                if (fDist <= 3)
                {
                    if (mover .WasCaughtSource is TextOnDemand)
                    {
                        TuningInfo (e .Location);
                    }
                    else
                    {
                        MenuSelection (mover .WasCaughtObject);
                    }
                }
            }
            … …
Text Box: Menu on ElasticGroup Menu on SolitaryControl Menu on DominantControlFig.7 Several context menus which can be called on the elements of different classes

To call the appropriate context menu for any pressed element, there is the Form_Main.MenuSelection() method. This method identifies the pressed element and declares the menu which must be opened. These menus are slightly different for each class of elements, so there are seven different context menus in the Form_Main.cs. The code of the MenuSelection() method is available in the file and I don’t think that I need to include it into the text of the article, but it is interesting to compare the views of those menus (at least some of them) and see what appears on the screen. Users don’t know and don’t need to know the names of the classes of the pressed elements, but such association of menu and the class of the pressed element (figure 7) can be interesting for developers. The important thing for users is the similarity in all these menus, whenever it is possible, and the implementation of the above mentioned rules when some command line in any menu is pressed.

What can be the result of design based on the ElasticGroup class? As the developer of the Personal data program, I have to put under users’ control the full power of such design but I can’t predict how users are going to rearrange the view of the program. Considering the easiness of movability and resizability of elements and the possibility of hiding any set of them, there can be an infinitive number of variants. At figure 8, you can see my versions for two different tasks. This is my vision of good solutions for these tasks; yours can be absolutely different and anyone else may have his own opinion. The most important thing is that each opinion can be transformed into the different view of really working application just in seconds and it will continue to work according to the task.

Text Box: Fig.8a Sending Christmas cards Fig.8b Preparing professional meetingFig.8 Possible variants of the same Personal data program but for different tasks

Figure 8a shows the same application for the period when Christmas cards have to be sent, while figure 8b demonstrates the view which is more suitable for organizing some professional meeting. Rearranging the application to any of these views takes only a couple of seconds; the default view can be reinstalled in an instant via a command of context menu. There is nothing common between two views at figure 8, but also don’t forget that these are the possible views of the same group that is shown at figure 6. Compare all three pictures and try to answer a simple question: “Is there any type of adaptive interface which can allow such flexibility?”

In all the previous examples, the ElasticGroup objects are constructed from the beginning and can be used at any moment. Users can hide those groups (figure 8), but they still exist. In the next example, there are no groups by default. The ElasticGroup object is organized only on a special demand from user and disappears as a result of another user’s command.

Calculator

File:                     Form_Calculator.cs

Menu position:   Calculator

I think that nearly everyone using Windows system is familiar with the Calculator application from Microsoft. Several years ago, during a conversation with one of the colleagues, I heard a complaint: having a poor vision, the colleague had big troubles with the standard Calculator program because even the combined efforts of several specialists did not reveal any way to increase the font used by this application. Another mystery from Microsoft. So I sat down and developed a Calculator which that colleague could use. The program had to work as a normal Calculator with operations and functions; figure 9 demonstrates its default view.

Text Box: Fig.9 The default view of my Calculator
It is obvious that even the positioning of numbers (buttons with numbers) in this view is non-standard. In any calculator, the numbers are organized into a grid three by three with the zero number placed below the bottom left corner of such square. I prefer to have the numbers in a single row; you can transform the view of this program in any way you want. Any control is movable (and resizable), so it’s not a problem to move the elements one by one. But occasionally, it would be easier to move synchronously a whole group of elements; this is done by uniting them into a group and then moving the group.

Uniting of elements into a group is done in a standard way. Press the left button at any empty place and start moving the mouse without releasing the button. Special flag – bRectInView – is used to inform about the demand for painting a temporary rectangular frame.

    private void OnMouseDown (object sender, MouseEventArgs e)
    {
        ptMouse_Down = e .Location;
        bMenuByMover = false;
        ContextMenuStrip = null;
        if (!mover .Catch (e.Location, e.Button) && e.Button == MouseButtons .Left)
        {
            bRectInView = true;
        }
    }

This temporary frame is shown only during the period of time when the left button is pressed. The rectangular area of this frame is based on two opposite corners; one of them is the initial point where the mouse was pressed (ptMouse_Down); another one is the current mouse position (ptMouse_Move). The temporary frame is painted with a blue pen.

    private void OnPaint (object sender, PaintEventArgs e)
    {
        Graphics grfx = e .Graphics;
        if (bRectInView)
        {
            grfx .DrawRectangle (Pens .Blue,
                                 Math .Min (ptMouse_Down .X, ptMouse_Move .X),
                                 Math .Min (ptMouse_Down .Y, ptMouse_Move .Y),
                                 Math .Abs (ptMouse_Move .X - ptMouse_Down .X),
                                 Math .Abs (ptMouse_Move .Y - ptMouse_Down .Y));
        }
        if (controlsInFrame .Count > 0)
        {
            frame .Draw (grfx);
        }
    }

When at last the left button is released, then the SetGroup() method is called to check the possible existence of any controls inside the area of the temporary frame.

    private void OnMouseUp (object sender, MouseEventArgs e)
    {
        ptMouse_Up = e .Location;
        double dist = Auxi_Geometry .Distance (ptMouse_Down, ptMouse_Up);
        if (mover .Release ())
        {
            … …
        }
        else
        {
            if (bRectInView)
            {
                SetGroup (new Rectangle (Math .Min (ptMouse_Down .X, e .X),
                                         Math .Min (ptMouse_Down .Y, e .Y),
                                         Math .Abs (e .X - ptMouse_Down .X),
                                         Math .Abs (e .Y - ptMouse_Down .Y)));
                bRectInView = false;
            }
            … …

The SetGroup() method tries to divide all the controls of the form between two lists. Controls which are rounded by the rectangular frame are included into the controlsInFrame; all other controls are included into the controlsSingle. If there is only one control in the controlsInFrame, then it is also moved to the controlsSingle. Thus, if the controlsInFrame is not empty, then there are at least two elements in this list.

    private void SetGroup (Rectangle rc)
    {
        controlsSingle .Clear ();
        controlsInFrame .Clear ();
        foreach (Control control in Controls)
        {
            if (rc .Contains (control .Bounds))
            {
                controlsInFrame .Add (control);
            }
            else
            {
                controlsSingle .Add (control);
            }
        }
        if (controlsInFrame .Count == 1)
        {
            controlsSingle .Add (controlsInFrame [0]);
            controlsInFrame .Clear ();
        }
        RenewMover ();
    }

If there are items in the controlsInFrame, then the ElasticGroup is organized; the group contains all elements from the list. The construction of the group is done inside the RenewMover() method.

    private void RenewMover ()
    {
        mover .Clear ();
        … …
        if (controlsInFrame .Count > 0)
        {
            … …
            frame = new ElasticGroup (this, controlsInFrame,
                                      new int [] { 10, 6, 10, 10 }, title);
            frame .Movable = bMovable;
            frame .IntoMover (mover, 0);
        }
        … …

Thus organized group behaves like any other group of the ElasticGroup class. It can be moved around the screen; the inner elements can be moved and resized individually as any other control in the form; the frame of the group adjusts to all changes of the inner elements (figure 10). The group disappears when you try to organize another group or simply click at any empty place.

Text Box: Fig.10 Temporary ElasticGroup is used for synchronous movement of the controls.

There is only one difference between this temporary group and all other groups of the same ElasticGroup class demonstrated in the previous examples: this group cannot be modified. I simply don’t see any sense in organizing the tuning of such group. If you want, you can do it in exactly the same way as was demonstrated in the previous examples.

Conclusion

After the development of the ElasticGroup class, it quickly became one of the most usable classes in design of my applications. The class is so flexible that it can be successfully used in the programs for absolutely different areas. These groups combine easiness of design, which is very important for developers, and easiness in use. It is common in programming to use some simple instruments for simple tasks, while for complex tasks much more complex objects are used. The ElasticGroup class stands like an exception from this rule because it is of high demand for the tasks of any level of complexity. Among the simplest are the situations when a group consists of several small buttons. In the most complex cases, there can be many elements inside the group; though developer does whatever he can to design the best possible view, chances are high that users will be not satisfied. It is a standard situation that for the complex groups nearly every user will have his own vision of the good design and the ElasticGroup class gives everyone a chance and an easy way to organize the best view.

In the demo application accompanying the book World of Movable Objects, there are more than 30 examples which use the ElasticGroup class. I want to show the images of some of these groups. The names of the associated forms will help you to find the code and to see how these groups are constructed and used. While looking at these figures, don’t forget that there is nothing fixed in the ElasticGroup objects and if you prefer to change the view of any of them, you can do it in seconds.

From the From_DataViewerTXT.cs

From the Form_DefineNewRing

From the Form_Functions.cs

From the Form_FuncXrYr.cs

From the Form_Zoom.cs

From the Form_SetOfObjects

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

SergeyAndreyev

United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberfredatcodeproject29-May-13 1:02 
GeneralMy vote of 5 PinprofessionalPrasad Khandekar28-May-13 23:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 29 May 2013
Article Copyright 2013 by SergeyAndreyev
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid