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

Moveable Resizable Objects

, 9 Oct 2009
Rate this:
Please Sign up or sign in to vote.
Here is a description of an extremely powerful mechanism that makes screen objects moveable and resizable.

Abstract

People communicate with computers on two different levels. On the upper level we have a very flexible system of windows: we can move them, resize, overlap or put side by side. However, starting an application immediately removes all the flexibility, and now you can work only inside the scenario developed by the designer of the program; you cannot move graphics or controls nor resize them. Here is a description of an extremely powerful mechanism that makes screen objects moveable and resizable. This not only significantly improves the existing applications, but it takes them to another level. This article explains in detail the construction and use of moveable and resizable graphical objects and applying the same technique to controls and groups of controls, which opens absolutely new possibilities for forms’ customization and design of programs. Applications, based on such graphics and controls, become user-driven. This is absolutely new in computers – users’ relation.

Content

Introduction

When you switch on your PC, you usually sink into the world of rectangular windows. You can easily move all these windows, resize them, overlap them or put them side by side. At any moment you can reorganize the whole screen view to whatever you really need. It wasn’t this way at the beginning of the computer era; it became the law after Windows conquered the world. This is an axiom 1 in modern day programming design: On the upper level, all objects are moveable and resizable. To make these features obvious and easy to use, windows have title bars, by which they can be moved, and borders, by which they can be resized. Being moveable and resizable are standard features of all the windows, and only for special purposes these features can be eliminated.

Usually the goal of switching on the computer is not to move some rectangular windows around the screen; you want to do a bit more. You start one or another application, step into the inner level, and then everything changes. Here, inside the applications, you are doing the real work you are interested in, and at the same time you are stripped of all the flexibility of the upper level – you can only do what the designer of the program allows you to do. The design can be excellent or horrible, it can influence the effectiveness of your work in different ways, but still it is awkward that users are absolutely deprived of any control of the situation. Have you ever questioned the cause of this abrupt change? If you have, then you belong to the tiny percentage of those who did. And I would guess that the answer was: “Just because. These are the rules.”

Unfortunately, these ARE the rules, but rules are always based on something. The huge difference between levels is that on the upper level there is only one type of objects – windows, and on the inner level there are two different types: controls, inheriting a lot from windows, and graphical objects that have no inheritance from them and that are absolutely different. The addition of these graphical objects changes the whole inner world of the applications.*

The inheritance of controls from windows is not always obvious, as controls often do not look like windows. Controls have no title bars, so there is no indication that they can be moved; usually there are no borders that indicate the possibility of resizing. But programmers can easily use these features for all controls, and from time to time they do use them, for example, via anchoring and docking. The most important thing is not how controls can be moved and resized, but that for them moving and resizing can be organized, though I have to mention that the programmers use this moving and resizing of controls as their secret weapon and never give users the direct access to it. The designer decides, what would be good for the users in one or another situation, and when user changes the size of the window, the controls inside can change their size and position according with those programmer’s decisions.

Graphical objects are of an absolutely different origin than controls and, by default, they are neither moveable, nor resizable. There are ways to make things look different than what they are in reality (programmers are even paid for their knowledge of such tricks). One technique that programmers use is to paint on top of a control: any panel is a control, so it is resizable by default; with the help of anchoring / docking features, it is fairly easy to make an impression as if you have a resizable graphics, which is changing its sizes according with the resizing of the form (dialog). Simply paint on top of a panel and make this panel a subject of anchoring / docking. By default, panels have no visible borders, and if the background color of the panel is the same as its parent form, then there is no way to distinguish between painting in the form or on the panel, which resides on it. Certainly, such “resizing” of graphics is very limited, but in some cases it is just enough; all depends on the purpose of application. Another solution for resizing of rectangular graphical objects is the use of bitmap operations, but in most cases it can’t be used because of quality problems, especially, for enlarging the images. Both of these tricky solutions (painting on a panel or using bitmap operations) have one common defect – they can be used only with the rectangular objects.

If any limited area is populated with two different types of tenants (in our case – controls and graphical objects), which prefer to live under different rules, then the only way to organize their peaceful residence and avoid any mess is to force them to live under ONE law. Because graphics are neither moveable nor resizable, the easiest solution is to ignore these controls’ features, as if they don’t exist. That is why so few applications allow users to move around any inner parts. Thus we have axiom 2: On the inner level, objects are usually neither moveable nor resizable. Interestingly, these two axioms create this absolutely paradoxical situation:

  • On the upper level, which is not so important for real work, any user has an absolute control of all the components, and any changes are done easily.
  • On the inner level, which is much more important for any user because the real tasks are solved here, users have nearly no control at all. If they do have some control, then it is very limited and is always organized indirectly through some additional windows or features.

Axioms I mentioned were never declared as axioms in a strict mathematical way; at the same time I’ve never seen, read, or heard about even a single attempt to look at this awkward situation in any other way than as an axiom and to design any kind of application on a different foundation. Programmers received these undeclared axioms from Microsoft and have worked under these rules for years without questioning them. If you project these same rules on your everyday life, it would be like this: you are free to move around the city or country, but somebody tells you where to put each piece of furniture inside of your house. Would you question such a situation?

Certainly, anyone reading this article can easily think of an example of resizing a graphical object. For example, in Paint you’ve seen a dotted line, moving with your mouse cursor. It is a fairly easy trick that can be done, for example, with the help of an XOR operation, and it has nothing to do with the real moving or resizing of the objects. This type of “moving or resizing” is only an imitation, though it’s enough for some situations.

Making moveable / resizable graphics is not a theoretical idea of the “nice to have” type. For many years I worked to develop very complicated programs for engineering and scientific tasks in absolutely different areas. Though the aims of these programs had nothing in common, all of them required, to a high extent, the use of different forms of plotting. The quality of users’ analysis of the most difficult problems in both engineering and scientific tasks (and in many other disciplines) highly depends on the quality of the graphical presentation of data and results. Because every user has his personal view about how a system must look to be the best instrument for their own work, the development of such systems goes in parallel with never-ending discussions (and even quarrels) between designers and users. I think that anyone, who designs this type of applications, is familiar with this situation and has to work under the same pressure of multiple requests, which often can demand opposite things. Giving users full control to move and resize objects in such complicated systems will reduce the endless discussions about which graphical layout offers the best view. Full control will significantly increase the effectiveness of engineers’ work with such applications, which is the main goal. I saw again and again that the inflexibility of the objects, designed and fixed by the programmer of the application, became not only the main problem in further improvement of engineering and scientific software, but became the real barrier in exploration and solving of the most interesting problems.

Big engineering and scientific programs are brilliantly designed, but development of big applications takes time, so every user is restricted to whatever vision of the situation the designer had one, two or three years ago. These are the consequences of designing programs on the base of non-moveable and nor-resizable elements – everyone has to work with designer-driven applications.

“In science, finding the right formulation of a problem is often the key to solving it…” [2]. When I started to work on the problem of making objects moveable and resizable, I began with the analysis of the features, which I would like to implement. My goal was not to make some kind of graphical objects moveable (for individual objects of a particular type anything can be done), but to find the general solution. Though initially I looked for the solution for scientific and engineering plotting, which usually has a rectangular shape, but that was only one kind of experimental model. I looked for the general solution and I found it. Before describing the whole algorithm I want to emphasize that:

  • You can use my algorithm in absolutely different areas and with arbitrary forms of objects.
  • You can and must look on the design of moveable / resizable objects separately from the consequences of using them. Classes and algorithm, used for the design, can be different, but if there is a way of turning objects into moveable and resizable, then it is the base for an absolutely new paradigm – user-driven applications.
  • It is not a theoretical idea of a “nice to have” type. At www.SourceForge.net in the MoveableGraphics project (names are case sensitive there!) there is the whole package of applications and documents that explain the algorithm of turning different objects into moveable / resizable and the design of applications on the basis of such objects. Files are constantly renewed there (usually once a month).

Basic Requirements and the Main Ideas

As a designer and a programmer of very complicated systems, I know exactly, what features the system of moveable / resizable elements must have to be very useful for my work.

  • I need an easy way to declare the objects in the form (dialog) moveable and resizable; just touch them (or touch the keyboard several times), and the objects will become moveable ([3]).
  • Easy doesn’t mean that it can be applied only to primitive objects. The configuration of my objects can be at any level of complexity, and changing of configuration might be influenced by a lot of different things. For example, some objects may allow any changes, others may need fixing of several parameters (sizes), and parts of the objects may generate restrictions on other parts’ changes. And still the whole variety of possible reconfigurations must be easy to understand and implement.
  • This feature – moveable and resizable – must be added like an extra “invisible” feature, so it will not destroy any image, but still it will be obvious that it is there. The best thing would be simply to know that objects are moveable and resizable without any additional indication. In some cases, I can demand to show an indication of these features to me by some sort of additional visualization, but usually I would prefer to go on without any extra lines or marks.
  • These features shouldn’t be an “all or nothing” case. Objects of the same classes can be used with or without these new features; so that the decision of declaring an object moveable / not moveable, or changeable / fixed can be even done by a user, while the application is running.
  • The whole technique of working with moveable / resizable elements must be extremely simple: press and move, press and reconfigure, and even press and rotate, which is not organized for windows, but can be very useful for many graphical objects. And if it is useful, it must be organized without any limitations.

Some of these “nice to have” features look like they conflict (simple but with all the possibilities you can imagine) and even provide alternatives (not visible and obvious), but I am not writing the article about the future of programming in 2025. The biggest secret that all these things are designed and work now; a programmer can apply these features to any object.

I am constantly working with the C# language, so all the programs, I am going to mention here, are written in this language; the code samples will be in C#, and I’ll use some terms from this language. But the algorithm and designed classes are not linked only to C# – they can be easily developed with other instruments; I simply prefer to use C#. The samples are from the Test_MoveGraphLibrary application; all codes from this project are available (see link in Programs and Documents at the end of the article). Several additional samples are from the TuneableGraphics application.

The easiest way to move any object around the screen is to drag it, so only three mouse events are used for the whole process: MouseDown, MouseMove, and MouseUp. No additional keyboard pressing is used for moving / resizing anywhere throughout my applications. If an element is involved both in moving and resizing, then starting of one of these processes is determined only by the place, where an object was pressed with a mouse. The distinction between the forward movement and rotation (a lot of elements are involved in both) is made on the pressed button: I use left button for forward movement and right button for rotation.

Applications are populated both with graphical objects and controls. Graphical objects can be of an arbitrary shape; the proposed technique is especially designed for turning into moveable / resizable the objects of any shape. Standard controls have rectangular shape, which simplify some problems; very important things for turning objects into moveable / resizable can be automated in the case of controls. I’ll write about controls and groups of controls later in the special chapters, but throughout the whole explanation, beginning from the simplest cases, you’ll see the moveable controls and groups of controls in the same forms, where some features of moving / resizing the graphical objects are demonstrated. Now let’s begin with graphical objects.

I saw two ways to add moveable / resizable features to the objects: either to use an interface or an abstract class; after trying both ways I decided upon an abstract class. Any object that you want to become moveable and resizable must be derived from the abstract class GraphicalObject, and three crucial methods must be overridden

public abstract class GraphicalObject

{

    public abstract void
        DefineCover ();

    public abstract void Move (int dx, int dy);

    public abstract bool MoveNode
        (int i, int dx,
        int dy, Point

        ptMouse, 

        MouseButtons catcher);

My idea of making graphical objects moveable and resizable is based on covering an object with a combination of sensitive areas, some of which are used to start the forward movement of the whole object, others – for moving of some part(s), which cause either resizing or reconfiguring.* Each sensitive area belongs to the CoverNode class. Three shapes of these objects are used in the current version; the shapes are declared in such enumeration:

enum NodeShape { Circle, Polygon, Strip };

Any circular node is defined by its central point and radius.

Polygon node is described by an array of points. Polygon must be convex! If a polygon must be reconfigured in such a way that it can turn into non-convex, then replace it with an array of convex polygons. Such samples are demonstrated further on (see Chatoyant polygons in the chapter Cover samples).

A strip node is defined by two points and radius. These points are the centers of semicircles at the ends of the strip; the strip’s width is equal to the diameter of these semicircles.

Moving / resizing of any object are defined by its Cover, which consists of an array of CoverNode objects. The number of nodes and their types are not important; any cover can be designed in many different ways, the nodes can overlap or stay apart; the most important is the order of nodes as they may have different effect on moving / resizing, and they are checked for moving according with their order in the array. Usually the number of nodes is small even for really complicated objects, but there are cases, when a huge number of nodes are needed even for an object of a simple form; this is called N-node covers and will be explained later. For graphical objects, the Cover is organized in the DefineCover() method. Controls are automatically wrapped by a graphical object with the needed resizing; this is explained in chapters Use of standard covers and And controls also.

Each node in the cover has its personal identification number (from the range between 0 and the number of nodes in the cover); this number is used for identification in the MoveNode() method.

Note. Though the whole idea of moving and resizing objects is based on Cover, the DefineCover() method is usually the only place where you will have to think about the cover! The rule is: organize the cover and forget about it. There is one exception for this rule; I’ll write about it a bit further, while describing the individual movements of the nodes.

Move (dx, dy) is the method for linear moving the whole object for a number of pixels, passed as the parameters.

The drawing of any graphical objects with any level of complexity is usually based on one or few very simple elements (Point and Rectangle) and some additional parameters (sizes). While moving the whole object, the sizes are not changed, so only the positions of these basic elements have to be changed.

MoveNode (i, dx, dy, ptMouse, catcher) is the method for individual moving of the nodes. The method returns a Boolean value, indicating whether the required movement is allowed; in the case of forward movement, the true value must be returned if any of the proposed movements along the X or Y axes is allowed. If the movement of one node results in synchronous relocation of a lot of other nodes, it is easier to put the call to DefineCover() inside this method, and then it doesn’t matter, what value is returned from the MoveNode(). This may happen even for the forward movement, when movement of one node affects the relocation of the other nodes, and it usually happens with rotation, when all the nodes must be relocated. This is the exception of the previously mentioned rule, that cover is not even thought about anywhere outside the method of its definition.

In some cases the movement of a node starts the resizing of an object, which in turn demands the change of the nodes’ number in cover (this depends on the Cover design); in such situation, the call to DefineCover() inside the MoveNode() may cause a problem. To avoid these problems, the Cover is not changed throughout the resizing, but only when an object is released; I’ll mention these cases while writing about the N-node covers later.

MoveNode() method has five parameters, but not all of them are used each time:
int i Identification number of the node – the same order number that was used in the DefineCover() method on construction of this node.
int cx Movement (in pixels) along the horizontal scale; positive number for moving from left to right; use this parameter if you are writing the code for forward movement.
int cy Movement (in pixels) along the vertical scale; positive number for moving from top to bottom; use this parameter if you are writing the code for forward movement.
Point ptMouse The mouse cursor position. For calculations of forward movement I simply ignore this parameter and use the previous pair; for calculations of rotation I ignore the previous pair and use only this cursor’s position; I found it much more reliable for organizing any rotations.
MouseButtons catcher Informs you, which mouse button was used to grab the object. If by the logic of application the object can be grabbed by any mouse button, then simply ignore this parameter; if the move is allowed by one button only, then use this parameter; in case the node can be involved in both types of movement (forward movement and rotation), this is an extremely useful parameter to distinguish between them.

Usually the method for an individual nodes’ movement is the longest of all three methods; however, there are interesting situations, when the method is very short. Because this method must include the code for moving each node, it can be long, but relatively uncomplicated, as more often than not the code for different nodes is partly the same. Further on I’ll describe a special type of covers, which I call the N-node covers, because they consist of a huge number of nodes. For such covers, the MoveNode() method is usully very short, because the behaviour of all those nodes is identical.

Any object, derived from the GraphicalObject, receives an ability to become moveable / resizable, but can be moved and resized only if registered with the Mover. An object of this class not only supervises the whole moving / resizing process, but also provides a lot of information, associated with it. Regardless of the number of different objects, involved in moving / resizing, usually there is a single Mover object per form (dialog). However, there are situations when it’s better (easier and more reliable) to have several Mover objects in the form; these situations are discussed later in the chapter One, two, many… Movers.

As I mentioned before, only three mouse events are used for the whole process, and these are the methods, where Mover works.

Declare and initialize a Mover object:

    Mover mover = new Mover ();

Register with it all the objects that you want to move and resize

    mover .Add (…);
    mover .Insert (…);

After it the whole moving / resizing can be organized in such a way

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    mover .Catch (mea .Location);
}
private void OnMouseUp (object
                               sender, MouseEventArgs mea)
{
    mover .Release ();
}
private void OnMouseMove (object sender, MouseEventArgs mea)
{
    if (mover
        .Move (mea .Location))
    {
        Invalidate ();
    }
}

This is not a simplification; this is the code, which you see in nearly every form of the Test_MoveGraphLibrary application, regardless of the number or complexity of the objects, involved in moving / resizing.

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.

Those three mouse events – MouseDown, MouseMove, and MouseUp – are the standard places, where Mover is used to organize the whole process of moving / resizing objects. There are two other events – MouseDoubleClick and Paint – where Mover can be mentioned and used, but this happens only on special occasions.

I use the MouseDoubleClick event for opening the tuning forms of the complex objects. Without Mover at hand, I had to decide about the clicked object by comparison of the mouse location and the boundaries of the objects; Mover can do this job much better:

private void OnMouseDoubleClick (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, MouseButtons .Left))
    {
        … …

In the Paint event Mover can be mentioned on those rare occasions, when covers have to be visualized.

    private void OnPaint (object sender, PaintEventArgs e)
    {
        mover .DrawCovers (e .Graphics);
    }

The covers’ visualization is really a rare thing, as good samples of design are those, where objects can be moved by any inner point and resized by any border point. Certainly, for such objects the cover’s visualization is not needed. Cover’s visualization is described in the chapter Visualization of covers.

Safe and Unsafe Moving

The design of applications on the basis of moveable elements opens an opportunity for accidental disappearance of the elements from view. It is impossible in the ordinary applications, but now users can simply move any element out of view across the border, release it there, and then what? If an object is moved across the right or bottom border, and the form is resizable, then it’s not a problem; if an object is dropped anywhere across the upper or left border, then there is no way to return it back to view by resizing the form. The Mover can take care of this situation, but only if you want it to. For this the Mover has to be initialized in such a way, where the form is passed, as a parameter.

    mover = new Mover (this);

You can see that such type of initialization is used nearly everywhere throughout the code of the Test_MoveGraphLibrary application. In such case, when Mover grabs any element for moving or resizing, the clipping will be organized inside the borders of the client area. The level of clipping can be changed with one of the Mover’s properties; three levels of clipping are implemented

    public enum Clipping { Visual, Safe, Unsafe };
  • Visual – elements are moved only inside the client area
  • Safe – elements can be moved outside only across the right and bottom borders
  • Unsafe – elements can be moved outside across all four borders.

By default, the Visual level is used, but can be changed at any moment. And no clipping is used, if the Mover is initialized without any parameter.

Nodes and the Simplest Covers

Text Box:  
Fig.1  Node shapes

Each moveable / resizable object has a cover. Usually this cover is invisible, in order not to destroy any image, but if you want, you can make it visible. The covers provide the moving / resizing mechanism regardless of whether they are visible or not. The best types of covers provide the moving by any inner point of the objects; this means that the cover must close the entire area of an object and completely wipe it out on visualization. For such objects the covers are never shown; it’s enough for users to know that objects can be moved by any inner point. But Test_MoveGraphLibrary is a demonstration program, and for the purpose of better explanation some forms have a small button to switch the covers’ visualization ON / OFF.

Select menu position Nodes and covers – Node shapes and you’ll see something similar to figure 1. There are five moveable objects in this Form_NodeShapes.cs: three colored figures, text, and a small button.

Cover of an object may include any number of CoverNode elements. There are different constructors for Cover, which are described in the next chapter Use of standard covers. For this sample I want to be sure that the cover of each of the shown colored figures consists of exactly one CoverNode of particular type, so I use such constructor for Cover in all three classes

    public Cover (CoverNode [] nodes)

For three classes – PrimitiveStrip, PrimitiveCircle, and PrimitiveRectangle – I’ll construct the covers, consisting of exactly one node of the needed shape.

A CoverNode in the form of a strip has eight different constructors; for the PrimitiveStrip class I use the one, which will set the radius of semicircles at the ends, but the individual movement and the cursor shape get the default values.

public override void DefineCover ()
{
    cover = new Cover (new CoverNode [] { new CoverNode (0, pt0, pt1, radius) }); 
}

A CoverNode in the form of a circle has eight different constructors; for the PrimitiveCircle class I use the one, which will set the radius and the cursor shape, but leave the default individual movement.

public override void DefineCover ()
{
    CoverNode node =
        new CoverNode
        (0, center, radius, Cursors .SizeAll);
    cover = new Cover (new CoverNode [] { node });
}

The circular nodes are used mostly in small size at those points, which are pressed to resize or reconfigure objects, so, by default, these nodes can be moved in any direction and the cursor above them is Cursors.Hand. But in this Form_NodeShapes.cs the circular node is used to cover a relatively big object and not for resizing, but for moving, so to make it consistent with other colored objects, I changed the cursor above this node.

A CoverNode in the form of a polygon has several different constructors; if the number of elements in the array of points, passed to such constructor, is less than 3, then it can also produce either a circle, or a strip. Defining the Cover for the PrimitiveRectangle, I made one change: set the node’s clearance to true. The polygon nodes are used mostly to cover big parts of the objects, so, by default, they do not wipe out the object on cover’s visualization, but here I changed this parameter (only for consistency with others).

public override void DefineCover ()
{
    CoverNode node = new CoverNode (0, Auxi_Geometry .CornersOfRectangle (rc)); 
    node .Clearance = true;
    cover = new Cover (new CoverNode [] { node });
}

To turn all objects in the form into moveable, they must be registered with the Mover.

    mover .Add (circle);
    mover .Add (rect);
    mover .Add (strip);
    mover .Insert (0, text);
    mover .Insert (0, btnCovers, Resizing .None);

The objects in the form are checked for moving according with their order in the Mover’s list; as we expect that the object on top will be selected first, then the objects must be painted in the reverse order to Mover’s list. Controls are always shown atop all the graphical objects, so all controls must precede all the graphical objects in this list, otherwise instead of the upper object something underneath can be moved.

The code for the whole process of moving objects around the screen is extremely simply: one call to Mover in each of the three events.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    mover .Catch (mea .Location);
}
private void OnMouseUp (object sender, MouseEventArgs e)
{
    mover .Release ();
}
private void OnMouseMove (object sender, MouseEventArgs mea)
{
    if (mover .Move (mea .Location))
    {
        Invalidate ();
    }
}

If you don’t care by which button – left or right – the moving must be started, then the code for moving any node is as simple, as shown in the code for the PrimitiveRectangle class.

public override bool MoveNode (int i, int dx, int dy, Point ptM, MouseButtons mb)
{
    Move (dx, dy);
    return (true);
}

However, if you want the start of the moving to depend on the pressed button, then a small change of the code is needed.

First change must be done in the MoveNode() method of the class. For example, if you want the PrimitiveCircle objects to be moved only by the left button, then the MoveNode() method for this class must be changed in such a way.

public override bool MoveNode (int i, int dx, int dy, Point ptM, MouseButtons mb)
{
    bool bRet = false;
    if (mb == MouseButtons .Left)
    {
        Move (dx, dy);
        bRet = true;
    }
    return (bRet);
}

But on trying this you’ll find that the PrimitiveCircle object can be still moved by any button. This happens, if the Mover.Catch() method in the code of the OnMouseDown() is used with a single parameter, as was shown before. To distinguish between the pressed buttons, the Mover.Catch() method must be used with two parameters.

    private void OnMouseDown (object sender, MouseEventArgs mea)
    {
        mover .Catch (mea .Location, mea .Button);
    }

After this change, you’ll find such a situation with our five objects.

  • Circle and the small button can be moved only by the left mouse button. (The small button can be moved by pressing anywhere close to its borders; the explanations are in the next chapter Use of standard covers and further on in the chapter And controls also.)
  • Rectangle and the rounded strip can be moved by pressing them with any mouse button, unless you changed their code in the same way, as for the PrimitiveCircle class.
  • The text can be moved forward by pressing it with the left button; the right button starts the rotation, which is simply one of the features of the TextMR class. All the labels (comments) throughout the application belong to one or another class, derived from TextMR, so all of them have this feature.* I’ll write about the TextMR class further on in the chapter Rotation.

Objects of the Form_NodeShapes.cs demonstrate different shapes of nodes. For objects of different shapes, the covers can be either combined of such nodes, or the standard cover constructors can be used. The next chapter describes the use of the standard constructors of the Cover class.

Use of Standard Covers

Cover class includes a set of constructors. The cover configuration depends on the type of parameters, passed to the constructor; on this base, covers can be divided into four groups.

  1. Covers, based on a set of points.
  2. Covers for the rectangles.
  3. Covers on the base of the rectangle’s corners.
  4. Covers, consisting of a set of arbitrary nodes.

The first three groups are described in this chapter. The fourth group - the most powerful one - allows to combine any set of nodes of different shape and size. Though this constructor is used for the most complicated objects, I would like to mention a funny situation that this constructor was used in the previous chapter for design of the really primitive objects. The majority of objects receive the covers of the fourth group.

Text Box:  
Fig.2  Polygon view from Form_InfinitiveLoop.cs

Points, Connected into an Infinitive Loop

Text Box:  
Fig.2  Polygon view from Form_InfinitiveLoop.cs

Constructors of the first group are based on an array of points. For demonstration of this group, the Form_InfinitiveLoop.cs is designed (figure 2); this form can be opened via the menu position Nodes and covers – Infinitive loop.

Cover (PointF [] pts,   // centers of circular nodes
       int radius,      // radius of each circle
       int halfstrip)   // half of the strips’ width

Each Point of the array becomes the center of a circular CoverNode; each pair of consecutive points is used for construction of a strip node; the last and the first points are also used to construct the strip node, so the loop becomes infinitive.

The screen object and its Cover are linked with each other only for the purpose of moving / resizing this object; the same Cover can be used for absolutely different objects, and the Form_InfinitiveLoop.cs demonstrates it perfectly. The group Parameters and view allows to change the parameters of the Cover and the view. There are five different views; for some of them, but not for all, the changing of parameters also affects the view.

The group Parameters and view is also a moveable object, which can be moved around the screen by any inner point or by its frame. This is an object of the LinkedRectangles class, which is explained in the chapter From bricks to blocks and is widely used throughout the application.

Text Box:  
Fig.4  ReverseCover view
Text Box:  
Fig. 3  Cover view

Nodes of the cover often overlap. When the mouse button is pressed and the Mover analyses the situation for possibility of moving any object, the nodes are checked according with their order in the cover, so this order is very important. The cover of the InfinitiveLoop class first includes all the circular nodes and then all the strip nodes. Two views in this form demonstrate this situation. On figure 3 the nodes are painted according with their order in cover: first all circular nodes are painted, then all the strips, so in this view you see the last nodes atop of all others. But Mover analyses the nodes according with their order, and for Mover all the following nodes at the same place are simply blocked by the preceding, so Mover sees the situation as shown on the ReverseCover view (figure 4).

Two sizes are important for such covers: radius of the circular nodes and the width of the strips. While thinking about them, don’t forget that these are the parameters of the covers, which are usually invisible (there are no circles or strips on figure 2). These parameters are important for mouse operations (press the node and grab for moving or resizing of the object) and can differ from the sizes of the real objects on the screen, even if the view of an object is similar to the cover’s view.

Half of the strip width is used as radius of semicircles at the ends, so the parameter is half, but not the full width of the strip. Six pixels are more than enough to catch the line with a mouse, so in many cases the full width of the strip is set to 6. If you have a one pixel line on the screen, it is still good to have wider than line sensitive area, but if you have a really wide line, it’s not a good idea to declare sensitive only the middle part of it.

Usually I set the diameter of the circle nodes, which are used for resizing, to bigger value, than the strips’ width; in many cases the radius of such nodes is set to 6.

You can produce different situations with combinations of these two parameters. For example, you need an object to be moveable, but not resizable, so that users will have no chances to reconfigure this object; set the radius of circular nodes to 0 and the appropriate width of the strips, and you have the needed object. If you switch the view to ReverseCover, you can try this situation and check it visually:

  • while reducing the radius, you can see the circle, and even for a tiny one with a one pixel radius you can find a small spot to move the circle and reconfigure an object;
  • decrease the radius to zero; the tiny dots disappear, and you have no chances for reconfiguring this object; it can be only moved around.

There can be an opposite situation, when you want the nodes to be moveable, but not the object itself; declare the strips’ width equal to zero and some needed radius of the circular nodes. I’ll demonstrate the use of such combination for the Profile class in chapter Y(x) plots and more.

Attention. If any parameter, involved in Cover design, is changed, then the DefineCover() method must be called. This is very important to keep the Cover correct with the changes of the object. There is only one and very important exception of this rule: in some cases of the special N-node covers it’s important not to change the Cover, though the valuable parameters are changing. I’ll explain this special situation in chapter N-node covers, but until then don’t forget to call the DefineCover() method on changes of parameters, on which the Cover depends.

When you select either Cover or ReverseCover view, the cover for the text (a TextMR object) is also visualized. There is an easy way to visualize only some of the covers, which you need to show; this is explained further on in the chapter Visualization of covers.

Rectangle – General Case

Text Box:  
Fig.5  Rectangles in Form_RectangleGeneralCase.cs

The Cover constructors of the second group are for rectangles. There exist four variants of resizing such rectangles, but their movement by any inner point always exists.

Cover (RectangleF rc,
       Resizing resize,
       int radius,      radius of corner circles
       int halfstrip)   half of the border strips

Variants of resizing are defined by the Resizing enumeration; I hope that their names speak for themselves

enum Resizing { None, NS, WE, Any };

Figure 5 shows all the resizing variants that are demonstrated in the Form_RectangleGeneralCase.cs (menu position Nodes and covers – Rectangle-general case). The red lines show the borders of the nodes, so you can see that the number of nodes depends on the resizing type. The order of nodes is very important for correct use of Cover, so here are the details of all four variants.

Resizing.Any Nine nodes:

  • four circular nodes in the corners: left-top, right-top, right-bottom, and left-bottom;
  • four polygon nodes on sides: left, right, top, and bottom;
  • one polygon node, covering the entire area of rectangle.

Resizing.WE Three nodes:

  • two polygon nodes on sides: left, then right;
  • one polygon node, covering the entire area of rectangle.

Resizing.NS Three nodes:

  • two polygon nodes on sides: top, then bottom;
  • one polygon node, covering the entire area of rectangle.

Resizing.None One polygon node, covering the entire area of rectangle.

For this type of cover, both parameters, which change the sizes of the sensitive nodes in the corners and on sides, cannot be less than 1, so the number of nodes for each type of resizing is fixed (9 – 3 – 3 – 1). With the sensitivity of borders’ strips set to minimum, it is still possible to resize a rectangle, as the change of the mouse cursor on crossing the border is seen very well even in such situation. With the radius of corner circles set to 1, they still work, but it would be a tricky thing to find that single pixel, to which the circle shrinks, though the change of the cursor will help. But I doubt that users would like such a set of parameters.

As you can see from the code of the Form_RectangleGeneralCase.cs; the MoveNode() method for the RectangleGeneral class is a bit long, but rather primitive. It is long, because there are four cases of resizing with individual cover for each case; each attempt of resizing must be checked against the allowed minimum and maximum sizes. Yes, the RectangleGeneral class allows to set the maximum sizes of the rectangle; if you don’t need them, you can develop a similar, but simpler class. There is also a minimum allowed size of any rectangle, which prevents the accidental disappearance of the rectangles during the resizing. If you need to take an object out of the screen, there are always better ways to do it, than by accident.

Two moveable objects in this form consist of controls and texts. The first object includes two combo boxes with the comments; this is an object of the LinkedRectangles class. This group of elements is used without any frame; this is a more common case of using this class. Each ComboBox could be easily paired with a Label, but I prefer to use the painted text in such situations. Visually there is no difference between two situations, but the painted text can be moved by any point and any control can be moved only by the border. I’ll write about this further on in the chapter From bricks to blocks. A single ComboBox with its comment could be organized into another LinkedRectangles object, and this was done in the previous version, but now I changed it for a CommentedControl object.

The resizing type of the four colored rectangles is predetermined (fixed in code); the resizing of the White rectangle can be changed at any moment by selecting the value in the combo box. On changing this resizing type, the cover of this rectangle is changed, so cover is not fixed once and for all, but can be changed at any moment according with the purpose of the application. This can be predefined by the designer, or can be decided by the user of an application, so the decision about the form of moving / resizing can be also given to the users of an application.

If you switch OFF the covers’ visualization, all moving / resizing will work exactly in the same way, but then it is impossible to decide simply by rectangle’s view, in which way it can be resized. This is one of the problems, which I’ll mention in many places throughout the further text: movement and resizing (reconfiguring) of the objects is absolutely independent from the covers’ visualization; after the first half of explanations you’ll see that such additional visualization is not needed at all, so moving / resizing of the objects must become obvious from their view and from the general ideas of the applications’ design.

The Form_RectangleGeneralCase.cs uses the RectangleGeneral class, which is included into the same file. This was done only to demonstrate, how such moving / resizing can be implemented in the code. The resizable rectangles are used in many different situations, so the MoveGraphLibrary.dll includes a very similar ResizableRectangle class. The significant difference is that the minimum and maximum sizes of the rectangle are declared on initialization; the type of resizing is determined, based on these two parameters. The Form_ResizableRectangles.cs (menu position Nodes and covers – Resizable rectangles) demonstrates the use of this class.

Use of such cover constructors. The MoveGraphLibrary.dll includes the class of moveable texts that cannot be rotated – TextM. This class uses this standard cover constructor.

    cover = new Cover (area, Resizing .None);

Rectangle – Case of Controls

The third group of Cover constructors is also used for rectangular areas; in order to distinguish these constructors from the previous group, they use the coordinates of four sides instead of the rectangle as a parameter. This group has also one additional parameter – the choice of moving an object by the inner points of the area or not; in the previous group it was always set to true, but here you have a choice.

The title informs that the constructors of this group are used with the controls. What was the reason to develop special group of Cover constructors? Graphical objects have no inherited features for moving and resizing; they must be specially added to them. But graphical objects can be pressed by a mouse at any point, and their moving / resizing is implemented as a response to such press. The best practice is to resize the graphical objects by any border points and to move – by any inner point. On the contrary, all controls have these inherited features (moveable and resizable), but the pressing of control with a mouse is usually translated into some predefined messages; because users expect the predetermined reaction on such a press, the inner area of any control can’t be used for moving this control. The only and reasonable place to grab a control for moving or resizing is somewhere near its border. Because of this limitation, the Cover constructors of the previous group cannot be used for controls. To move and resize a control, the area around its borders must be divided between the nodes for resizing and the nodes for moving.

FramedControl is a special class in the MoveGraphLibrary.dll, which is used for moving / resizing of controls. As you’ll see in several chapters further on, the controls can be directly included into Mover’s list, or they can be wrapped in the FramedControl; then this object is registered in the List. In reality, the FramedControl wrapper is always used, but when it is not mentioned, then some parameters on its initialization get the default values. In any case, such Cover constructor is used.

Cover (int cxL,                      // left border.
       int cxR,                      // right border.
       int cyT,                      // top
       int cyB,                      // bottom
       Resizing resize,              // type of resizing
       bool bMoveByInside,           // to use (or not) the moving by any inner point
       int radius,                   // radius of the corner circles
       int stripwidth)               // the width of the strips along the sides (full width
                                     //  – not half !)

Form_RectangleControlCase.cs (figure 6 and menu position Nodes and covers – Rectangle-case of controls) demonstrates the use of the FramedControl class for moving and resizing of individual controls. You can see in the code of this form several lines of such a type

    mover .Add (new FramedControl (panelNS, ……));

All the needed covers are organized by the parameters, passed to the new FramedControl object; the DefineCover() method for this class consists of exactly one code line

public override void DefineCover ()
{
    cover = new Cover (control.Left, control.Right, control.Top,
        control.Bottom,
        resize, bMovable, radius,
        frame);
}

Text Box:  
Fig.6  Form_RectangleControlCase.cs

Further on, in the chapter And controls also, I’ll write about the direct including of controls into Mover’s list. When a control is included into the Mover’s list, then the Mover gives such control a wrapper of the FramedControl class, and this wrapper is included into the Mover’s list, but in such a way some of the parameters receive the default values. Here I want to demonstrate the work of all these parameters, so I use the FramedControl objects directly.

There are nine moveable controls in this form – a couple of controls for each Resizing type plus an additional one for a special case; together they demonstrate different aspects of using such covers.

Any Cover of this type always has eight nodes along the frame – this is one of the differences with the previous group, where the number of nodes depends on the Resizing type. If you don’t see part of these nodes around some of the controls, then it means that their parameters were changed so, as to make them invisible and not working, but the number of nodes didn’t change, and this constant numbering makes the implementation of the MoveNode() method easier.

These eight nodes are numbered from the left top corner and going clockwise, so the node in the left top corner has the number 0, in the middle of the upper side – 1, and so on. Four nodes in the corners are circular. The Form_RectangleControlCase.cs was designed especially for demonstration, so you can switch the covers’ visualization ON / OFF, but in real applications I never show the covers, so the easiness of moving and resizing the controls depends on the design decision and the selection of several parameters.

The default size of the corner nodes is bigger than the width of the frame: the frame’s width is 6 pixels, and the circles’ diameter is 12. The enlarged size of the corner nodes makes them preferable place for resizing.

The corner nodes are used not only for Resizing.Any, but for two other types of resizing (WE and NS); for these three types of resizing the cursors over the corner nodes are different, and the resizing works according with the demand.

On comparison of the controls with the same resizing type, you can see that the nodes in the side middles can be either tiny squares or several times bigger rectangles. There is no additional parameter or method to choose this shape, because it is done automatically, depending on the side’s size. If the side is small, then there will be a small square node in the middle, so that part of the frame between the middle node and the corner node can be still used for moving the object. With the growth of the side, the square can be changed to rectangle; the bigger node is easier to find with a mouse, but there is still place on the side both for resizing and moving. You can change the sizes of the ListView or GroupBox with the Resizing.Any type and see this change of the nodes.

The type of resizing and needed ranges are not passed as parameters on FramedControl initialization. Instead, the MinimumSize and MaximumSize properties of the control can be used for setting such ranges; the analysis of these values and their comparison with the control’s sizes defines the type of resizing. In addition, the size of the control, which is used inside FramedControl, can not be less than 16 pixels. This was added to avoid the accidental disappearance of the control.

The ListView with all the nodes around it, but without the red frame – the only control of such type in view – demonstrates the special situation of resizable, but not moveable control. I am not sure that this is a useful combination for a control, but I want to mention it, because it is possible, if the bMoveByInside parameter is set to false. The biggest part of the inner area is simply closed by a control itself, so the parameter bMoveByInside is applied to those parts of the frame, which are not covered by the mentioned eight nodes, aimed at resizing. If this parameter is set to false, then the frame disappears, but all the resizing are still possible. To be absolutely correct, there is a funny way to move even this control, but exactly in the way all the caterpillars are moving. It also reminds me of Alice in Wonderland, eating the mushroom from her left and right hands in order to return to some proper size, but I had no such picture in mind, when I was developing the FramedControl class.

The GroupBox with several controls inside demonstrates several things. On resizing this GroupBox, the controls inside change the sizes according with the declared rules of dynamic layout, so it has no conflicts with the implemented technique of moving / resizing objects You can see that on the upper side the red line (the border of the frame) is farther apart from the line of the GroupBox than on the other three sides. It’s not a bug in the FramedControl design, but simply the origin of the standard GroupBox class. When the covers are not visualized (this is a common case) and you try to move a standard GroupBox (but wrapped into FramedControl) around the screen, then it becomes annoying that on three sides you can grab the group by its visual frame (two or three pixels don’t make the difference), but on the upper side you can’t, unless you move the cursor somewhere up. The background color of the form and of the GroupBox are the same, so there is no way to see the upper bound of the GroupBox and it’s annoying not to find the moveable frame anywhere near the visible line, but only far away from it. And why the same GroupBox can’t be moved by any inner point? Programmers know that the full inner area is blocked by the GroupBox, but users don’t know anything about it. Because of all these annoying things, I included into MoveGrapLibrary.dll the Group class, which I prefer to use instead of the standard GroupBox. I use the Group class as the main element in forms’ design; this class is used in several forms of the Test_MoveGraphLibrary application and there will be more about this class in the chapter From bricks to blocks.

These were the three groups of standard Cover constructors. Though some objects can simply use the constructors from these groups, in general objects have covers, combined of a set of CoverNodes. The next chapter is about some of such objects.

Cover Samples

This is a collection of objects with different types of covers. Some of these objects can be rotated, but I will write about the rotation in a special chapter, so this part of explanation will come later.

An Apartment House

Text Box:  
Fig.7  Form_ApartmentHouse.cs

Several houses from the Test_MoveGraphLibrary application are used for explanations throughout this text. The ApartmentHouse class is the simplest of them (figure 7, menu position Nodes and covers – Apartment house).

The cover for such objects is very close to what was shown at figure 5 for a rectangle with the Resizing.Any case. Only this house has an additional circular node on the ridge, and the inner area is covered by the pentagon. While writing about the CoverNode class, I mentioned that one of the three basic shapes is polygon. In all the previous samples polygon nodes were always of a rectangular shape, but there is no such restriction, and these nodes may have any number of apices. The only requirement is that the polygon must be a convex one.

The code of the MoveNode() method for the ApartmentHouse class includes some additional checking for possible nodes’ position and even the forced relocation of the ridge on squeezing the house, because at any moment the ridge must be not closer to the side of the house than half of the room. This restriction is simply an architect’s whim.

Once more about the left and right mouse buttons. If you check the code for the ApartmentHouse class, you’ll see that such houses are supposed to be moved by the left button:

public override bool MoveNode (int i, …, MouseButtons catcher)
{
    bool bRet = false;
    if (catcher == MouseButtons .Left)
    {

But if you try, the house can be moved and resized by any button! OK, this situation was already mentioned before in the chapter Nodes and the simplest covers. If the pressed button is not passed as a parameter to the Mover.Catch() from inside the OnMouseDown() method, then the default value (left button!) is used as a parameter, and MoveNode() works according with this value. Then why do I prefer to underline the use of the left button inside the ApartmentHouse.MoveNode() method? Because the ApartmentHouse objects are used in other places (for example, in the Form_Main.cs), where the pressed button is important. In that form the button is passed as a parameter from the OnMouseDown() method, and the houses are moved only by the left button. But to do this, the left button must be mentioned, while describing the needed moving / resizing in the ApartmentHouse.MoveNode() method.

Text Box:  
Fig.8  RegPoly_NR objects
Text Box:  
Fig.7  Form_ApartmentHouse.cs

Three Types of Regular Polygons

Let’s take a simple regular polygon and see, how it can get different types of resizing. The code for this sample is in the Form_RegularPolygons.cs (menu position Nodes and covers – Regular polygons).

First, let’s develop a class for non-resizable, but moveable polygons - RegPoly_NR (figure 8).

public class RegPoly_NR : GraphicalObject
{
    protected PointF ptC;	// polygon center
    protected int radius;	// radius of the apexes
    protected int nApexes;	// number of apexes
    protected double angle;	// angle to the first apex
    protected SolidBrush brush;	// the brush 
    protected int minR = 20;	// minimum radius

Everything for such polygons – painting, moving, and resizing, when we’ll come to it – everything depends on the apexes positions, so here is the property for their calculation

protected PointF [] Apexes
{
  get
  {
    PointF [] pts = new PointF [nApexes];
    for (int i = 0; i < nApexes; i++)
    {
        double ang = angle + 2 * Math .PI * i / nApexes;
        pts [i] = new PointF (ptC .X + Convert.ToSingle (radius * Math.Cos (ang)),
                              ptC .Y - Convert.ToSingle (radius * Math.Sin (ang)));
    }
    return (pts);
  }
}

The cover for non-resizable polygon consists of exactly one polygon node, so the DefineCover() method consists of one line.

    public override void DefineCover ()
    {
        cover = new Cover (new CoverNode [] { new CoverNode (0, Apexes) });
    }

Method of moving such polygons also consists of one line

    public override void Move (int dx, int dy)
    {
        ptC += new Size (dx, dy);
    }

Moving of a single node has to do nothing else, but to call the Move() method, if the proper button was pressed.

    public override bool MoveNode (int i, int dx, int dy, Point ptM,
                                   MouseButtons catcher)
    {
        bool bRet = false;
        if (catcher == MouseButtons .Left)
        {
            Move (dx, dy);
            bRet = true;
        }
        return (bRet);
    }
Text Box:  
Fig.9  RegPoly_RA objects

Everything is very simple: any polygon in view can be moved by any inner point, but no resizing for RegPoly_NR.

Objects of the next class – RegPoly_RA – differ only in one aspect: they can be resized by moving any apex (_RA). RegPoly_RA class is derived from the RegPoly_NR and has not a single field or method of its own.

public class RegPoly_RA : RegPoly_NR
{
    public RegPoly_RA (Point center, int rad,
                int apexes, double angDeg, Color clr)
        : base (center, rad, apexes, angDeg, clr)
    {
    }

The cover for RegPoly_RA class combines a set of circular nodes in the apexes with an additional polygon node, filling the entire area of an object. When a mouse is pressed anywhere close to an apex, the start of resizing is expected, but not moving, so the polygon node must be the last in the queue of nodes.

public override void DefineCover ()
{
    CoverNode [] nodes = new CoverNode [nApexes + 1];
    PointF [] pts = Apexes;
    for (int i = 0; i < nApexes; i++)
    {
        nodes [i] = new CoverNode (i, pts [i], 5);
    }
    nodes [nApexes] = new CoverNode (nApexes, Apexes);
    cover = new Cover (nodes);
}

Usually, if I have to draw the cover, I do it after all other drawings. In the Form_RegularPolygons.cs the covers are not painted at the end, but in order with the polygons, to which they belong. The nodes, closed by the objects with the higher priority, are not visible to Mover at all; figure 9 shows exactly the situation, which Mover has to analyze.

Moving of the RegPoly_RA objects is identical to the base class moving, so the new Move() method is not needed, but the situation with the MoveNode() method is different. Two types of nodes in the cover of the RegPoly_RA polygons have different purposes (resizing or moving), so the MoveNode() method must be new and becomes longer.

public override bool MoveNode (int i, int dx, int dy, Point ptM,
                               MouseButtons catcher)
{
    if (catcher == MouseButtons .Left)
    {
        if (i < nApexes) {
            double dist = Auxi_Geometry .Distance (ptC, ptM);
            if (dist >= minR)
            {
                radius = Convert .ToInt32 (dist);
                DefineCover ();
            }
        }
        else {
            Move (dx, dy);
        }
        return (true);
    }
Text Box:  
Fig.10  RegPoly_RB objects

I mentioned before that if the movement of one node changes the positions of other nodes, then it’s better to call the DefineCover() method from inside the MoveNode() method. This is just the case of the RegPoly_RA class.

The third class of regular polygons – RegPoly_RB – can be resized by any border point (_RB). The cover for this class combines an infinitive loop of strips with an additional polygon node (figure 10). We had a sample of an infinitive loop before (figures 3 and 4); only that loop consisted of circular and strip nodes, and here we have only strip nodes, because in RegPoly_RB moving of an apex is identical to moving of any other border point, so the circular nodes in the apexes are not needed. When a mouse is pressed anywhere close to the border, the start of resizing is expected, but not moving, so the polygon node must be the last in a queue of nodes.

RegPoly_RB class is derived from the RegPoly_NR and has one additional field and the StartScaling() method, which provide the correct resizing regardless of the border point, where the resizing starts.

public class RegPoly_RB : RegPoly_NR
{
    double scaling;
    public RegPoly_RB (Point center, int rad, int apexes, double angDeg, Color clr)
        : base (center, rad, apexes, angleDegree, clr)
    {
    }

In the previous case of the RegPoly_RA class, the resizing can start only somewhere very close to an apex, so the proposed radius is estimated as a distance between the mouse point and the center point; this distance can be checked against the minimum allowed radius to decide about the possibility of resizing. With the RegPoly_RB class the situation is not so simple, as the resizing can start with the mouse located at any border point; for triangle the mouse can press the border at such distance from center, which is only half of the radius, so radius can not be substituted by this distance. But if at the moment of the resizing start the coefficient between the mouse distance from center and the current radius is calculated, then this coefficient, called scaling, can be used throughout the resizing process for correct calculation of radius at any moment.

For this reason, the OnMouseDown() method is slightly changed; if the caught object is of the RegPoly_RB class, then the StartScaling() method of this object is called.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, mea .Button))
    {
        if (mover .CaughtSource is RegPoly_RB)
        {
            (mover .CaughtSource as RegPoly_RB) .StartScaling (mea .Location);
        }
    }
}

The StartScaling() method in the RegPoly_RB class is primitive and consists of one line, but this is all what is needed.

public void StartScaling (Point ptMouse)
{
    scaling = radius / Auxi_Geometry .Distance (ptC, ptMouse);
}

With the known coefficient, it’s easy to decide about the possibility of resizing regardless of its starting point

public override bool MoveNode (int i, …, Point ptM, MouseButtons catcher)
{
    if (catcher == MouseButtons .Left)
    {
        if (i < nApexes)
        {
            double distanceMouse = Auxi_Geometry .Distance (ptC, ptM);
            if (distanceMouse * scaling >= minR)
            {
                radius = Convert .ToInt32 (distanceMouse * scaling);
                DefineCover ();
            }
        }
        else
        {
            Move (dx, dy);
        }

I am writing about this simple scaling in details, because the same technique is used for resizing of more complicated objects (only there the StartScaling() method will have an additional parameter) and the same technique is used for rotation of objects.

Three classes of regular polygons were developed to demonstrate the differences in their cover design. In reality three classes are not needed here, as the cover of any object can be changed just on a fly. One of the previous samples – Form_RectangleGeneralCase.cs – demonstrates four variants of rectangle’s resizing. The cover for the fifth rectangle in that form (figure 5) can be changed at any moment via the ComboBox.

Similar thing can be done with the regular polygons. You can do it as an exercise; then you can look into the Form_CombiRegularPolygons.cs for such a sample. The RegPoly_Combi class simply combines the pieces of code from those three classes of regular polygons with different types of resizing. At any moment you can call the context menu on any polygon in this form and change its type. This requires only the identification of the polygon, which was clicked by the mouse. The Identification() method in this form is primitive, but it is of the same type that is used throughout the application for identification of more and more complicated objects.

Text Box:  
Fig.11  Form_ChatoyantPolygons.cs

Changing of the object’s type of resizing is demonstrated here on a primitive sample, but this thing can be very useful in a lot of places. Suppose that the users of your program are dealing with a lot of different objects, which can change their configuration in order to fit better with each other. When after some transformations an object becomes of the needed form and size, the user can click on it and declare it only moveable, but unchangeable; this will prevent the accidental changes. Such object can be moved around the screen, but if at any moment further transformations will be needed, the user can again declare this object changeable in the same easy way.

Chatoyant Polygons

The covers for polygons from the Form_ChatoyantPolygons.cs (menu position Nodes and covers – Chatoyant polygons) demonstrate the use of all three different types of nodes (figure 11). At the moment of initialization and without cover visualization all these polygons look very similar to the regular polygons from figure 8, because these polygons are also born as regular by shape, but with a chatoyant painting. But switch ON their cover visualization, and the difference becomes obvious:

  • Each apex has a circular node.
  • The center point also has a circular node.
  • Each border segment is covered by a strip node.
  • The inner area is covered not by a single polygon node, but by a set of triangles; each triangle is a polygon node itself.

The nodes of different shapes are used here for three different movements.

Circular nodes are used for individual movements of the points, with which they are linked, regardless of whether it is an apex or a center point. This is the way for reconfiguring a polygon, and though born as regular polygons, these objects can be quickly turned into very strange figures.

Strip nodes are used for zoom.

Polygon nodes (triangles) are used for moving the whole object.

For originally regular N-gon, the number of nodes is 3 * N + 1, so a polygon with 12 apexes has 37 nodes. It looks like there must be a lot of code writing for such object, especially in the MoveNode() method, but in reality it’s not so, because the nodes of the same group have similar behaviour.

The scaling of transformed polygons is done exactly in the same way, as was described for regular polygons, only in this case of an arbitrary object’s shape, the angle, radius, and scaling coefficient for each apex must be saved, so in the StartScaling() method not one scalar, but three arrays are calculated. The zoom operation must be started on pressing not any node, but only those of them, which cover one of the border segments, so, in addition to the catch point, the number of the pressed node must be sent as a parameter to this method.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mea .Button == MouseButtons .Left) {
        … …
        if (mover.CaughtSource is ChatoyantPolygon) {
            (mover.CaughtSource as ChatoyantPolygon).StartScaling (mea.Location,
                                                               mover.CaughtNode);
        }

Inside the StartScaling() method, the decision about the start of zoom can be based directly on the node’s number, because the order of nodes is well known (look into the DefineCover() method), but I want to demonstrate the use of one of the Cover’s methods. The zoom operation must be started at any point of the lines between the two consecutive apexes; these lines are covered by the strip nodes; the strip nodes are not used anywhere else except these lines, so the decision to start zoom can be based on the type of the caught node.

    public void StartScaling (Point ptMouse, int nodeCaught)
    {
        if (cover.GetNodeShape (nodeCaught) == NodeShape.Strip)
        {

Cover has a full set of methods to get or set the parameters of an individual CoverNode or to set the parameters for all nodes of a particular type; these methods are described in the MoveGraphLibrary_Classes.doc and are very useful in a lot of situations. Certainly, the same checking of the pressed node type can be done on the previous level in the OnMouseDown() method, but this depends on how you prefer to write the program. If you want to call the StartScaling() method only when it is really needed, then add an additional checking into the OnMouseDown() method and exclude the second parameter from the StartScaling(). Only don’t forget to make the same changes in other forms (Form_Main.cs and Form_LinkedElements.cs), where the same ChatoyantPolygon class and its StartScaling() method are used. This is a variant of the OnMouseDown() method with the checking of the node type.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    … …
    if (mover .CaughtSource is ChatoyantPolygon  &&
        mover.CaughtNodeShape == NodeShape.Strip)
    {
        (mover .CaughtSource as ChatoyantPolygon) .StartScaling (mea.Location);
    }

Forward movement, reconfiguring, and resizing are organized for the objects of the ChatoyantPolygon class. Is there anything else? Yes, there is a rotation! Nodes of three different shapes cover the whole area of an object, and the starting of one of the mentioned movements depends on a type of the pressed node. All those movements are started with the left button press; the rotation is started by the right button regardless of the node, which happened to be underneath.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    … …
    else if (mea .Button == MouseButtons .Right) {
        … …
        if (mover.CaughtSource is ChatoyantPolygon) {
            (mover.CaughtSource as ChatoyantPolygon). StartRotation (mea.Location);
        }

The StartRotation() method is very similar to the StartScaling() method; it also calculates the three arrays; two of them are identical (radius and angle of each apex), the third array includes an additional angle compensation for each apex, depending on the starting mouse position. The rotation is described in more details in the chapter Rotation.

The new thing in the Form_ChatoyantPolygons.cs, which wasn’t used in the previous samples, is the possibility of changing the objects’ order. The whole set of polygons is like a multilevel construction with a single object on each level. Polygons can be moved and transformed on their personal levels without changing the order of objects, but if you click any polygon, then it pops up and appears on top of all others. With an addition of this new thing, the new dilemma opens on any click of any object: is it a simple forward movement of an object or a call for popup? The same type of dilemma opens in a lot of situations on a right button press: is it a rotation or a context menu call? Throughout an application, I am looking at these situations in such a way: if the distance between the occurrences of the MouseDown and MouseUp events is relatively small, then it is a call for popup or context menu; if it is bigger, then it is a forward movement or rotation. There can be situations, when it is not so, for example, you really need to move an object for one or two pixels or rotate it for a tiny angle. In such a case, after the needed movement a less expected thing will happen (for example, a menu will appear), but such situations are rare, and in addition to what was really a required action another one happens, but it’s not a disaster.

N-node Covers

Prior to version 5.01, the available set of nodes didn’t include strips; there were only circular and square nodes; later polygon nodes were added. That set of nodes worked perfectly for nearly any situation, but there were some exceptions, which marred the whole design. For some objects, it was difficult to think out an easy way to resize them by any border point, especially for the curved borders. Then I thought out a special general solution for such cases: it was not in adding the new type of nodes, but in design of the covers on the base of a huge number of standard small nodes, covering any border without gaps, overlapping each other and forming a narrow sensitive area along the border of an arbitrary shape. I called this type of covers N-node covers. If you are familiar with the previous versions of MoveGraphLibrary.dll, then in its description you could see even the samples of rectangles with such cover type. With an addition of the strip nodes, such technique is not needed for rectangles any more, but the N-node covers are very helpful in other cases, so let’s look at them.

Circle

The NCircle class from the Form_NNodeCovers.cs (menu position Nodes and covers – N-node covers) is a relatively simple object (figure 12). The cover consists of only the circular nodes of different size: the big one covers nearly the whole area of an object and is used for moving it; a set of small overlapping circles cover the border; these nodes are used for resizing.

public override void DefineCover ()
{
    int nOnPerimeter = Convert .ToInt32 ((2 * Math .PI * radius) / distNeighbours);
    CoverNode [] nodes = new CoverNode [nOnPerimeter + 1];
    nodes [0] = new CoverNode (0, ptCenter, radius - nrSmall + 1, Cursors.SizeAll);
    for (int i = 1; i <= nOnPerimeter; i++)
    {
        nodes [i] = new CoverNode (i, Auxi_Geometry .PointToPoint (ptCenter,
                          2 * Math .PI * (i - 1) / nOnPerimeter, radius), nrSmall);
        nodes [i] .Clearance = false;
    }
    cover = new Cover (nodes);
}
Text Box:  
Fig.12  N-node covers for circle and ring

The radius of the small circles (nrSmall = 5) is defined in such a way that the grabbing of the border would be easy. The distance between the centers of consecutive circles (distNeighbours = 9) is set so that the union of the overlapping circles compose a sensitive strip, covering the border.

The big circle always has number 0; the small circles are numbered from 1 and up; the total number of small circles on the perimeter (nOnPerimeter) is defined by the big radius and the distance between the neighbours. The number of small circles changes all the time with the change of the big radius, but at any moment they cover the perimeter exactly in the same way; you can see it by resizing the circle, while the cover visualization is switched ON. What makes possible such a simple work with the N-node cover of NCircle is the fact that all the small circles are absolutely identical in their behaviour, and it doesn’t matter, which of them is used for resizing at any particular moment.

 

Ring

The NRing class is a bit more complicated and much more interesting. Both borders – inner and outer – are covered by the small circular nodes, used for resizing; in order to organize the forward movement of the ring, its entire area is covered by a set of polygon nodes. That is a real sample of an N-node cover – the number of nodes is in hundreds and can easily exceed 1000. But look at the code: it is fairly simple, when you have an idea of what is behind the cover design.

The whole array of nodes is combined of three different sets in such an order:

  1. Circular nodes on the outer border.
  2. Circular nodes on the inner border.
  3. Polygon nodes between two borders.

An interesting and the most important thing of this design that the number of nodes in each set is not calculated inside the DefineCover() method, but outside of it. You can try, what would happen, if you define the cover for NRing in the same way, as for NCircle, where the number of small circles is calculated inside the DefineCover(). Try to squeeze the outer border, and at some moment instead of it the inner radius will start to change! This also depends on the small circle, with which you start the resizing, but after several attempts you will see this strange thing. Try to squeeze the inner radius, and at some moment the whole ring may start moving around the screen. What is the cause of these strange and definitely unexpected results? If the number of nodes in each set is constantly recalculated on the base of changing inner and outer radiuses, then it is possible that the number, with which you started the resizing, will jump from one set to another, and then another part of the MoveNode() method will be in use.

To avoid these unneeded situations, the number of nodes in each set must be fixed from the start of resizing until the object is released, but recalculated at the moment of the ring’s release according with the new sizes. The correct number of circles on the borders (and the number of polygons is linked to the number of circles on the outer border) is calculated at the moment of the ring construction in the NodesOnBorders() method.

private void NodesOnBorders ()
{
    nNodesOnOuter = Convert .ToInt32 ((2 * Math .PI * nrOuter) / distNeighbours);
    nNodesOnInner = Convert .ToInt32 ((2 * Math .PI * nrInner) / distNeighbours);
    nNodesPoly = nNodesOnOuter / 2 + 1;
}

Throughout the resizing, these numbers are not changed; you can see that on enlarging any border, the small nodes on it does not overlap any more, but can stay far away from each other. These gaps are not important during the resizing, because the number of the node that starts the resizing is fixed and is not changing. But at the moment of the object’s release everything must be corrected, and this is done in two steps.

The first addition is in the OnMouseUp() method, where the NRing.RedefineCover() method is called.

private void OnMouseUp (object sender, MouseEventArgs e)
{
    if (mover .Release ())
    {
        if (mover [mover .WasCaughtObject] .Source is NRing)
        {
            (mover [mover .WasCaughtObject] .Source as NRing) .RedefineCover ();
            Invalidate ();
        }
    }
}

The second addition is this RedefineCover() method, from which first the NodesOnBorders() is called to calculate the correct number of nodes in each set and after it the new cover is defined.

    public void RedefineCover ()
    {
        NodesOnBorders ();
        DefineCover ();
    }

Though the number of nodes in NRing is huge, the MoveNode() method is simple and short, because it doesn’t matter, which node exactly is moved at the moment; the important thing is to which of three sets this node belongs, and the code for each group is simple.

The NRing class demonstrates the correct procedure to deal with the N-node covers. The covers of this type usually have a huge and depending on the sizes number of nodes. As a rule, the calculation of the node number inside the DefineCover() method will lead to different mistakes, the correct number of nodes is calculated at the moment of design and then every time an object is released, but this number is never changed during the period, when an object is caught by Mover.

Rotation

Rotation of the screen objects is used maybe not as wide as the forward movement but often enough. Even on the very first acquaintance with the proposed technique (figure 1) there was one object – a text – that could be rotated. The whole algorithm of turning all objects into moveable / resizable is based on covering the object’s area with a set of sensitive nodes and the transformation of the movement of the grabbed node either into the reconfiguring (resizing) of this object or into the movement of the whole object. MoveNode() method is the place, where these transformations are described.

    public abstract bool MoveNode (int i, int dx, int dy, Point ptMouse, 
                                   MouseButtons catcher);

For forward movement, more accurate results are received by using two of the parameters dx and dy, which describe the linear mouse movement. For rotation, it’s better to use the exact mouse position from the ptMouse parameter.

The painting of even the most complicated objects is always based on some set (usually, a very small set) of primitives: points and rectangles. When such object must be rotated, then, to avoid any distortions, the angle(s) between these basic elements must be stored at the start of the rotation, and these fixed angles must be used throughout the rotation.

There are four different samples in this chapter, and though they demonstrate the same general method of organizing the rotation, implementation for one of these classes – TextMR – differs from all others. The rotation can be started by pressing the right button at any inner point of these objects; at that moment the angle between the mouse position and the object’s angle must be calculated. During the rotation this angle doesn’t change. I call this angle compensation, and the method, in which it is calculated, is called StartRotation(). The only sample, where you’ll not find those things, is TextMR. The class is so widely used (by itself and as a base class for all different types of comments) that for it the calculations at the beginning of rotation are done automatically. There are two more classes in MoveGraphLibrary.dll, for which these calculations at the beginning of rotation are done by default without any mentioning - PieChart and RingSet. Both classes are used in the area of financial graphics and can be seen at figure 28 in the chapter Complicated applications.

TextMR

Text Box:  
Fig.13  TextMR objects together with the covers

A TextMR object is a text that can be moved and rotated. This class is included into the MoveGraphLibrary.dll, so the full description of its constructors, properties, and methods can be found in MoveGraphLibrary_Classes.doc. TextMR objects can be used by themselves; this class is also used as a base class for different types of comments. Form_TextSamples.cs (menu position Nodes and covers – TextMR samples) demonstrates several TextMR objects; at figure 13 they are shown together with their covers.

A TextMR object has a very simple cover, consisting of one polygon node; rotation of such text goes around its middle point. Rotation can be started by pressing with the right button at any inner point of the TextMR object.

In the Form_TextSamples.cs, three TextMR objects are designed, registered with the Mover, and that’s all. I hope that now you are familiar with the way the moving / resizing of objects is organized: three standard mouse events have one line each, in which the Mover is mentioned, and usually that’s enough. In general case of rotation it’s not enough and one or two small additional things must be done, but not in the case of the TextMR class – for this class everything is automated. There is only one exception: if you want to show the currently changing angle of the TextMR object under rotation, then the TextMR.AngleLabel() method must be called at the beginning of rotation. For example, you can find such sample in the Form_Profiles.cs.

    private void OnMouseDown (object sender, MouseEventArgs mea)
    {
        …
        else if (mea .Button == MouseButtons .Right) {
            if (mover .Catch (mea .Location, mea .Button)) {
                if (mover .CaughtSource is TextMR) {
                    (mover .CaughtSource as TextMR) .AngleLabel (bShowAngle);

This form of organizing the label for the TextMR object under rotation is left in the Form_Profiles.cs. especially to show the details. For the TextMR class this thing can be also automated, only to get this effect the Mover.Catch() method must be used not with two, but with three parameters. In many forms, in which the comments under rotation can be shown together with their angle, you’ll see such a call

    mover .Catch (mea .Location, mea .Button, bShowAngle);

To show or not the small label with the changing angle is decided by the user and it is set through the context menu. If shown, the label appears on the horizontal line to the right of the text’s center at the moment, when the text is caught for rotation, and disappears, when the text is released.

In the Form_TextSamples.cs the label with an angle is not shown; to see it, add the true value as the third parameter into the mover.Catch(…) method.

Objects of the TextMR class have a very primitive cover, consisting of one polygon node (figure 13). All classes, derived from TextMR, have the same type of cover, but you’ll never see them. The cover for the TextMR class is left visible for the purpose of better explanation; the covers for this and all the derived class don’t need any visualization, because any text can be simply moved and rotated by any inner point. The explanation of a simple way to get rid of the cover’s visualization is given further on in the chapter Visualization of covers.

Polyline

Text Box:  
Fig.14  Polyline object to move and rotate

A Polyline object is a classical object in rotation. Figure 14 demonstrates Form_Polyline.cs (menu position Nodes and covers – Polyline) with four moveable objects:

  • a small button to switch covers ON / OFF - can be moved;
  • a small red spot - center of rotation - can be moved;
  • the text in green - a TextMR object – can be moved and rotated;
  • a blue polyline – a Polyline object – can be moved by any segment and rotated by any segment end.

Polyline has a StartRotation() method to calculate all the needed radii and angles at the moment of the rotation start. This method is called from inside the OnMouseDown(); the decision about calling the method is based first on the class of the object that Mover has caught; if it is Polyline, then also the shape of the caught node has to be circular.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    … …
    else if (mea .Button == MouseButtons .Right)
    {
        if (mover .Catch (mea .Location, mea .Button)) {
            if (mover .CaughtSource is PolyLine && 
                mover .CaughtNodeShape == NodeShape .Circle)
            {
                pline .Anchor = center .Point;
                (mover [mover .CaughtObject] .Source as PolyLine)
                                                   .StartRotation (mea .Location);
                bInRotation = true;
                Invalidate ();

The StartRotation() method calculates the radius for each segment end and the angle between the mouse cursor and each segment end; these values are not changed throughout the rotation.

public void StartRotation (Point ptMouse)
{
    double angleMouse = -Math .Atan2 (ptMouse .Y - ptAnchor .Y,
                                      ptMouse .X - ptAnchor .X);
    for (int i = 0; i < pts .Length; i++)
    {
        radius [i] = Auxi_Geometry .Distance (ptAnchor, pts [i]);
        compensation [i] = Auxi_Common .LimitedRadian (angleMouse +
                Math .Atan2 (pts [i] .Y - ptAnchor .Y, pts [i] .X - ptAnchor .X));
    }
}

While the Polyline object is in rotation, the circles for all the segment ends are painted. To determine, if these circles must be painted, there is an additional variable bInRotation, which changes its value inside the OnMouseDown() and OnMouseUp() methods. You can get rid of this additional variable and rely on the Mover for this thing. Comment all the places, where bInRotation is mentioned (four lines in the code) and change the checking in the OnPaint() method for such line (this line is left as a comment there).

    if (mover .Caught  &&  mover .CaughtSource is PolyLine 
                       &&  mover .CaughtNodeShape == NodeShape.Circle)

Circle

Text Box:  
Fig.15  NRCircle object

In the chapter N-node covers I demonstrated a circle and a ring; both of them were stripped of any rotation in order to keep the explanation of the N-node technique pure and simple. Now let’s add the rotation to a circle; for better visualization this will be a multicolored circle.

Form_CircleInRotation.cs (menu position Nodes and covers – Circle in rotation) has one object of the NRCircle class (figure 15); the cover for such objects is identical to the cover of the NCircle class and consists of a big circular node inside and a set of small circular nodes, covering the perimeter. There is an array of values, associated with an NRCircle object; the values define the division of a circle into sectors; each sector has its own color. The initial coloring of a circle starts at zero degree and goes counter clock. This starting angle (variable angle) changes with the rotation of a circle, but the division of circle into sectors doesn’t, because the values are fixed, so the only important thing is the correct calculation of the starting angle.

The rotation is done by the right button, so there is a check inside the OnMouseDown() method, if the rotation has to start.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, mea .Button))
    {
        if (mea .Button == MouseButtons .Right &&
            mover [mover .CaughtObject] .Source is NRCircle)
        {
            (mover [mover .CaughtObject] .Source as NRCircle)
                                                  .StartRotation (mea .Location);
        }

If the NRCircle object is caught by the right button, then its StartRotation() method is called, which is doing only one thing: calculates the compensation between the mouse position and the circle’s starting angle.

public void StartRotation (Point ptMouse)
{
    double angleMouse = -Math .Atan2 (ptMouse.Y - Center.Y, ptMouse.X - Center.X);
    compensation = Auxi_Common .LimitedRadian (angleMouse - angle);
}

A small addition in the MoveNode() method is the last touch to involve such circle into rotation.

public override bool MoveNode (int i, …, Point ptM, MouseButtons catcher)
{
    … …
    else if (catcher == MouseButtons .Right) {
        double angleMouse = -Math .Atan2 (ptM .Y - Center .Y, ptM .X - Center .X);
        angle = angleMouse - compensation;  
        bRet = true;
    }

Dorothy’s House

Text Box:  
Fig.16  Form_DorothyHouse.cs

The first sample in the chapter Cover samples was of an apartment house (figure 7). That house can be moved and resized; now let’s take the same house and add a rotation to its possible movements. The house, turning around some center, looks like a flying house, and I think that the most famous of all the flying buildings was the Dorothy’s house, so I called this class DorothyHouse. As you can see on figure 16 (menu position Nodes and covers – Dorothy’s house), the house with Dorothy’s name has exactly the same cover, as the ApartmentHouse class, only the shape of a cursor above some of the nodes is changed. I can’t remember seeing in Kansas a single house with the ridge moved anywhere from the center, so in our sample the ridge can be moved only up or down. The real Dorothy’s house was a tiny one, but for the purpose of experimenting with the code, I’ll allow the representatives of the DorothyHouse class to grow up to two storey building. Though the covers for two classes are identical, there is a small difference in the process of their resizing, which has an effect on the MoveNode() methods:

  • when the side of an ApartmentHouse is moved, only this side of the house changes its position;
  • when the side of a DorothyHouse is moved, the house is changed symmetrically on both sides.

There is also a small difference in description of such objects: the main part of an ApartmentHouse is defined by a rectangle; the main part of a DorothyHouse is described by four points of its corners.

Rotation of the DorothyHouse object is similar to rotation of a colored circle. There is an additional parameter – an angle between the floor of the house and the horizontal line. In my sample, the middle point of the ceiling is used as a rotation center, but any other point can be selected for this purpose. Rotation can be started by pressing the right button at any inner point of the house; at this moment the DorothyHouse.StartRotation() method is called.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, mea .Button))
    {
        if (mea .Button == MouseButtons .Right)
        {
            if (mover .CaughtSource is DorothyHouse)
            {
                (mover.CaughtSource as DorothyHouse) .StartRotation (mea.Location);
            }

This StartRotation() method calculates the compensation angle between the floor of the house and the line from the pressed point to the center of rotation. This compensation is fixed for the whole time of rotation (between the MouseDown and MouseUp events).

public void StartRotation (Point ptMouse)
{
    double angleMouse = -Math .Atan2 (ptMouse .Y – ptAnchor .Y,
                                      ptMouse .X – ptAnchor .X);
    compensation = Auxi_Common .LimitedRadian (angleMouse - angle);
}

What is interesting in the case of the DorothyHouse class that its DefineCover() method is called from inside the MoveNode() method not only for rotation, but for the forward movement also. This is the easiest way to work with this class, because of that small addition in design (the mirror movement of the both sides on resizing) the movement of any node causes the relocation of nearly all other nodes; in this case it’s much easier to redefine the cover.

The DorothyHouse class demonstrates one important thing: there is nothing special in the design of covers for the objects, involved in rotation. The same objects can be involved in rotation or not; this is defined by mentioning this class in the OnMouseDown() method, but not by anything special in design of the class. Certainly, if the objects of the class are going to be rotated, then some sort of the StartRotation() method must be added.

Complicated Moveable Objects

All the samples in the previous chapters were designed as simple as possible for the purpose of pure explanation of one or another feature. Further on I’ll demonstrate much more complicated samples, like financial graphics or scientific plotting. In this chapter, let’s look at the samples, which are still easy to understand, but which can demonstrate some of the new features and useful classes that are widely used in more complicated objects and applications.

Boards and Balls

Text Box:  
Fig.17  Form_BoardsAndBalls.cs

Figure 17 demonstrates the Form_BoardsAndBalls.cs (menu position Nodes and covers – Boards and balls) with two boards and one group. Though this group looks like an ordinary GroupBox, it is an object of the LinkedRectangles class, so it can be easily moved around by any inner point or by the frame. The number of boards in the form can vary: the form opens with a single board, but any number of boards can be added with the button Add. Any new board can have between two and seven balls; these balls can have the same size or different sizes; the number of balls on board and variations in their sizes are defined by two controls inside the New board group.

The boards can be moved by any inner point and resized by the borders. The balls and the text of each board can be moved only inside their “parent” board.

All the objects can be moved; balls and the boards’ titles can be moved individually, but moving of any board enforces the inner elements to move with it; the same thing happens on resizing the boards.

With all these individual and synchronous movements, you maybe surprised to find that the methods for those three mouse events still consist of one line each! If you check the code of the OnMouseDown() method, you’ll see there more than one line, but only because there are three different cases in this form. The selection of cases is done via the small ComboBox (also moveable, as all other elements in this form!). In each case only one line of code is used inside the OnMouseDown() method. Depending on the selection, the moving / resizing process is going differently.

Case A. This is the standard case, when the really pressed button is passed as a parameter and everything works according with the design: forward movement of any object and resizing of the boards can be started only by the left button; texts can be rotated with the right button.

private void OnMouseDown (object sender, MouseEventArgs e)
{
    mover .Catch (e .Location, e .Button); 
}

Case B. Forward movement and resizing are started by the left button; rotation is started by the right button; during the rotation the text’s angle is shown in a small label next to the text. In more complicated forms this third parameter can be changed via the context menu, so users can decide at any moment about showing or not this additional label with an angle

private void OnMouseDown (object sender, MouseEventArgs e)
{
    mover .Catch (e .Location, e .Button, true); 
}

Case C. In this case moving and resizing can be done by any button, but no rotation for the texts. As the button is not passed as a parameter, then the MouseButtons.Left is used as its default value, so the forward movement and resizing are started regardless of the really pressed button. The TextMR objects can be rotated (according with their design) only by the right button, so there will be no rotation.

private void OnMouseDown (object sender, MouseEventArgs e)
{
    mover .Catch (e .Location); 
}

In reality, the boards’ titles are not the pure TextMR objects, but belong to the BoardTitle class, which is derived from the TextMR.

    public class BoardTitle : TextMR
    {
        Rectangle rcLimit;
        … …

Further on you’ll see the use of different comments, and all of them are derived from the TextMR class. I already explained in the chapter Rotation that TextMR objects can be moved and even rotated without adding a single line to your code. These features are automatically transferred to any derived class, so for any such class you get it automatically and can think about other features. All those classes, derived from TextMR, differ in their relations with some “parent” objects and how they can move relatively to those linked objects, and that’s what you have to write for each of them. In the case of the BoardTitle class, the limitations come from the board’s boundaries, because the title must not move out of the “parent” board, so the BoardTitle.Move() method includes some personal checking for each board.

public override void Move (int dx, int dy)
{
    if (rcLimit .Left <= Location .X + dx && Location .X + dx <= rcLimit .Right)
    {
        base .Move (dx, 0);
    }
    if (rcLimit .Top <= Location .Y + dy && Location .Y + dy <= rcLimit .Bottom)
    {
        base .Move (0, dy);
    }
}

Ball is the class of very simple moveable objects; their cover even uses one of the standard Cover constructors.

I can say that the board is also a simple object, because it also uses one of the standard Cover constructors. At the same time a board is a very interesting object, because it includes other moveable objects, and such combination hadn’t been used in any of the previous samples. These moveable inner objects can be involved in individual movements, but have to move synchronously with the board, when it is moved or resized.

    public class BoardWithBalls : GraphicalObject
    {
        Rectangle rc;
        List<Ball> balls = new List<Ball> ();
        BoardTitle text;
        RectRange range = new RectRange (100, 500, 100, 500);

Synchronization of movements is organized in such a way. The “parent’s” movement must also start the movement of all the children; this makes up the biggest part of the BoardWithBalls.Move() method, as the movement of the board itself is described in a single line.

    public override void Move (int dx, int dy)
    {
        rc .Location += new Size (dx, dy);
        text .Move (dx, dy);
        foreach (Ball ball in balls)
        {
            ball .Move (dx, dy);
        }
        SetAreas ();
    }

Method SetAreas() declares the limits of individual movements for all the objects on board; the individual movements of each of them can go only according with these personal restrictions. In this case of board and balls, the correlation of movements is done by setting the area limits; in the cases of PieChart or RingSet (these two cases of financial plotting are shown at figure 28 in the chapter Complicated applications) there are no limits on movements of related parts (comments), but the same mechanism is organized for their positioning.

An interesting and never used anywhere before is the mechanism of registering complicated objects with a Mover. I would say that the BoardWithBalls class is a simple one among the complicated, but even for it the number of “children” is variable. The mentioned class is relatively simple, because this number is set at the moment of construction, but it’s very easy to add the possibilities of adding / deleting the balls, when they are in view, and also of changing their order. All these operations will require the change of elements’ order in the Mover’s queue, so I want to demonstrate here (at least partly) another way of registering such objects with a Mover: they are not using the Mover.Add() or Mover.Insert() methods, but combine all such needed calls into its own IntoMover() method. This guarantees that a complicated object will be registered fully and correctly regardless of constituents.

    public void IntoMover (Mover mover, int iPos)
    {
        if (iPos < 0 || mover .Count < iPos) {
            return;
        }
        mover .Insert (iPos, this);
        for (int i = balls .Count - 1; i >= 0; i--)
        {
            mover .Insert (iPos, balls [i]);
        }
        mover .Insert (iPos, text);
    }

I mentioned several times that Mover checks the elements for possibility of moving / resizing according with their order in the queue, so the elements that appear on top of others must precede them in the queue. According with this rule, we receive the order of elements for the BoardWithBalls class:

  1. Text – an object of the BoardTitle class.
  2. Balls – objects of the Ball class in reverse order.
  3. Board – an object of the BoardWithBalls class.

There must be a clear understanding of differences between the changing of cover for any object and changing the Mover’s queue. Moving / resizing of ANY object affects only the covers, but not the Mover’s queue. Usually the number of nodes is constant and only their sizes change, but even if the number of nodes is changed, it demands the call to the DefineCover() method, but not more. None of such changes demands the rearranging of Mover’s list. Certainly, if any kind of moving / resizing will add / remove any moveable parts of an object, then this will demand the call to RenewMover(), but I have not a single sample of such kind.

But any adding / deleting of moveable elements in view, regardless of whether it is a whole object or only a part of it, requires the refreshing of the Mover’s list. In the simple cases it is done directly via the Mover.Add() or Mover.Insert() methods; in complicated cases the IntoMover() methods are often in use.

The Form_BoardsAndBalls.cs is the first sample, in which the RenewMover() method is used; further on I’ll show that such method is used in all the complicated forms with a lot of different moveable objects. The Mover’s list includes all the moveable / resizable objects of the form; these objects have different covers; some of these covers can be simple, others can be much more complicated. The IntoMover() method guarantees that a complicated object will be registered fully and correctly regardless of its constituents; the RenewMover() method guarantees that all the objects are included into the Mover’s list in correct order.

    public void RenewMover ()
    {
        mover .Clear ();
        for (int i = boards .Count - 1; i >= 0; i--)
        {
            boards [i] .IntoMover (mover, 0);
        }
        mover .Insert (0, lrNew); 
    }

What is not demonstrated in this form, but is often required for complicated cases, is the accurate system of identification for moveable objects. That’s another aspect of the BoardWithBalls class, which allows to call it simple. There is no identification for balls, because the sample doesn’t need it. But, for example, the systems of engineering (scientific) plotting require a very accurate identification of all the graphical areas, scales, and comments; this will be described in the chapter Y(x) plots and more.

Text Box:  
Fig.18

One more interesting object in the Form_BoardsAndBalls.cs is the group, which is not an ordinary GroupBox, but an object of the LinkedRectangles class (figure 18). Several classes to organize the moveable groups are discussed later in the chapter From bricks to blocks; two of these classes are widely used in my applications.

  • LinkedRectangles objects are used in many places of the Test_MoveGraphLibrary application, but a frame in such object is only an option.
  • Group objects always have a frame, but these objects are mostly used with one or another possibility of resizing, which are signaled by the small changes in frame, so such object with an ordinary frame means that it is not resizable, which is a rare thing.

Text Box:  
Fig.19  Form_RegPolyWithComments.cs

Polygon with Comments

In the chapter Cover samples I have described three classes of regular polygons:

  • Nonresizable RegPoly_NR
  • Resizable by apexes RegPoly_RA
  • Resizable by border RegPoly_RB

In the Form_RegPolyWithComments.cs (menu position Nodes and covers – Regular polygon with comments) you can see (figure 19) one more class of regular polygons. The RegPolyWithComments class has exactly the same cover, as the RegPoly_RB class, but the objects of this new class can be also rotated, and they may have an unlimited number of comments of the CommentToCircle class.

    public class RegPolyWithComments : GraphicalObject
    {
        Point ptCenter;
        int nRadius;
        int nApexes;
        List<CommentToCircle> comments = new List<CommentToCircle> ();

CommentToCircle class is derived from the TextMR class, so all these comments can be moved and rotated automatically without any mentioning them in the code.

A RegPolyWithComments object consists of a set of moveable parts; in order to organize their correct movement and resizing regardless of the number of elements in the set, such object is not registered with a Mover manually, but via its IntoMover() method.

    public void IntoMover (Mover mover, int iPos)
    {
        … …
        mover .Insert (iPos, this);
        for (int i = comments .Count - 1; i >= 0; i--)
        {
            mover .Insert (iPos, comments [i]);
        }
    }

On resizing a RegPolyWithComments object, all its comments change their position, but the algorithms for their relocation are different and depend on the relative text’s position to the circle, on which all apexes reside:

  • if a comment is inside the circle, then the ratio between its distance from the center and the circle’s radius is kept constant;
  • if a comment is outside the circle, then its distance from the perimeter of the circle is kept constan.

All the comments are moved automatically and are never mentioned anywhere inside the code of the three mouse events, but to start the resizing or rotation of the RegPolyWithComments object, one of its methods must be called.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, mea .Button) &&
        mover [mover .CaughtObject] .Source is RegPolyWithComments)
    {
        RegPolyWithComments poly =
                      mover [mover .CaughtObject] .Source as RegPolyWithComments;
        if (mea .Button == MouseButtons .Left)
        {
            poly .StartScaling (mea .Location);
        }
        else if (mea .Button == MouseButtons .Right)
        {
            poly .StartRotation (mea .Location);
        }
    }
}

The resizing or moving of the “parent” polygon is signaled to all the “children” via the InformRelatedElements() method, which calls the CommentToCircle.NewParentSizes() for each of the existing comments.

    private void InformRelatedElements ()
    {
        foreach (CommentToCircle comment in comments)
        {
            comment .NewParentSizes (ptCenter, nRadius);
        }
    }

Text Box:  
Fig.20  A Group to add new comments

Such mechanism of synchronizing the movements of the related parts works for all types of comments, only each comment class has its own method of adjusting children’s position to the parent’s resizing. As you’ll see further on, the PieChart class uses two different types of comments with two different systems of positioning.

Adding the new comment in the Form_RegPolyWithComments.cs is organized with the help of a Group object (figure 20). On figure 18 you can see a non-resizable group; here is a fully resizable group; the possibilities of such resizing are signaled by the changes in frame, but are also signaled by the change of a cursor, when the mouse is moved across the frame.

Spotted Text

Text Box:  
Fig.21	Arbitrary positioning of the SpottedText objects

MoveGraphLibrary.dll includes several complicated classes, which use the scales with numbers and texts. Scales with the numbers, positioned along the main line, are used, for example, in the MSPlot class, which is the basic class for all the scientific and engineering applications; the use of this class is demonstrated further on in the chapter Y(x) plots and more. The scale with the textual information is used in the BarChart class. Numbers and texts along the scales must be (and can be) lined in different ways, which is organized via the tuning forms of these scales. Each of those tuning forms includes a sample of the text with some additional spots. While the sample is moved or rotated, all the numbers (or texts) of the associated scale are moved or rotated synchronously with this sample. Let’s design the class for such spotted text and see, how it can work. The code for the SpottedText class and the demonstration of its work are in the Form_SpottedText.cs (menu position Nodes and covers – Spotted text).

There are 13 moveable texts in the Form_SpottedText.cs (figure 21). Though all these objects belong to the same SpottedText class, only one of them is always shown as a really spotted text; the spots on other objects (months) can be switched On / Off with the help of a special check box. This form demonstrates a very interesting situation, when the objects of the same class behave in different ways and even can be moved in different ways.

The special sample is always shown with the spots; this view is much better for understanding of the cover, used in this class. The cover consists of the nine circular nodes and a single polygon node. The circular nodes are positioned under the colored spots; the polygon node covers the whole area of the text, but it is the last node in the queue, so it has the lowest priority.

    public override void DefineCover ()
    {
        CoverNode [] ca = new CoverNode [10];
        for (int i = 0; i < 9; i++)
        {
            ca [i] = new CoverNode (i, pts [i], nRadius);
        }
        ca [9] = new CoverNode (9, Frame);
        cover = new Cover (ca);
    }

These objects can be moved and rotated; any colored spot can be declared as the center of rotation. No matter whether the spots are shown or not; these objects are so simple that the spots can be found even without visualization, and if you press any spot with the left button, then this spot becomes the center of rotation. Any month object can be moved forward by pressing it at any inner point, regardless of whether it is one of the colored spots or any other point; rotation can be started at any point except the rotation center.

public override bool MoveNode (int i, …, MouseButtons catcher)
{
    bool bRet = false;
    if (catcher == MouseButtons .Left)
    {
        if (i < pts .Length)
        {
            basis = (TextBasis) i;
        }
        Move (dx, dy);
        bRet = true;
    }
    else if (catcher == MouseButtons .Right && i != (int) basis)
    {
        double angleMouse = -Math .Atan2 (ptMouse .Y - AnchorPoint .Y,
                                          ptMouse .X - AnchorPoint .X);
        Angle = angleMouse - compensation;
        bRet = true;
    }
    return (bRet);
}

The rotation is organized exactly in the way as it was described in chapter Rotation for the TextMR class, only this is a sample of the object with the changeable rotation center, so before the calculation of the compensation angle in the StartRotation() method, the current center of rotation must be defined.

public void StartRotation (Point ptMouse)
{
    Point ptAnchor = pts [(int) basis];
    double angleMouse = -Math .Atan2 (ptMouse .Y - ptAnchor .Y,
                                      ptMouse .X - ptAnchor .X);
    compensation = Auxi_Common .LimitedRadian (angleMouse - angle);
}

All these 13 texts in the form belong to the SpottedText class and are supposed to work in the same way, but if you try to move them, you’ll find something interesting: the names of the months can be moved by any inner point, but the sample can be moved only by pressing one of the colored spots. To organize such a strange difference in behaviour, I added several lines of code into the OnMouseDown() method. First, on catching any SpottedText object I check (via the unique identification number), which of the texts was caught. If it is the sample, then the reaction depends on the node, by which it was caught:

  • If it is one of the circular nodes (colored spots), then all the months are lined at their initial places and their lining is defined by the pressed spot of the sample (figure 22).
  • If the pressed point is not in any of the colored spots, then not only the months ignore any command and continue to stay freely wherever they want, but the sample itself is released from Mover and is not going to move anywhere. Throughout the application, it’s the only example of using mover.Release() method anywhere outside the OnMouseUp(), but I have never said that it can be used only inside the MouseUp event. You can decide yourself, which events to use for catching and releasing objects. I prefer to use the MouseDown and MouseUp events, but there is no strict law about it.
void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, mea .Button)) {
        if (mea .Button == MouseButtons .Left) {
            if (mover .CaughtSource is SpottedText  &&  
               (mover [mover.CaughtObject].Source as SpottedText).ID == sample.ID )
            {
                int iNode = mover .CaughtNode;
                if (iNode < Enum .GetNames (typeof (TextBasis)) .Length) {
                    sample .TextBasis = (TextBasis) iNode;
                    for (int i = 0; i < month .Length; i++) {
                        month [i] .Relocate (ptAnchor [i], sample .TextBasis,
                                             sample .Angle_Degree);
                    }
                    Invalidate ();
                }
                else {
                    mover .Release ();
                }
Text Box:  
Fig.22	SpottedText objects with the ordered angle

You can see at the figure 21 that months can have arbitrary angles, because each one of those objects can be rotated individually. But when the sample is rotated, then all the months have exactly the same angle and rotate synchronously. This mechanism is used in all the tuning forms for numerical and textual scales of the MoveGraphLibrary.dll.

void OnMouseMove (object sender, MouseEventArgs e)
{
    if (mover .Move (e .Location))
    {
        if (mover .CaughtSource is SpottedText &&
            (mover [mover .CaughtObject] .Source as
                            SpottedText) .ID == sample .ID 
             &&  e .Button == MouseButtons.Right)
        {
            for (int i = 0; i < month .Length; i++)
            {
                month [i] .Angle = sample .Angle;
            }
        }
        Invalidate ();
    }
}

Visualization of Covers

It’s really an interesting and funny title for a chapter, because I think that covers must not be visualized at all and the best design must avoid any visualization of covers. In the Test_MoveGraphLibrary application the covers are visualized only for the purpose of better explanation, but in the real applications the moving and resizing of all the objects must be obvious without any covers in view. This is the situation with all the scientific applications, which I have developed. Yet, in some applications there can be a request for cover visualization, but while reading this chapter, don’t forget that all the questions of visualization are important only when there is such a request. The standard situation is when the covers are not shown.

The Test_MoveGraphLibrary application is a demonstration program, and in a lot of its forms I switched ON the covers’ visualization only to show, how the covers are designed. But regardless of whether the painting of covers is necessary or redundant, did you pay attention, who is responsible for this painting? Even if you didn’t pay attention to this question before, you can look again throughout the code of the previous samples, and I hope that now you’ll be surprised by a very strange thing: none of the moveable / resizable objects is responsible for painting of its own cover. It’s really a strange thing, because the good programming practice (at least, how it is understood today) requires the Draw() method to be included into the list of methods of any visualized class, so that any object of such class can draw itself. Cover belongs to an object, and all the demonstrated classes have their own Draw() methods, but you’ll not find a single DrawCover() method in any of these classes. If the whole process of cover visualization is organized in such an unusual way, then it was definitely done so on purpose.

Cover belongs to an object, but it is needed and used only for the purpose of moving / resizing an object. It is an additional layer of object’s parameters, which never interfere with any other parameters; cover is defined by other parameters (for example, by sizes) and strongly depends on them, but it’s a one way relation: cover never rules other parameters. The main thing was already mentioned: cover is an additional, auxiliary parameter. Though an object’s cover is designed at the moment of the object’s construction, you may look at the situation with its cover in such a way: it is activated, when an object is registered with a Mover. You can look through all the samples and you’ll not find a single object with any kind of indication of either it is registered with any Mover or not. No object knows if it is moveable / resizable at the moment or not!

Any object, derived from the GraphicalObject class, can be used as non-moveable or moveable; this status can be easily changed, while the application is running, by including an object into Mover’s list or excluding an object from this list. Mover is the only one, who knows the list of moveable objects at any moment; because of this, Mover is responsible for drawing all the covers.

There is an additional interesting thing about this covers’ drawing. At any moment Mover knows everything about the covers to be drawn, but not a bit more; Mover doesn’t know anything beyond the covers and doesn’t know anything about the real objects, which are behind those covers. This situation was demonstrated perfectly at the very beginning, when in the chapter Use of standard covers I wrote about the infinitive loop (figures 2 – 4). There are several pictures, based on the same cover, and I can easily add several more pictures to them. Mover has no idea about the real objects; this powerful ruler deals with the covers and only with the covers. Nothing else.

The most interesting questions in the area of covers’ visualization are:

  1. How to change the parameters of visualization?
  2. How to visualize only part of the cover?
  3. How to select only some of the covers for viewing?

Four different classes can be involved in cover visualization: Mover, MovableObject, Cover, and CoverNode.

Covers consist of the CoverNode objects, so the visualization of cover is in reality the visualization of all its nodes or only part of them. The shape of any node can be a circle, a strip, or a polygon; any of them has inner area and a border, so the choices of nodes’ drawing are limited:

  • To fill the inner area of the node with some color or not.
  • To draw the nodes’ border or not.

The default colors for this drawing are:

  • White – to fill the node’s area.
  • Red – to draw the node’s border. Color can be changed, but if painted, a border is a solid line of 1 pixel width.

The color to fill the node’s area can be set for each node individually; there is a CoverNode.Color property, but I never found a single chance to use it and if I have to fill the node’s area, then it is always White.

Much more important and widely used is the setting of the flag, which commands to clear (to fill) the node’s area or not. The selection of the flag is usually done inside the DefineCover() method; the setting of the flag can be done with a CoverNode.Clearance property, but in many cases the default value is used. The default value of the clearance flag depends on the node’s shape: for circles and strips it is true, for polygons – false. These default values are based on the predominant use of the nodes of each shape. The circular nodes are mostly used in small size around the points, which are used for resizing or reconfiguring; the strip nodes are mostly used as narrow areas on borders for resizing. Usually the nodes of these two shapes cover a small part of the object’s area and don’t significantly destroy the objects view, even when visualized; because of the small sizes, it’s better to clear the node’s area, so they will be much better seen on visualization. On the contrary, polygon nodes are usually big and cover the significant, or even the whole object’s area. If cleared, they will simply wipe out the whole object, so by default they are not cleared.

If the nodes of any shape have an unusual size, then their clearance is often changed in DefineCover() method. There are different ways to change the clearance:

  • It can be changed individually for any node with the CoverNode.Clearance property.
  • It can be changed for all the nodes with the Cover.Clearance() method.
  • It can be changed for the nodes of the particular shape with another variant of the Cover.Clearance() method.

Of the previously demonstrated forms, you can find the samples of changed clearance in the Form_NodeShapes.cs (figure 1 - the case of a single rectangle), in the Form_CircleInRotation.cs (figure 15 - all the nodes), and in the Form_NNodeCovers.cs (figure 12 - all the circular nodes of the ring).

While the color for filling the node’s area can be set individually for each node, the situation with the color for nodes’ borders is opposite – usually it is set once for all nodes in all the covers in the Mover. It’s a very rare situation that this color is set at all; there was one sample in the previous version of application, where I demonstrated the use of several Movers, and for better illustration I changed this color for one of them. In reality, each MoveableObject may have the individual color for its cover, which can be set by MovableObject.Color property. Though it can be done, I can’t imagine the situation, when the visualization of covers for objects, registered with the Mover, would require personal color for each of them. When any object is registered with the Mover, color for its cover is set to whatever is currently set in the Mover. If you want, the color for its cover can be changed through the already mentioned MovableObject.Color property, or the color for all the covers can be set with the Mover.Color property.

If you need to draw the cover for one object, then there are MovableObject.DrawCover() and MovableObject.DrawCoverReverse() methods. The second method draws the nodes in reverse order, so the first one will be on top of all others – this is exactly the situation, which Mover looks at, while making a decision about the moving / resizing.

If you need to draw all the covers, there is another pair of methods: Mover.DrawCover() and Mover.DrawCoverReverse(). Pay attention: both of them draw the covers in the reverse order! But the second one also draws each cover in the reverse order of nodes, so this is the exact situation with the covers and nodes as Mover sees it and analyses.

This chapter is about the visualization of covers, but there are situations, when you don’t want the cover for one or another class to be visualized. It’s not about hiding your ideas from anyone else; it’s only for convenience. For example, further on I’ll demonstrate the use of engineering (scientific) plotting and samples of financial graphics. I’ll explain the design of covers for them, there are no secrets; all those covers are designed in such a way that completely cover the objects. Those objects can be moved by any inner point and resized by any border point; these are the only things users need to know to work with them, so there is absolutely no sense in visualizing their covers. If for any reason you want to exclude the visualization of cover for any class, add a short ShowCover property to this class.

    public override sealed bool ShowCover
    {
        get { return (false); }
    }

In such a form the ShowCover property is used for all the classes, used for scientific or financial graphics. It is not used for the TextMR class, but that was done only for better explanation; all the comments in the MoveGraphLibrary.dll, derived from the TextMR class, have such property. For example, in the Form_RegPolyWithComments.cs, the comment’s cover is not visible, because the comments there belong to the CommentToCircle class.

Y(x) Plots and More

I wrote at the beginning that my work on design of moveable / resizable graphics was triggered by the huge demand for such graphics in the areas of scientific and engineering applications. The results turned out to be extremely interesting for areas that are absolutely different and far away from the original, but for this particular area the new results are not simply an improvement, they have to become a revolution in design of the most complicated applications.

Text Box:  
Fig.23  Form_UnmovablePlots.cs

The conflict between the widest variety of constantly changing users’ demands and the fixed design of scientific / engineering applications is obvious to both communities of users and designers. In attempts to soften this conflict, programmers are looking for new and new improvements in interface design, and there are huge achievements in this area. But all these achievements are accomplished within the misguided idea that the programmer can design something that would be excellent for each and all users. You can (and certainly must) give to the users what you think is the best, but if you give them the instrument to easily construct their own dream environment – this will be the most effective application for their work. This is the main idea of applying the moveable / resizable graphics to the complicated applications; the area of scientific and engineering programs maybe one of the best to show the real difference in results. In this chapter I want to show in detail the transformation from the ordinary fixed plotting to something different, in which all the parts are moveable / resizable, and where the discussion of “where and what to show” simply doesn’t exist any more.

The samples for this chapter are taken from the different forms, available through several menu positions under Y(x) plots. All these forms use the MSPlot class, which was designed for the scientific (engineering) plotting. Any MSPlot object has a single rectangular plotting area and an unlimited number of scales, though for majority of the samples each plotting area will use only one horizontal and one vertical scale. Any scale or plotting area can be linked with an unlimited number of comments. The MSPlot class is included into MoveGraphLibrary.dll; you can find the detailed description of this class together with the explanation of its tuning forms in the file MoveGraphLibrary_Graphics.doc. Now let’s look at the different steps of turning unmoveable standard plots into fully moveable and resizable.

The first form in this group is Form_UnmovablePlots.cs (menu position Y(x) plots – Unmovable plots) with two standard unmoveable plots (figure 23). Interestingly, there is a working Mover in this form: the information, painted in Green, is moveable. If you want to eliminate all movements in order to receive the ordinary form with all fixed elements, you can do it easily: comment whatever is dealing with Mover.

The codes of all three needed mouse events are as usual short; only the OnMouseMove() method has several extra lines, which you’ll not find in any other form.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    mover .Catch (mea .Location);
}
private void OnMouseUp (object sender, MouseEventArgs e)
{
    mover .Release ();
}
private void OnMouseMove (object sender, MouseEventArgs mea)
{
    if (area_0 .InsideSensitive (mea .Location) ||
        area_1 .InsideSensitive (mea .Location)) {
        Cursor .Current = Cursors .Hand;
    }
    if (mover .Move (mea .Location)) {
        Invalidate ();
    }
}

Those several lines of code inside the OnMouseMove() method had to be added because the areas are unmoveable! If you’ll check the code for all other forms that I m going to mention in this chapter, you’ll not find such lines any more, but the mouse cursor over moveable MSPlot objects is always changed. This is one of the things that Mover is taking care of, but only for the objects that are included into its List<>. In Form_UnmovablePlots.cs the plotting areas are unmoveable, Mover doesn’t know anything about them, that’s why I have to add those lines of code to inform users that the double click inside these areas still works.

One Moveable Plot

Text Box:  
Fig.24  Form_OneMovablePlot.cs

Those two graphical areas from the Form_UnmovablePlots.cs (figure 23) can be easily turned into moveable / resizable, as all the objects of the MSPlot class have these features. The only needed thing to turn them into moveable objects is to register them with the Mover; this is demonstrated in Form_OneMovablePlot.cs (figure 24). There is only one plotting area in this form; this area is moveable and resizable. MSPlot objects have the type of cover that was shown at figure 5 for rectangles with the Resizing.Any type, so:

  • Moving of this plotting area is started by pressing the left button at any inner point.
  • Resizing of the plotting area is started by pressing any border point.

There is a small addition to the last statement. The scales are often placed on the borders of the plotting area or very close to them, as shown at figure 24. The scales have higher priority, than the area itself, so if you press on the border, covered with the scale, then this scale is moved and not the whole picture. To get a chance of resizing the area even in the direction of the side, covered with a scale, the cover was slightly changed, so the pressing in the corners will still start the resizing of the area.

What is interesting about this sample that to register this MSPlot object with the Mover, I am not using something expected like

mover .Add (area);

Instead there is a small method RenewMover(), in which registering of an MSPlot object is done by the special method of this class MSPlot.IntoMover(…)

private void RenewMover ()
{
    mover .Clear ();
    area .IntoMover (Movers, 0);
    mover .Insert (0, panelComments);
}

In all other samples of this chapter, I use exactly the same technique; these changes in registering of an MSPlot object and in using an additional RenewMover() method are related to the design ideas of the MSPlot objects, so let’s look at this important thing, because it can be useful for other complicated objects, consisting of several moveable parts.

An object of the MSPlot class has only one plotting area, but may have any number of horizontal and vertical scales (class Scale); each of the scales can be moved individually, but all of them move together with the area, when it is moved or resized. In addition, any number of comments can be related to the main area or any of the scales. These comments (class CommentToRect) can be moved and rotated individually, but they also move synchronously with their “parents”, either it is a scale, or a main plotting area. Each individually moveable object must be registered with a Mover, so a simple mover.Add(…) or mover.Insert(…) is not enough for MSPlot object; for each object of this class all its scales and comments must be registered with a Mover. The order of their registration is so significant, as the objects are chosen for the movement according with their order in the Mover’s list, that there has to be an absolutely reliable method of correct registration for any combination of the parts.

The comments are shown atop of their “parent”, so it is expected by any user that if the comment is pressed with a mouse, then this comment must be moved and not a plotting area or a scale below; thus comments must always precede their “parent” in the Mover’s list. The same thing happens with the scales: they can be positioned atop their “parent” plotting area, so the scales must precede it in the same list. In order always to have the correct Mover’s list, it’s much better not to populate this list manually with all the needed elements, but let an MSPlot object do it. Slightly shortened, the IntoMover() method of the MSPlot class looks like this:

    public void IntoMover (Mover mover, int iPos)
    {
        mover .Insert (iPos, this);
        foreach (CommentToRect comment in Comments) {
            if (comment .Visible) {
                mover .Insert (iPos, comment);
            }
        }
        foreach (Scale scale in HorScales) {
            if (scale .Visible) {
                mover .Insert (iPos, scale);
                foreach (CommentToRect comment in scale .Comments) {
                    if (comment .Visible) {
                        mover .Insert (iPos, comment);
                    }
                }
            }
        }
        foreach (Scale scale in VerScales) {
            if (scale .Visible) {
                mover .Insert (iPos, scale);
                foreach (CommentToRect comment in scale .Comments) {
                    if (comment .Visible) {
                        mover .Insert (iPos, comment);
                    }
                }
            }
        }
    }

The use of area.IntoMover() method guarantees that all the moveable parts of this MSPlot object will be placed into the Mover’s list in correct order. The RenewMover() method plays the same role for the whole form: it guarantees that all the moveable / resizable objects of the form will be placed into Mover’s list in correct order. There are so many places in the form and different occasions on which the content of this list is changed; it would be impossible to check the list manually in all these places. Even in case of a single MSPlot object with the minimum number of used scales, there are three different tuning forms, in which comments can be added, deleted, and modified. With the increasing number of plotting areas in the form, there are much more places, where the content of the Mover’s list can be changed. To keep the Mover’s list in correct order at any moment, all these tuning forms simply receive an opportunity to call this RenewMover() method of the parent’s form through a delegate.

    area .ParametersDialog (this, RenewMover, ParametersChanged);

In the Form_OneMovablePlot.cs you can organize any number of comments; all of them can be moved and rotated. If you want to see the angle of the text during the rotation, you have to add the third parameter into the call of the Mover.Catch() method.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    mover .Catch (mea .Location, mea .Button, true);
}

The plotting area in the Form_OneMovablePlot.cs became moveable and resizable. Any comments can be added into view through the tuning forms of the main plotting area and scales, but there are still some things, which can be done, but not in an easiest way. All these annoying things must disappear in the next more powerful Form_ManyPlots.cs.

Many Moveable Plots

Text Box:  
Fig.25  Form_ManyPlots.cs

We turned ordinary plots into moveable and resizable; now we can move to Form_ManyPlots.cs (menu position Y(x) plots – Many plots), which demonstrates the real power of moveable / resizable graphics for the area of scientific and engineering applications. This is a case, when users get a chance to decide what, when, and how to show.

For the applications, based on the standard (fixed) graphics, it is fairly easy to demonstrate a picture and say that “this form will look like shown on the picture”; for the Form_ManyPlots.cs it is absolutely impossible. Here you can organize any number of different plots; all of them are moveable and resizable, so I can’t predict how this form will look, when you start playing with the functions; I can only say that “you can organize something similar to this” (figure 25). Here I am not writing the scenario of users’ work with one or another function; but as a designer I have to provide the instrument, with which users can organize and analyze different functions. In this application there is a small limitation: users can select the functions only from a predefined set. In the main form of the TuneableGraphics application, the same instrument is realized without such limitation; there users can explore any number of arbitrary functions.

That is the main idea of applying the moveable and resizable graphics to the area of scientific and engineering applications: the new technique gives a chance to turn the powerful calculators (our day programs) into real research instruments. In such type of applications USER defines the position, size, and content of each plotting area, so there must be implemented some very reliable link between the graphical area and its content. In the Form_ManyPlots.cs this is done with the help of the PlotOnScreen class:

    public class PlotOnScreen
    {
        DemoFuncType funcType;
        MSPlot plot;

Graphical areas, which must be shown on the screen, are organized into the List<>:

    List<PlotOnScreen> plotInView = new List<PlotOnScreen> ();

The plot, which is shown on top of all others, is the first element in this List<>. When users add, delete or reorder plots, all these changes are reflected in the List<>, and the objects are registered with the Mover by the RenewMover() method.

private void RenewMover ()
{
    mover .Clear ();
    for (int iPlot = plotInView .Count - 1; iPlot >= 0; iPlot--)
    {
        PlotOnScreen pos = plotInView [iPlot];
        pos .Plot .IntoMover (mover, 0);
    }
    mover .Insert (0, panelComments);
    mover .Insert (0, panelWithList, Resizing .Any, 225, 400, 106, 320);
}

It doesn’t matter, how many different plots there are in the form, how many scales each of them has, and how many comments are related to all these scales and plotting areas. What has to be done in the OnMouseDown() method is the same:

  • If any moveable element is caught with the left button – move it.
  • If any comment is caught by the right button – start rotation, and if there is a requirement to show the angle of this comment during the rotation – organize a small label to show this angle.

These two items can be implemented in several lines of code.

void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, mea .Button))
    {
        if (mea .Button == MouseButtons .Right  &&  mover .CaughtSource is TextMR)
        {
            (mover .CaughtSource as TextMR) .AngleLabel (bShowAngle);
        }
    }

But the same lines of code would have to be repeated again and again for all the complicated forms, working with the engineering (scientific) or financial graphics, so these actions can be implemented by a single call to the Mover.Catch() method with three parameters.

void OnMouseDown (object sender, MouseEventArgs mea)
{
    …
    mover .Catch (mea .Location, mea .Button, bShowAngle);
    …
}

Though the Form_ManyPlots.cs is much more complicated then all the previously demonstrated forms, its OnMouseDown() method is very simple and short. But in any complicated form a lot of different things are organized, like reordering the objects in view, or calling different context menus, depending on the click point. Both things are started from inside the OnMouseUp() method, so, though still simple, it is noticeably bigger, than before. While writing about the Form_BoardsAndBalls.cs (figure 17), I mentioned that the complicated forms with a lot of different moveable objects need a very accurate and absolutely reliable system of objects’ identification. MSPlot is the first, but not the last class, mentioned in this text, which has such a system for identification of all its moveable parts. This identification is needed for two purposes.

In the form with a lot of different objects, the context menu, to be opened, depends on the type (class) of object under the mouse. Though the selection of context menu often depends on the class of the object, the status of some lines in this menu can depend on the parameters of the particular object that was clicked, so the identification only up till the class is not enough. As the right button is used both for rotation of objects and for calling a context menu, I distinguish these two things by the distance between the two points, where the mouse was pressed and released.

void OnMouseUp (object sender, MouseEventArgs mea)
{
    … …
    double nDist = Auxi_Geometry .Distance (ptMouse_Down, ptMouse_Up);
    … …
    else if (mea .Button == MouseButtons .Right)
    {
        if (mover .Release ()) {
            if (nDist <= 3) {                       // ContextMenu if nDist <= 3
                iInMover = mover .WasCaughtObject;
                Identification (iInMover);           // Identification
                if (iPlotTouched >= 0) {
                    // selection of context menu, depending on the class

Identification is also used to determine the exact object, which must be moved or rotated. Even the pure identification of the object itself is not enough, when there are several levels of related moveable parts in an object; the identification of the related parts up the tree must be done also. This depends on the clicked object, but for an MSPlot object there can be up to three objects in this chain: comment (CommentToRect object) – scale (Scale object) – plotting area (MSPlot object).

You can look at the system of moveable objects on the screen as if each of them occupies its own level. With the left button, they can be either moved around on their own levels, or they can be ordered to popup and appear on top of all other objects. These two things are also distinguished by the distance between the points of two mouse events.

void OnMouseUp (object sender, MouseEventArgs mea)
{
    … …
    double nDist = Auxi_Geometry .Distance (ptMouse_Down, ptMouse_Up);
    if (mea .Button == MouseButtons .Left) {
        if (mover .Release ()) {
            iInMover = mover .WasCaughtObject;
            if (mover [iInMover] .Source is MSPlot) {
                Identification (iInMover);                   // Identification
                if (nDist <= 3  &&  iPlotTouched > 0) {
                    PopupPlot (iPlotTouched);

There is no difference in using the Identification() method either for a single or a double mouse click, so Mover helps to decide about the opening of one or another tuning form.

void OnMouseDoubleClick (object sender, MouseEventArgs mea)
{
    if (mover .Catch (mea .Location, MouseButtons .Left))
    {
        int iInMover = mover .CaughtObject;
        Identification (iInMover);            // Identification
        if (iPlotTouched >= 0)
        {
            MSPlot plot = plotInView [iPlotTouched] .Plot;
            …
            if (mover [iInMover] .Source is MSPlot)
            {
                plot .ParametersDialog (this, RenewMover, ParametersChanged, …);
            }
            else if (mover [iInMover] .Source is Scale)
            {

In addition to any number of plotting areas, there are two more moveable elements in the Form_ManyPlots.cs: an information area and a ListView with some parameters of the plotting areas. For nearly two years the information area was organized in the form of a Panel; not long ago it was changed to the object of the TwoColumnInfo class. It is a nonresizable object, but now it can be moved around by any inner point. The controls can be move only by their border points; the new solution is much better for the users and is organized according with the rules of the user-driven applications. The ListView needs to be resizable, as the number of plotting areas in view 9and the number of lines in the ListView) is unpredictable. This ListView is positioned on the panel and changes its sizes, when the panel is resized. The resizing of controls is explained further on in the chapter And controls also.

Text Box:  
Fig.26   Form_Profiles.cs

While I was describing the basic level of moveable graphics – nodes and covers – I tried to explain all the possible variants and always included the covers’ visualization. Now we are looking at much more complicated cases of engineering (scientific) plotting; further on I’ll show the cases of complicated financial graphics, and nowhere in these forms (with, maybe, one exception) you’ll see covers. All these classes (Scale, TextScale, MSPlot, BarChart, PieChart, and so on) are designed in such a way that their covers close the entire area of the objects, so there is no sense in any visualization of such covers. Simply know that all objects can be moved by any inner point and resized by any border point.

Profile

In this chapter I want to show one more sample, related to the MSPlot class. The menu position Y(x) plots – Profile demonstrates the work of the Profile class. This class perfectly fits the idea of covers presentation; however, it is a unique class within the MoveGraphLibrary.dll: it is the only demonstrated class of resizable, but not moveable objects. A Profile object is not moveable by itself (and this was done on purpose), but it can be moved along with another moveable / resizable object, on which it can reside. The host object can be different; in Form_Profiles.cs (figure 26) two cases of using Profile are demonstrated: in the left-top corner you see the Profile object inside the ordinary Rectangle; two other Profile objects reside on the MSPlot object, which is the subject of all the standard moving / resizing transformations.

A Profile object is a set of unconnected nodes; the lines that you see on this picture can be easily taken out by using Pens.Transparent, and then you have a set of isolated nodes – exactly what the Profile in reality is. I do not demonstrate this elimination of lines in the Test_MoveGraphLibrary application, because I try to keep it simple and show only the code, dealing with moveable / resizable features. In the TuneableGraphics application there is one form with a sample of Profile; try menu position Single samples – Profile. There the right mouse click on any Profile’s node opens the small context menu and gives you a chance to switch the auxiliary lines between the nodes ON / OFF.

Two Profile’s end nodes are always located on the left and right borders of the rectangle and can be moved only up or down; all intermediate nodes can be moved freely between upper and lower boundaries, but can not move farther to the sides, than the neighboring nodes. Each node is characterized by its location (Point) and two double values from the ranges, defined by the borders’ values. Initialization of a Profile object can be done either by two arrays of double values or by an array of points (Point []).

double [] xs = new double [9] { … };
double [] ys = new double [9] { … };
profile [1] = new Profile (rc, area.ValuesOnBorders, xs, ys, NodeForm .Circle, 3);
pts = new Point [] { … };
profile[2] = new Profile(rc, area.ValuesOnBorders, pts, NodeForm.Polygon, 3);

As in all other cases, the moving of the nodes is organized with the MouseDown, MouseMove and MouseUp events in the same simple way. However, there are several additional lines of code in the OnMouseDown() method, as the Profile object is nearly unique from one more aspect: for the majority of the moveable / resizable objects the cover is organized at the moment of initialization and later it is only transformed, but with the Profile object the user can add intermediate nodes at any time or delete them. Each time the cover must be redesigned, which is done automatically inside a couple of methods that add or delete nodes.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (mea .Button == MouseButtons .Left)
    {
        for (int i = 0; i < profile .Length; i++) {
            int iDot = profile [i] .InsideDot (mea .Location);
            if (iDot < 0) {
                // not inside any Dot, check if it is close enough to the line
                iDot = profile [i] .InsertNewDot (mea .Location);
            }
            if (iDot >= 0) {
                profile [i] .Selected = iDot;
                break;
            }
        }
        mover .Catch (mea .Location);
    }

If the left mouse button is pressed not inside any node, then there is an additional check to see if this point is close enough to any segment between the consecutive nodes to insert the new node there. If Yes, the new node is added ( InsertNewDot() ) and it is just on the move, as the mouse button is already pressed.

There are also several additional lines of code inside the OnMouseMove() method. If the Profile object resides on an MSPlot object, then the profile’s transformation depends on whether the host area is moved or resized. If the host MSPlot object is moved, then the Profile is moved exactly in the same way:

private void OnMouseMove (object sender, MouseEventArgs mea)
{
    … 
    if (area .MainArea .Area .Width == profile [1] .Area .Width &&
        area .MainArea .Area .Height == profile [1] .Area .Height)
    {
        // area is simply moved, so profiles must be moved also
        int dx = area .MainArea .Area .Left - profile [1] .Area .Left;
        int dy = area .MainArea .Area .Top - profile [1] .Area .Top;
        profile [1] .Move (dx, dy);
        profile [2] .Move (dx, dy);
    }

If the host MSPlot object is resized, then the profile’s area is changed to the new area of the MSPlot object.

The Profile is an example of resizable, but not moveable objects; it also demonstrates the interaction between moveable objects of the different classes.

    else {
        // area is changed, so dots in profile must be recalculated
        profile [1] .Area = area .MainArea .Area;
        profile [2] .Area = area .MainArea .Area;
    }

Complicated Applications

During the last three years I was designing new and new classes for moveable/resizable engineering plotting. I started with the simple case (at least, now it looks very simple), in which the main plotting area could be moved and resized with the help of several tiny nodes, and finally came to the MSPlot class.

Object of the MSPlot class has the main plotting area and can have any number of horizontal and vertical scales. An MSPlot object can be moved by any inner point of the plotting area; the size of the plotting area can be changed by any border point. To achieve such results, MSPlot class has the type of cover that was shown at figure 5 for rectangles with the Resizing.Any type. The scales can be easily moved around with a mouse and can be positioned anywhere, even atop the main plotting area. Scales are often positioned on the border of the plotting area, but scales have higher priority for grabbing, so in such situation this side of area will be “closed” for resizing. Certainly, the scale can be moved slightly aside, the area’s size changed, and then the scale returned back on the border, but this looks a bit awkward. Instead, the scales’ cover is designed with special “windows”, through which the main area cover can “look out”; even if the scale is “closing” the border, the plotting area can be resized by grabbing its corners. The main plotting area and each of the scales can have any number of labels (comments). These comments can be moved around the screen and rotated individually; they also move together with their “parent” scale or main area.

So now we have such a system of elements, populating the form’s area:

  • Plotting Areas Are moved by any inner point and resized by any point on the border.
  • Scales Are moved by any inner point; resizing is done automatically with the resizing of the “parent” plotting area.
  • Comments Are moved and rotated by any point.
  • Controls Are moved and resized (if needed) by the border.

Text Box:  
Fig.27  Form_GraphsAndComments.cs

All the elements are moved and resized in such a natural way, that I don’t need to show covers any more. And that’s the best way to design the applications: users simply KNOW that all elements are moveable and resizable, and use these features without any additional prompts. Such a sample, which is very close to the real engineering/scientific applications, is demonstrated in the Form_GraphsAndComments.cs (figure 27, menu position Y(x) plots – Graphs and comments).

In real engineering and scientific applications, users need maximum flexibility in making a decision about the content of the plotting area. In this form, the data for plotting is obtained in different ways:

  • Data arrays.
  • Standard methods for calculation of Y(x) functions.
  • Methods for calculating Y(x) functions with an additional set of parameters.
  • Parametric functions.
  • The output from FunctionInterpreter, which takes a function’s text and converts it into special form for further calculations.

In many aspects, this form is similar to previously mentioned Form_ManyPlots.cs (figure 25), only here you can select any combination of functions to be shown in one plotting area; this is much closer to the case of the real applications. All the basic ideas from the design of really complicated scientific / engineering applications are demonstrated in Form_GraphsAndComments.cs.

  • The form is populated with controls and different graphical objects (plotting areas and comments).
  • Users decide about the placement of each and all parts. No limits on the number and size of the plotting areas.
  • No limits on the number of comments; any number of them can be linked with each plotting area or scale.
  • No non-moveable elements. Comments can be also rotated.
  • Easy tuning of all the parameters. No restrictions on dealing with these tuning forms; all of them work independently, but if they work with the linked elements (like plotting area and its comments), then they inform each other about the changes.
  • Saving and restoring of all the visualization parameters. The number of tuneable parameters is huge; if a user spent some time on rearranging the view to whatever he prefers, then loosing of these settings is inadmissible. The easiness for a user of rearranging the whole view requires the system of storing the tuned areas somewhere in memory (in Registry) or in a file for using them later. Such system of saving / restoring is provided.
  • A system of context menus covers a lot of requirements. Different menus are opened, depending on the place of its call; there are menus for plotting areas, for scales, for comments, and for an empty place. Partly these menus duplicate the actions that can be made in the tuning forms, but they are done much faster via the context menus. Other menu positions allow to do the unique things, which cannot be achieved in other ways.

There can be special additional requirements for applications in one or another area, but these are the general things that must be implemented in any engineering / scientific application.

The OnMouseUp() method in the Form_GraphsAndComments.cs looks similar to the same method in Form_ManyPlots.cs. This means that that this is a standard view of the OnMouseUp() method for any complicated form of similar type; the methods will differ only in selection of context menus (one form in the Test_MoveGraphLibrary application has 10 different menus) and in reaction on clicking the empty place.

I have a lot of experience in the design of very complicated scientific and engineering applications and I understand all too well that converting of such applications from designer-driven into really user-driven applications is not a five-minute job. The simple change of graphics, which will turn all plots into moveable / resizable without any other changes in application, can be done really quickly, in minutes. It is the development of programs inside the new paradigm of user-driven applications that will require some thought by the designers. But the users’ benefits from the new type of applications will be enormous.

Text Box:  
Fig.28  Form_Medley.cs

Warning! Be sure that after you give your clients (engineers and scientists) anything based on moveable / resizable graphics, there is no way back for you. After it, your clients will never accept any application, which they can’t redesign in any possible way they want. But also be sure that you can’t hide the possibility of such design from your clients for long.

There are several interesting forms in the Test_MoveGraphLibrary application to demonstrate the simultaneous use of several different complicated objects; one of them is the Form_Medley.cs (figure 28 menu position Financial graphics – Medley).

Any number of objects of “financial” graphics can be used here; at the moment, the main objects can be of the BarChart, PieChart, and RingSet classes; maybe I’ll add other classes in the nearest future.

Any object of these classes can be moved by any inner point and resized by any border point; rings and pie charts can be rotated by any inner point. As the covers close the whole objects’ area, there is no need in covers’ visualization, so there is no such visualization at all. Whatever textual information you see here, it can be moved and rotated by a mouse. All these comments move with their “parents” whenever they are moved, resized or rotated, but different types of comments differ in reaction to their “parent” movements. For example, the PieChart and RingSet objects use two types of comments each. Those of the comments, which are associated with the sectors, react to any change in parent’s position or size. Other comments, associated with an object as a whole, react to parent’s forward movement, but not to its rotation.

At the beginning of design, the idea of turning any object into moveable / resizable was simple enough:

  1. An object gets a cover.
  2. An object is registred with a Mover simply by including it into the Mover’s list.
  3. Mover organizes the moving / resizing of this object according with the designed cover and the rules, declared in the Move() and MoveNode() methods.

When an object consists of different parts that are involved in synchronous movement but can be also moved individually, then a simple adding of such objects to the Mover’s list may lead to some strange results. A lot of things depend on the correct order of all these parts in the Mover’s list; that’s why all the complicated classes, like MSPlot, PieChart, and so on, must have an IntoMover() method to register the objects of such classes in correct way. (I have already written about the MSPlot.IntoMover() method in the chapter Y(x) plots and more.)

Figure 28 shows only three main classes of financial graphics, but there are TEN different classes of individually moveable objects in this form. The use of so many different classes may add something to the code of the OnMouseDown() and OnMouseUp() methods, but also influences the design of applications.

The first significant change is in the redistribution of some needed commands from the tuning forms to context menus. As a result, the tuning forms become simpler and easier in use, but the classes become more powerful and give more opportunities in rearranging the complicated objects.

The use of an unlimited number of objects of different classes makes the problem of the objects’ identification extremely valuable; this is how I solved it.

To each moveable object, the constructor gives a unique identification number, provided by the Auxi_Common.UniqueID() method. This ID is used for objects’ identification in the OnMouseUp() method, when Mover releases any caught object, and there must be a decision which object to popup, or what context menu to show, and how this menu must be adjusted to the touched element. It’s easy to get the order of the released element in the Mover’s list:

iInMover = mover .WasCaughtObject;

and after receiving the ID of this element, look for it through all the elements of the known type

    private void Identification (int iInMover)
    {
        if (mover [iInMover] .Source is PieChart)
        {
            id = (mover [iInMover] .Source as PieChart) .ID;
            for (iElement = elems.Count - 1; iElement >= 0; iElement--)
            {
                if (elems [iElement] .ElementType == MedleyElem.PieChart)
                {
                    if (id == elems [iElement] .PieChart .ID)
                    {
                        break;
                    }
                }
            }
        }
        else if (mover [iInMover] .Source is Scale)
        {
            id = (mover [iInMover] .Source as Scale) .ID;
            …

The same type of the Identification() method is used throughout the Test_MoveGraphLibrary application. The number of lines in the method depends only on the number of different classes, used in each form, but the identification of each class is as simple, as you can see in this part of code for the PieChart class.

Though a lot of elements in the Form_Medley.cs are involved in moving, resizing, and rotation, the OnMouseDown() method in this form is remarkably short.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    ptMouse_Down = mea .Location;
    mover .Catch (mea .Location, mea .Button, bShowAngle);
    ContextMenuStrip = null;
}

You can find exactly the same OnMouseDown() method in the most complicated forms of the application:

  • Form_ManyPlots.cs (figure 25);
  • Form_GraphsAndComments (figure 27);
  • Form_Medley.cs (figure 28);
  • Form_BarCharts.cs;
  • Form_PieCharts.cs.

How could it be that one of the main methods to organize all the moving / resizing in the most complicated forms is so short and simple? What the OnMouseDown() method has to do is to start the moving / resizing, which is done by a single call to Mover.Catch(…). And as I explained before, even the rotation of three classes (TextMR, PieChart, and RingSet) is automated inside this Mover.Catch(…) method. Certainly, if for any reason you would like to show the angle of rotation for some classes of comments, but hide it for other comments (I can’t imagine such situation, but still…), then instead of the mover.Catch(…) method with three parameters there must be several lines of code, as was described in the chapter Rotation.

The OnMouseUp() method in all the forms with the complicated objects also becomes longer, but only because the different context menus can be called depending on the place of the right button click. Usually each class is associated with its personal context menu, so the more different classes you have in the form, the wider can be the variety of the context menus, but the general form of this method will be still the same:

private void OnMouseUp (object sender, MouseEventArgs mea)
{
    if (mea .Button == MouseButtons .Left) {
        if (mover .Release ()) {
            // redefine the cover, if it is of the N-node type
            Identification (iInMover);                  // Identification
            // popup object, if the movement was minimal
        }
    }
    else if (mea .Button == MouseButtons .Right) {
        if (mover .Release ())
        {
            Identification (iInMover);                  // Identification
            // context menu selection depending on the touched element
        }
        else
        {
            // context menu for an empty space or some action
        }
    }

Several rules, mentioned above, are the universal rules for all the user-driven applications. When the users receive another scientific or financial application for their work, they don’t need to learn anything. The only thing users need to know is that all elements in the program are moveable, resizable (if needed), and can be rotated (if needed). What is amazing that after getting one application of such type, users automatically expect all other applications to work in the same way. Looks like this is the thing they really need.

And Controls Also

The code samples that I showed before and the explanations were mostly about the design of moveable / resizable graphical objects and working with such objects. But a lot of applications are based on the coexistence of graphical objects and controls; if the mechanism of making graphics alive will simply ignore controls, then for areas of such applications the developed algorithm will lose a significant part of its value. I started the whole design under the idea of eliminating the difference between graphical objects and controls, so everything was designed under the main idea that it has to work identically with both types of elements.

The designed mechanism of turning graphical objects into moveable / resizable can be easily applied to controls; moveable and resizable controls are used in a lot of forms of the Test_MoveGraphLibrary application. However, there is a small difference in organizing covers for graphical objects and controls.

The cover for a graphical object is designed in the DefineCover() method. You must also develop two other methods for each class of moveable / resizable graphical objects:

  • Move() method describes the movement of an object as a whole;
  • MoveNode() method describes the individual movements of each node.

All three methods are used by Mover from the moment you include the graphical object into Mover’s list.

Controls do not have such methods, and you don’t even need to think about them, but controls can be included into the list of moveable objects

    Mover .Add (Control control);

At this moment the Mover itself will organize the cover for this control, by adding it a wrapper of the FramedControl class. All the controls have the moveable / resizable features by default, so Mover has only to organize such a cover that will transfer the mouse movements into these control’s features. The controls’ inner area cannot be used for covers, as it is fully used by Windows for different mouse responses, and the main idea of including controls into forms is to use those numerous events, triggered by mouse clicks inside the controls. Thus, the only way to give any control a cover is to place it somewhere around its borders, and that’s what the Mover is doing with the help of the FramedControl. All standard controls have rectangular shape, so the cover will have the form of a frame around this rectangle; parts of this frame are used for resizing, other parts – for moving. The needed type of resizing is obtained by setting the MinimumSize and MaximumSize properties of the control.

  • If these values are equal, then this control is only moveable, but not resizable.
  • If the values for one dimension are equal, but for another give a range, then the control is resizable in one direction.
  • If there are ranges for both dimensions, then the control is fully resizable.

The variants of controls’ resizing are demonstrated in the Form_RectangleControlCase.cs (figure 6) and were described in the chapter Use of standard covers. If a control is added to the Mover’s list directly, then the organized FramedControl object receives the default parameters. It is possible to specify these parameters, by registering the FramedControl object with this control as one of the parameters.

mover .Add (new FramedControl (textbox, 5, 4));

While turning the controls into moveable / resizable, don’t forget about one important thing. Controls are always shown atop all the graphical objects. When all the elements are moveable and the control is positioned atop the graphical object, then their covers overlap. In such situation any user expects that the upper object is caught for movement, if the mouse is pressed in the area of the overlapped covers. As Mover selects the objects for moving / resizing according with their order in its List, then all the controls must precede all the graphical objects in this List. In such way the moving / resizing will go according with the expectations.

Each control can be turned into moveable / resizable individually, but in such a way I prefer to use only big panels or ListView controls, which can occupy a significant part of the screen. Much more often you use for design the groups of related controls that must be shown always together. There are different ways to organize the synchronous movement of several elements without changing their relative positions; next chapter is about these possibilities.

From Bricks to Blocks

As was shown before, the graphical objects of an arbitrary shape and individual controls can be turned into moveable / resizable, registered with a Mover, and after it moved around the screen. For the forms’ design the combinations of elements are often used; these unions of elements must be also turned into moveable / resizable. Each designer can organize special groups of objects and turn them into moveable / resizable according with some specific demands, but there are also well known combinations of elements, which are used by thousands of developers for many years. The MoveGraphLibrary.dll includes several useful classes, which represent the often used and highly needed combinations of elements, but in the moveable / resizable variants.

CommentedControl class synchronizes the movement of a single control with its associated text. The text can be placed anywhere in relation to the control and can be rotated simply by a mouse, as was described earlier. As the text can be involved in different types of individual movement (forward movement and rotation), the synchronous movement of the pair starts, when the control itself is moved. The possibility of the control’s resizing is determined by the values of its MinimumSize and MaximumSize properties. If the control is resizable, then it is done exactly in the same way, as was shown for the controls in the Form_RectangleControlCase.cs (figure 6). Throughout the control’s resizing, the relative position of its associated comment is not changed. There is only one restriction on the individual movement of the text, and this was done on purpose: the text can’t be left behind the associated control, so that it would be completely closed from view. If it would be allowed, then there would be a possibility of the accidental “losing” the text; if you’ll try to leave the text in such a place, it will jump from behind, so that it will be again visible and moveable.

There is a variation of the pair “control + text”, in which the limited text positioning is used. The limitation means that in an object of the CommentedControlLTP class the text can be positioned on any side of the related control with three variations of positioning along any side. Users can determine the text positioning, for example, via context menu. The sensitive area to move a pair consists of the frame around the control and the full area of the associated text. The use of the CommentedControlLTP class is demonstrated in the Form_PersonalInfo_LTP.cs (menu position Nodes and Covers – Personal information(II)).

LinkedRectangles class synchronizes the movement of a set of rectangles; some of these rectangular areas are used for painting information, others can be occupied by the controls. The positioning of these rectangles is not restricted by any rules, so they can stay far apart or overlap. There are no limitations on the filling of those rectangles with any painting, so there can be even a painted frame around all the elements of such object, but it’s not mandatory.
Group looks like an ordinary GroupBox, only it can be moved by any inner point. It also eliminates one annoying thing that happens, when an ordinary GroupBox is turned into moveable (see explanation further on).
DependentFrame this is also a group of elements, surrounded by a frame with a title. Visually it is indistinguishable from the groups of other types, but it is designed on absolutely different idea and the difference becomes obvious, when you try to move any part of it. In the groups of all other classes the resizing of the frame changes the positions and/or sizes of the inner elements; these changes are predetermined at the design stage by implementing some rules, for example, by using the dynamic layout. For the DependentFrame the inner elements become the main thing of the frame’s resizing. Those elements can be moved and resized in an arbitrary way; the frame changes its sizes so as to surround the combined area of the inner elements. I think that this is the most interesting and promising idea in groups’ design.

The most common case of a pair “control + comment” is the TextBox with a word (or couple of words) aside, explaining, what parameter must be entered in this TextBox. For the standard design, such comment to the TextBox is often organized in the form of another control – Label, but for the applications on the basis of moveable elements I prefer to substitute all these labels with a painted text. Visually there is no difference between these cases, but a Label, as any other control, can’t be moved by its inner points, but only by its border. On the other hand, the painted text can be moved by any inner point, which is much better and easier for users.

Some of the often used controls, like CheckBox or RadioButton, include the textual information into control itself. For the same reason of organizing better movement, I often strip these controls of their texts, but paint the same text at the side of the shrunk control and use a CommentedControlLTP object.

The CommentedControlLTP class was widely used in my applications, as it turned to be an excellent “brick” for forms’ design. However, the introduction of the CommentedControl class with the individually moveable text inside the pair demanded the rethinking of some design ideas. These objects are moved around the screen in different ways, but visually there is no difference between them, so a form (or even an application) must be based either on one or another class, so that users will be not confused.

Text Box:  
Fig.30  Group includes several objects of the CommentedControlLTP class.
Text Box:  
Fig.29  Form_AddChatoyantPolygon

The CommentedControlLTP class is the simplest among the mentioned classes; it is not even a “block”, but only a “brick” for the design of user-driven applications. The class is used in a couple of forms in the Test_MoveGraphLibrary application; one of the cases is the Form_AddChatoyantPolygon.cs (figure 29). Everything is moveable in this form; the polygon, which appears in a regular shape, is also resizable, so everyone has a chance to change the inner view of the form to whatever he prefers. There is a single parameter to be changed in this form – the number of apexes, so this is a classical case of using the commented control, which unites the control for parameter setting with a one word comment to this control.

ccApexes = new CommentedControlLTP (this,
                    numericUD_Apexes, Side .W, "Apexes");

Text Box:  
Fig.31  Form_PanelsAndGroups.cs

Usually, when you have several controls to organize the setting of several related parameters, then such controls are united into some kind of group. Figure 30 demonstrates such a group, inside which several CommentedControlLTP objects are used. This is a Line and ticks group from the tuning forms for horizontal and vertical scales; the group itself is resizable; I’ll write about such groups a bit later. All the CommentedControlLTP objects inside the group are positioned on a specially darkened area. Any object can be moved around and placed anywhere on this area; if moved across the border of the area, an object will simply disappear. (There is an easy way to restore the default view.)

Text Box:  
Fig.32	Nonresizable and resizable panels with their covers

Panels and groups with the sets of controls and graphical objects inside their areas are the “blocks” of the forms’ design. There are different variants of using such blocks for design of the user-driven applications; some of these variants can be seen in different forms of the application, but I think that the best way to compare different cases is to include them into one form, so there is the Form_PanelsAndGroups.cs (figure 31, menu position Nodes and covers – Panels and groups). Certainly all these objects are moveable, but only some of them are resizable. These objects differ in the way they can be moved around the screen and resized. Let’s look at these classes one by one and compare their features.

The simplest case in organizing such a group is a standard Panel. Two variants of using panels are shown in the left column: one panel is nonresizable, another is resizable (figure 32). Panel is a control, so it can be registered with a Mover in an easy way, which was explained in the previous chapter And controls also.

    mover .Add (panelNonresizable);
    mover .Add (panelResizable);

Text Box:  
Fig.33  Nonresizable and resizable GroupBox with their covers

There is no difference in registering the resizable or nonresizable panel with a Mover; the possibility of resizing is determined only by the MinimumSize and MaximumSize properties of these panels. If the panel is resizable, then the positions and sizes of all the inner controls during the resizing are determined not by the Mover, but according with the rules that were set at the design stage, for example, via anchoring, or coded inside the ClientSizeChanged event.

The case with a panel is very easy to organize, but I prefer not to use it, because any panel can be moved only by its border. The inner area of a panel can’t be used to start moving; from my point of view this negative feature outweighs the easiness of the panel’s use.

Use of a standard GroupBox is very similar to the use of a Panel; samples of the nonresizable and resizable GroupBox are shown in the second column at figure 31. This case is as easy to organize, as a previous one with a Panel. It also has the same negative feature as a Panel: you can’t move a GroupBox by its inner points. But in addition, the standard GroupBox, when turned into moveable, has one more negative feature of its own. On figure 33 the nonresizable and resizable GroupBox are shown with their covers; it’s easy to see that on three sides the cover is close to the visible frame, so on those sides anyone can grab this group “by the frame”, but at the top the cover is far away from the frame. Usually covers are not shown at all; if you try to grab the group with the invisible cover by its frame at the top, it would be impossible. You’ll find that you can grab such group, if the cursor is moved somewhere up, where there is no indication of such possibility (except for the changing of mouse cursor). This thing becomes very annoying if some form is constructed of the standard group boxes. That was the reason, why first a special GroupBoxMR class was designed and used everywhere throughout my applications instead of the ordinary GroupBox. Now I use even the better version – the Group class. I’ll write about these classes a bit further.

Text Box:  
Fig.34	LinkedRectangles object

LinkedRectangles class is represented in the Form_PanelsAndGroups.cs by two samples in the third column, but they are not for resizable and non-resizable cases; a LinkedRectangles object is always non-resizable! An object of this class can unite into a synchronously moving group any number of arbitrary positioned rectangular areas. Some of these rectangles can be occupied by controls; others can be used for painting. One of the LinkedRectangles objects is shown at figure 34.

LinkedRectangles class has a number of constructors (12 at the moment). The object on figure 34 includes controls and additional rectangles, so I use the constructor, based on an array of controls, and then I add to the designed object those rectangular areas.

lrNoFrame = new LinkedRectangles (new Control [] {
                                         btnInsideClr, btnBorderClr, comboShape});
lrNoFrame .Add (new Rectangle (…), "Picture");

Text Box:  
Fig.35	LinkedRectangles object with a frame

Any rectangle can be marked with a tag (of the string class), which makes easier the process of their identification in case of the needed painting. The sensitive area of any LinkedRectangles object includes all the rectangles. There is no resizing or reconfiguring; all parts of the sensitive area are used for one purpose only – to move the whole object without any changes of relative positions of its parts, so the order of those sensitive areas doesn’t matter at all. Their number and possible overlapping do not matter either, so the set of sensitive rectangles can be organized in different ways, but if there is no difference between the combined sensitive areas, then the design of such object is not important. Very often, but not always, a LinkedRectangles object has no frame to indicate its combined sensitive area. If the LinkedRectangles object is combined of several rectangles, positioned near each other, then it’s better to one or several additional rectangles to avoid any insensitive small areas inside. If by the logic of design, the group must consist of the parts, positioned far away from each other, but which must move synchronously whenever any one of them is moved, then you don’t need to unite them with an additional big area.

On figure 35 there is another LinkedRectangles object; this time it has a frame, which makes it look like an ordinary GroupBox. To make the frame look similar to an ordinary GroupBox, the drawing method Auxi_Drawing.CurvedFrame() from the MoveGraphLibrary.dll can be used.

rc = lrFramed .Rectangle ("Area");
rc .Inflate (-3, -3);
Auxi_Drawing.CurvedFrame (grfx, penFrameLR, rc, strTitle_Framed,
                          StringAlignment.Near, 4, Font, SystemColors.Highlight);

The LinkedRectangles object, if used with a frame, looks like a GroupBox, only it can be moved by any inner point and by any point, close to its visible frame. The only negative feature of this class is that it is nonresizable; this problem can be solved by switching to an object of the GroupBoxMR or Group class.

Text Box:  
Fig.37  Resizable Group
Text Box:  
Fig.36  Resizable GroupBoxMR

The GroupBoxMR class (figure 36) was the first attempt to design the resizable group, which can be moved by any inner point. The objects of this class can be used with four different variations of resizing, so there was a problem of visually distinguishing these four cases. To solve it, the frame in this class has the additional curves in the corners and in the middle of the sides; these curves inform users about the possible type of resizing. The areas of these curves are also the places, where the resizing can be started; all other parts of the frame are used for moving of an object. The curves in the corners are painted in three cases of resizing (Any, NS or WE), but the curves are different in these cases. The curves in the middles are painted only when the group can be resized in this direction. I used this GroupBoxMR class widely enough, but now the Form_PanelsAndGroups.cs is the only one, where you can see it. The rules of the good design for moveable / resizable objects require that an object can be resized by any border point. When I thought out a similar class, which is obedient to this rule, I switched everywhere to the Group class (figure 37).

The main problem was not the design of the class (it is simple enough); the problem was in visualization of the resizing possibilities in such a way that each of four cases can be easily distinguished. To inform about the resizing, the middle parts of those sides of the frame, which can change their length, are shown as a dash line. (If there is a title, the dashed line is not shown in the upper line.) If you look into the different tuning forms for the scientific or financial graphics, you’ll find that all these forms now use the Group objects (see figures in the next two chapters).

Two groups at the bottom of figure 31 represent the latest development in moveable / resizable groups – the DependentFrame class. Though visually they look similar to some of the previously mentioned classes, the main idea of this class design is absolutely different. With all other resizable groups, the visible frame is the main element: the positions and sizes of the elements inside are determined by the frame’s size. For the DependentFrame class this rule is turned upside down. The inner elements can be either of the FramedControl class or of the CommentedControl class. In the first case, it is a control, which can be moved and resized by its border. In the second case it is a moveable / resizable control, accompanied by an additional text; the pair “control + text” can be moved synchronously, but the text can be also moved individually and rotated. When any collection of such elements is united into a DependentFrame object, then all the inner elements can be freely moved and resized, but the frame’s sizes are automatically changed in such a way, as to surround the united area of all the inner elements and indicate that it is really a group of the related objects. The whole DependentFrame object is moved by its frame or by any inner point.

Text Box:  
Fig.38  DependentFrame object

At first, all these independent movements of all the inner parts sound like a real chaos; in reality it turned out to be a very interesting, effective and the most promising type of form’s design. For example, the group at figure 38 represents a widely used combination of controls, which you can see in a lot of applications, where the address information is required. In some parts of the world this is the usual way of writing the address; in other parts the opposite order is used with the country at the beginning, then province, town, and the street at the end. With all those inner elements moveable and resizable, it takes seconds to rearrange the view to whatever is needed.

The Test_MoveGraphLibrary application demonstrates three different forms for organizing personal information; through the menu position Nodes and Covers – Personal information (III) you can open the form, which is based on several DependentFrame objects.

The resizable group from figure 37 is represented in the Form_PanelsAndGroups.cs in three different samples: as a GroupBoxMR object, as a Group object, and as a DepenedentFrame object. The first two samples differ only in the type of resizing:

Text Box:

  • for the GroupBoxMR object the places for resizing are marked by the additional curves of the frame;
  • for the Group object the resizing can be started by any frame’s point.

In these two cases the resizing can change the relative sizes of the elements inside the group, but not the general view of the group. The case of the DependentFrame is absolutely different: not only the sizes of the inner elements, but the whole view of the group can be changed in the same simple way. That’s why I think that the DependentFrame class shows the most effective way of the forms’ design on the basis of the moveable / resizable elements.

Technical notes.

  1. This one was already mentioned at the beginning, but I want to repeat it here, because it is important even in case of moving exclusively controls. 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.
  2. The second note is especially important for the groups with a lot of controls inside. On moving such groups, a lot of controls change their positions. The system decides itself, when would be the best moment to do it; in some cases it produces not the best effect of visualization. That’s why in several forms, where I use the LinkedRectangles class, you can find such an addition inside the OnMouseMove() method.
if (mover [mover .CaughtObject] .Source is LinkedRectangles)
{
    Update ();
}

The possibility of moving groups of controls around the screen is so valuable, that I am using it widely enough, but the consequences of applying moveable / resizable features to the controls and groups of controls are much more interesting and valuable than simply moving them around. Now we have an algorithm that allows to:

  • Turn any graphical object into moveable / resizable.
  • Turn any control into moveable / resizable.
  • Combine any set of controls and graphical objects into a group, moving together.

Using of these three things allows to implement a new type of forms’ customization. But this is only a first step. When all the objects in a form are turned into moveable / resizable, then it is much more than a form’s customization, it is a new type of programs – user-driven applications.

More than Customization

At first, including controls into the same mechanism of turning objects into moveable and resizable doesn’t’ look so significant: we already have moveable and resizable graphics, now we have controls with the same features. The simple implementation of these features will give users much wider flexibility in their work; an excellent example of using these features in their pure form is demonstrated in the Form_ManyPlots.cs, which is shown on figure 25 and discussed in chapter Y(x) plots and more. While analyzing some graphs in that form, you change their sizes for a better view; if one of the controls close some part of the plots; then you simply move this control aside and / or change its size. But now I want to show that if you take the complicated form with a lot of different controls and graphical objects, and make all the parts of such form moveable and resizable, then it allows switching to an unparallel level of dialogs’ customization and even further on to the new way of applications’ design. What is also very interesting is the idea that the technique, originally designed for moveable / resizable graphics, turned out to be of exceptional value for the forms that may not include any graphics at all but consist of only a large number of different controls.

There is a huge demand for customization of forms for users’ purposes. There are tons of papers about the ideas in this area, but mostly there are two major directions for organizing such customization:

  • adjusting the sizes and positions of the form’s inner parts to the changes of the form’s sizes or parameters;
  • allowing users to set one of the predefined views of the form.

The first direction is inside the ideas of dynamic layout. At the beginning it was using only anchoring and docking; in the last few years Microsoft put huge effort into the development of dynamic layout tools for programmers and there are interesting results in this area. The second direction is called by different names; the most popular label throughout the last years is adaptive interface. Selection of the form’s view is often done either via context menu or through the set of buttons or… It really doesn’t matter, how it is done; the most important thing that the choices are predefined by the designer. The links between the two mentioned types of transformations and the two widely used terms are fairly tentative: dynamic layout is a subset of adaptive interface, but the two mentioned ways of forms’ transformations use absolutely different techniques.

What is common for any form of adaptive interface (even if it was declared under another name) that somewhere inside there is a model of the user’s behaviour. This model is either fixed at the moment of design, or can be refined throughout the work of an application, which is slightly better. The most important thing that this model was put inside by the designer; after it an application tries to work according with this model as best, as it can.

Implementation of moveable / resizable objects opens the new way of forms’ customization, but I want to emphasize that there is no epic battle of “Dynamic layout vs. Moveable graphics”; these two things are orthogonal.

Text Box:  
Fig.39  MSPlot tuning form (default view)

  • Dynamic layout is the programmers’ instrument to make forms look good under the changing conditions, but inside the dome of dynamic layout all transformations are predetermined; they are as good as the level of the programmer.
  • Moveable / resizable graphics, implemented in an application, becomes the users’ instrument, and with it each user determines for himself, what and how he wants to see in the form. There is no list of choices, predetermined by the designer; such list simply can’t exist in the applications, based on moveable / resizable elements.

For illustration let’s turn to the tuning form of the MSPlot objects, which was used with all the samples, described in chapter Y(x) plots and more. For example, when I double clicked the plotting area, which is shown atop of others in the Form_GraphsAndComments.cs (figure 27), then the tuning form for that area looks like shown on figure 39.

There are six obvious parts with the frames around them in this form. A couple of the groups look like ordinary GroupBox with some controls inside; other groups have an unusual type of frames, so your doubts that these are ordinary GroupBox are correct. Nearly all the things that I am going to write about here, can be achieved with ordinary GroupBox, but even better results can be achieved with the objects of the Group class, which was mentioned in the previous chapter. This class is included into MoveGraphLibrary.dll, and all the groups in this tuning form belong to this class.

There are no unmoveable elements in this form; there are six moveable groups (four of them are also resizable) and the small moveable button in the left top corner; this button is used for restoring the default view of the form. In the previous versions, there was one more small button to switch ON / OFF the visualization of covers in the form; I decided that that button was redundant, but the covers can be still switched ON / OFF via the context menu, called at any empty place outside the groups.

Text Box:  
Fig.40  MSPlot tuning form with the covers

If you switch ON the covers’ visualization, you see something similar to figure 40. The covers of the groups have some similarities, but also some differences. Maybe they reminded you something, and in this you are right: the covers here are identical to the rectangles’ covers at figure 5 in chapter Use of standard covers.

Frames (figure 39) and covers (figure 40) inform that:

  • all the groups are moveable;
  • two groups are non-resizable (Borders and Grids);
  • two groups are resizable only horizontally (Data colors and Main area);
  • two groups are resizable in all directions (Comments and Scales).

If you compare figures 39 and 40, you notice that all these differences in resizing are demonstrated by the dashed lines in the frames, so you don’t need this covers’ visualization at all. The only thing the users need to know that everything here is moveable, and then the change of the mouse cursor will inform about all the possible movements and resizing.

So how these moveable / resizable groups can help in forms’ customization?

Text Box:  
Fig.41  Customized MSPlot tuning form

This form is an instrument to change the viewing parameters of the main plotting area, to add / delete / modify any number of comments for this area, and to switch ON / OFF the scales. All these things are important and are used from time to time, so they must be included into the tuning form. Part of this tuning is used very often; other parts are rarely in use. Resizable form and the possibilities of moving and resizing all the inner parts allow to change the form’s view to whatever you personally need in a second.

Text Box:  
Fig.42  Customized MSPlot tuning form

For example, if you need only to change the lines and markers for painting a lot of functions in your plotting area, then you can enlarge and keep in view the only needed group – Data colors (figure 41).

If you have a plotting area with a set of graphs, to which you need to add a lot of comments, then you can enlarge another group (Comments) and move all other parts out of the view (figure 42).

Figures 41 and 42 show only two different views of the customized MSPlot tuning form, but the main idea of such customization is that USER can decide at any moment, which groups to keep in view, and what size each of those groups are going to be. The possible variations are not limited in number or view; this is a principally different customization that far exceeds by its power any other type of customization.

Any known (described in literature) form of adaptive interface (and dynamic layout is its subset) is based on the designer’s analysis of the possible form’s transformations. The developer fixes in code the form’s view for any possible combination of sizes, font, and selection of other parameters. The results are absolutely determined by the set of parameters, involved in this analysis, and the results are as good, as the programmer’s level. Users have no chances to go beyond the limit of developers’ understanding of the situation.

The forms’ customization, based on using moveable / resizable elements, is of absolutely different type. The main idea is different! The designer of the form has to make a decision about moveable / resizable parts; this decision influences the effectiveness of users’ work with the form; it must give the maximum flexibility to the users and at the same time not demand anything extra (not needed) from them. There can be different ideas in organizing moveable / resizable parts; there can be decisions about which of them are resizable and which are not; there can be different ways to inform users about possibilities of resizing, but these are the variants inside the main idea that USER receives the full control of the view.

For example, in the shown MSPlot tuning form, there can be two groups for grids (horizontal and vertical) and two groups for scales (again horizontal and vertical). Both things were implemented in the previous versions of the same form, so the number of moveable / resizable elements was different. Older versions of the same form used the standard GroupBox controls, so they could be moved only by the borders; the standard frames do not inform about the variants of resizing, so the only prompt was from the changing mouse cursor, or the covers had to be switched ON. Switch to the Group class allowed not only to make the type of resizing obvious, but also such groups can be moved by any inner point. I don’t think that these are the last changes in such forms’ customization; I constantly work on new and newer things in this area and implement these new things all the time, but the main idea is that:

  • all the parts in the form are moveable / resizable;
  • user has the full control of this moving / resizing, so this customization is done according with the user’s requirements.

The analogue with our everyday life. Nearly all our furniture is moveable, but we are not moving it around all the time; we can do it and we are using this feature whenever we need to. Maybe rarely, but this feature is valuable. Design your applications in the best possible way and give the users of your applications even much more flexibility than they have with their furniture. “Much more flexibility”, because the sizes of the forms can be changed, and by turning all the inner elements of the forms into moveable / resizable, you can give the users an extremely powerful, but easy to use instrument.

Charles Petzold wrote [4] that “dynamic layout is … an important part of future Windows user-interface design philosophy”. I would agree, if you are going to use only the standard fixed graphics. But moveable / resizable elements are the base of a new paradigm – user driven applications, and the shown type of forms’ customization is one feature of such applications.

Applying the moveable / resizable technique to graphics and controls can change the content of the form by changing its sizes and moving out of the view whatever is not needed at the moment. I think that this technique is a very interesting way of design; there are no predefined cases fixed forever at the stage of application’s design; even for the same set of parameters and settings, users get an instrument for customization according with their demands at each particular moment.

All the samples that were shown here and that are demonstrated in the Test_MoveGraphLibrary application are especially designed to highlight one or another feature of the new technique and design. The big questions are:

Text Box:  
Fig.43  Calculator program

  1. Can this technique be applied to the existing applications?
  2. How difficult is to include these new features into the existing programs?
  3. If the new features are added to the existing application, how difficult for the users will be to switch to the new version?

To answer all these questions, let’s look at the well-known Calculator program (figure 43) that is used by millions of people around the World.

I use this program very rarely, so when I have to divide two big numbers, I can find the needed digits after some time, but the location of those buttons looks a bit strange to me. I don’t know the feeling of those, who use this program all the time (I think that they simply got used to the placement of the digits and do not pay attention if their positioning is good or not); I know that the placement of the needed buttons is not good FOR ME. But the program works excellently, so the program itself doesn’t need any changes. It needs only a simple addition of several lines of code.

    Mover mover;
    mover = new Mover (this);
    foreach (Control control in Controls)
    {
        mover .Add (control);
    }

}

And then those three mouse events in their purest form.

    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 (object sender, MouseEventArgs e)
    {
        if (mover .Move (e .Location))
        {
            Invalidate ();
        }
    }

Text Box:  
Fig.44  My personal view of the Calculator

That is ALL! No other changes in the code, the Calculator works exactly in the same way as before, but now everyone can change its view to whatever he wants at any moment. I spent two minutes and received what is shown at figure 44.

Text Box:  
Fig.45  The Calculator I prefer to use now

I don’t see any sense in comparison of these two designs or discussions about the best layout for Calculator. I simply prefer my design. Tomorrow I’ll prefer something else, and I’ll change the view of this working application. I can put all those controls in any way; their positions are stored; the next time I’ll start my Calculator, it will look exactly like before saving. Everyone can organize the view, which is the best personally for him.

Several days passed and I understood, what I had to change more. I use this program only for primitive operations with the numbers, which I can’t do in my head with enough accuracy. On those rare occasions I never use calculator’s memory, so I don’t need all the related buttons and some others. Several seconds more for further transformations, and figure 45 shows the calculator that I prefer to use now. That’s the way all my applications work now: I can easily adjust them at any moment.

To customize the forms according with the constantly changing demands, you need to use moveable graphics and moveable controls, plus an easy way of declaring groups of elements, moving together. These are all the things that I tried to demonstrate in this and previous chapters. While applying this technique to more and more complicated forms, you will find out that their complexity does not affect the implementation of moving / resizing in your code. Nearly everywhere the code for organizing the whole moving / resizing process looks like for this Calculator. 15 lines of code at the top of this page!

Does it worth solving a lot of problems and giving users an extremely powerful and useful mechanism?

Text Box:  
Fig.46  Years selection

Let’s look at another nice problem that can be easily solved with the moveable / resizable elements. This is a case, when you have some data, of which you need to select a part for further processing. Usually the full set of data is shown in one list; the selected items are included into another list; figure 46 demonstrates the case, when you need to select several years from the long list. Suppose that all the parts of the code for selection of the needed data work fine; you can select, add, and delete the lines in the lists without any 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 mirror 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.

It’s not the problem of the implementation of those operations; it’s the problem of the general view on applications’ design:

  • you can fix the view in the way you prefer it to be OR
  • you can design it in such a way that everyone would easily make it looking nice personally for him.

In the first case (this is a standard case for all the modern day applications) 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 why not to design this form in such a way that EVERYONE will position the lists in the way he personally prefers? The changes are simple and easy to implement: two objects of the Group class and a moveable OK button. The code can be seen in the Form_DataSelection.cs (menu position Nodes and covers – Data selection).

Text Box:  
Fig.47  The default view of the Form_PersonalInformation.cs

The moveable OK button helps to solve another, maybe a minor problem, but very annoying to some people. To close a form with simultaneous saving of some needed results, you often have to click some button (usually it is OK, Yes or Save). Developers position this button according with their own preferences; the most common places are in the middle at the bottom of a form, in the right bottom corner or in the right top corner. Users often think that the best place for such a button would be not where the developer positioned it, but somewhere else. In this form with a moveable OK button, any user has a chance to position this button in the best place personally for him. The position of the button is saved, so this is done once and does not annoy you any more, but…an additional funny remark. I found out that with all the elements in the programs turned into moveable / resizable, you often want to move the buttons and other controls. Maybe a bit, but the best placement of the controls looks different on any new day. It’s like moving the chairs at home: we always put them at nearly the same place, but not exactly at the same spot.

Figure 47 demonstrates another sample of similar type, but with much bigger number of elements. A lot of programs work with some set of personal data; in some cases the users have to enter their information; in other cases they receive the information from the database and see the needed data in those fields. The exact set of edit controls depends on the purpose of application; the view of the form depends on the designer. The default view of the Form_PersonalInformation.cs 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.

The shown placement of the controls is good enough for some western countries, but definitely not good for other parts of the world. There are countries, in which:

  • The family name is always goes first.
  • The day always precede month in any form of the date.
  • The address is written in the opposite order: ZIP code, country, town, and so on.

Certainly, you can enter the needed information according with this form, even if the order of fields for a date or an address is very unusual for you, but what if you have to use it again and again? What if somebody has to analyse a lot of data from the database and this order of information is opposite to natural (native) way to which he got used for many years? It becomes very annoying and the effectiveness of such work is very low. It is also possible that some users need only part of the whole information, so it would be nice to get rid of the unneeded information and not to show it at all; it would be nice simply to move those unneeded controls out.

There can be a lot of different cases; the designer can’t predict them all and give the best predetermined solution to every user. It is much easier and much better to turn all these controls into moveable (and a lot of them are resizable) and let each user quickly and easily reorganize the form to the best personal view. Certainly, it requires some additional code for saving / restoring, but the application becomes much better.

There can be different ways of turning these controls into moveable / resizable and different ways of rearranging the view of such form; the Test_MoveGraphLibrary application allows to check three different solutions. The figure 47 can represent two of these solutions; it’s impossible to distinguish them visually, their default view is identical, and only on trying to move the elements of the forms their difference becomes obvious.

Form_PersonalInformation.cs (menu position Nodes and Covers – Personal information) is based on the standard CommentedControl objects. Each text can be moved and rotated individually; the pair “control + text” can be moved around by the sensitive frame around the control.

Form_PersonalInfo_LTP.cs (menu position Nodes and Covers – Personal information(II)) is based on the CommentedControlLTP objects. The abbreviation LTP means Limited Text Positioning. The text can be placed differently in relation to the control, but the number of different positions is limited: three possibilities of text alignment along each of four sides. Selection of the text’s position can be organized in different ways; in this form it is done via R_Click at any text, which opens a small additional form. Text cannot be rotated; the pair “control + text” can be moved around by the sensitive frame of the control, but also by the text itself.

The third type of organizing personal information is slightly different, it unites the related controls into obvious groups; from my point of view it is the best decision and it is very promising for forms’ design.

Text Box:  
Fig.48  DependentFrame objects

Form_PersonalInfo_DependentFrame.cs (menu position Nodes and Covers – Personal information(III)) is based on the standard CommentedControl objects and on the DependentFrame objects (figure 48). These groups were already mentioned in the chapter from Bricks to blocks, where different types of groups’ organization are compared. The inner elements of any DependentFrame object can be moved and resized in an ordinary way, but regardless of all those changes the frame is always shown around all its elements. And the whole group can be moved by the frame or by any inner point.

I try to keep the code of all the demonstrated forms as simple as possible, so that it shows only the most important features, but in this form I added a small auxiliary thing. Here the context menu can be called on any DependentFrame object; this menu allows to change the alignment of the frame’s title. Just to show that a lot of other parameters can be easily changed by the users.

All three variants of these forms for personal information demonstrate the ways of customizing the forms. It’s not only about the personal information; it’s more about the personal view of the applications for any user. The elements of such applications can be changed in different ways; the main thing that instead of the list of predetermined choices there is an instrument, which allows users to do whatever they want. It is the most effective type of customization, which has no limits, introduced by the designer. The designer is not supposed to decide, how this form must be used. The designer guarantees that each piece of data will be shown in the appropriate field; the users decide about the part of information they want to see and the placement of each of those pieces.

Evolution of Programs

When you start playing with the demonstration program Test_MoveGraphLibrary, based on moveable / resizable elements, you’ll have a feeling of entering a very strange world. There are rules in this world, but you are not familiar with them, because you never tried anything like it before. But I think that you’ll find very quickly that you can do in this world much more, than in an ordinary one.

After you’ll try to write programs on the basis of moveable elements, you’ll find yourself on the same way that I am going along for nearly three years. There is an interesting law in this world of moveable elements: even a single moveable element in an application will demand all the related elements also to be turned into moveable. Certainly, I had no idea about such a law at the beginning of my work, but with the increasing number of applications, designed on the basis of moveable / resizable elements, I began to understand that there are no exceptions in this rule. Here are the steps, which I had to make one after another, when I did the first one.

When this technique was first designed, I immediately applied it to the objects, which initialized the whole work: the scientific plotting. I took the big application with a lot of different plots, turned the plots into moveable / resizable, and began to play with this new program. It was a huge improvement of an application, but the more I worked with it, the stronger became the filling that something was incorrect. The logic of design was perfect for a fixed designer-driven application; even turning of a single part of it into moveable / resizable began to corrode the whole construction. The features, which were thought up to the tiny details and were used for years without any problems, didn’t want to work smoothly with the new plots. The logic of moveable and still non-moveable parts began to conflict.

Plots have different scales; usually the scales are positioned near the plotting area; in the old versions users could open the tuning forms and type in some parameters, which would change the scales’ positions. The moving / resizing of the plots changes the positions and sizes of the related scales in an appropriate way, but the individual relocation of the scales was still done only via the tuning forms, and it looked very awkward. If a plot with all its scales can be moved around, why the scales themselves can’t be moved in the same easy way?

The second step was done – scales also became moveable in the same easy way by the mouse, but it wasn’t simply turning another graphical object into moveable. Plots and scales are the strongly linked objects with the type of “parent - children” relation. So now there were objects (plots), which could be moved synchronously with all their related parts, but at the same time those parts could be involved in individual movements. That required some type of identification for the objects, involved in moving / resizing, which would guarantee the correctness of synchronous and individual movements for the objects with any type of relations between the parts. It had to be not the new type of identification for each new class of objects, but the general solution for any classes, which will be designed in the future and involved in different types of movements.

With the plotting areas and scales now moveable in any possible way, my attention turned to another huge problem, for which neither I nor anyone else could find a good solution before: positioning of the comments. In all the numerous scientific and engineering applications the plotting areas were better or worse positioned by the designers. The results, which are shown in those areas, often need some comments, but the exact lines’ positions are defined as the result of the applications’ work, so there is no way to determine beforehand the good position for these comments. Again, it was done before via some tuning forms, but now it looks very strange if the plots and scales can be moved easily around the screen, but the comments to these objects are positioned and changed in some archaic way. The comments have to be moved exactly in the same easy way, as plotting areas and scales – by a mouse. In addition, the comments need to be not only moved forward but also rotated for better placement along the arbitrary lines. At the third step all the comments were turned into moveable. What’s next?

Even if all the graphical objects (plotting areas, scales, and comments) are turned into moveable / resizable, but anything else in the form is left unmovable, then the users would always bump on this hillock, even on a small one. The inner area of applications is populated both with the graphical objects and controls, so controls and groups of controls had to be turned into moveable / resizable. I made this change, received the new type of scientific applications, and that was the new paradigm – user-driven application. But that was not the end of the changes.

The plots and other graphical objects have a lot of visualization parameters, so different forms are used for tuning all these parameters. If the main part of the application became user-driven, then why all the auxiliary parts are still fixed and the work in them is defined not by the user but by the designer? Those forms had to be turned into user-driven, and that was done. So, my steps along this road can be shortly described in such a way.

  1. I designed moveable / resizable areas for the scientific plotting.
  2. This required the use of moveable scales along those areas.
  3. Then all the comments, associated with areas or scales, had to be turned into moveable.
  4. Next the controls of the forms with moveable graphics had to become moveable.
  5. All the elements in the forms, where the scientific plotting was used, became moveable; now they demanded the redesign on a moveable basis of all the associated tuning forms.

The last item opens an interesting question: how difficult is such a turn of the standard forms into the new type? Does it require the full redesign of the forms?

Suppose that you have a complicated form, populated with a lot of controls. You developed it some time ago, maybe, years ago; this form of a classical design works perfectly; the clients got used to it. Do you have to redesign everything, if you want to turn your application into user-driven? No, certainly, not, but you have to change the way, in which the users would work with this form.

Text Box:  
Fig.49  Form_HorScaleParams.cs in March 2008
Text Box:  
Fig.50  Form_HorScaleParams.cs in March 2009

Let’s compare two figures, which show the changes, which happened throughout the last year in the tuning form for the horizontal scales of my scientific plotting. Figure 49 shows the view, which this form had a year ago (March 2008); at figure 50 the current day version is shown. Both pictures are taken at exactly the same ratio to the original size, so you can see immediately that the new version occupies less space on the screen, but this is not the most important result. The new version is much more effective in work, and this is the result of giving to the users the full control of its view.

The visualization parameters of the scale didn’t change throughout this year. Only instead of a single label, which was allowed for a scale a year ago, now a scale can have any number of comments (labels), so instead of the old Label group there is now a Comments group with all the possibilities to add, delete or modify an unlimited number of comments. The scale definitely became much better.

The main change in the form is that it is now divided into four moveable groups, of which three are resizable. Positioning of the groups and their sizes are decided by the user individually; if some groups are not needed, they can be simply taken out of view. Restoring of the form’s default view can be done with one click on a small button in the left- top corner; this is the only unmoveable object in this form, and this was especially done in order not to throw away this emergency button. Though this button can be expelled from the design and the restoring of the default view implemented via a position in context menu; this is for designer to decide about one way of restoration or another.

Very interesting is the organization of the Lines and ticks group. The inner area of the group is shown slightly darker, than other areas of the form; it was done to highlight the special features of the controls, which are placed inside this group. There are eight different controls inside; all of them are moveable, and not only can they be repositioned in any possible way, but they can be also taken out of the view: when any control is moved outside the darker area, it disappears. The darkened area changes its size according with the sizes of the group, so these controls can disappear or arrive again simply as a result of the group’s resizing. The default view of this group can be restored independently via the context menu that can be called on this darker area or on the group’s frame. If the small button in the corner is clicked to return the form’s default view, then the original view of the Lines and ticks group is also reestablished. These rules are working in the group now; I am sure that in several months there will be something new.

At the moment:

  • the sizes of the groups can be changed;
  • the groups can be positioned in any way relatively to each other;
  • groups can be taken out of view, if they are not needed;
  • the inner area of the Lines and ticks group can be changed to whatever any user wants to do with it.

These are the results of applying the rules of user-driven applications to this form. I don’t think that any user will change this form every time, but it can be easily changed every time he wants to do it, and all the changes are decided only by the users. This is definitely much more powerful mechanism than any type of adaptive interface.

The comparison of two versions of the same form shows that the redesign of the standard forms according with the rules of the user-driven applications does not demand the full rewriting of applications. There are all the same controls, used for tuning of the same parameters, so users don’t need to learn, how to work with absolutely new application. The code behind all these controls is absolutely the same; it wasn’t changed at all, so the main part of the code for this form is the same. The new part is the declaration of those moveable groups and an addition of those three magic events: MouseDown, MouseUp, and MouseMove. They were described in details in the previous chapters; the code for them is simple.

Previously I mentioned the five steps that I made on redesigning the scientific applications. I have to add that there is one more step on the mentioned way, but this step is special and very interesting. I was told about it several times throughout the last year, when my colleagues in our department of mathematical modelling began to use more and more several of my programs.

  1. Now the people, who are working with the new programs (user-driven applications), try all the time to do the same (move and resize) with the objects in all other applications, which were designed on non-moveable objects and do not allow such things. And these people are upset, because they quickly got used to the new and much more effective way of working with applications.

One, two, many… Movers

A Mover object supervises moving and resizing of different objects, but there are only three variants for the content of a Mover’s list:

  • several graphical objects of the same type, like regular polygons in Form_RegularPolygons.cs (figure 8);
  • absolutely different graphical objects as shown in Form_Main.cs or in Form_Medley.cs (figure 28);
  • combination of controls and graphical objects, as can be seen in different forms (figures 27 and 31).

Text Box:  
Fig.51  Form_ManyMovers.cs

Regardless of the number and type of the moveable / resizable objects, the single Mover can easily do all the work, and in all previously mentioned samples there is only one Mover per form. However, there is no such rule as “one Mover per form”, and if you think that it would be easier to have several Movers that will deal with different objects, it is possible to organize the whole moving / resizing process in such a way. But in order to use one or several Movers in the form, you have to understand the advantages and disadvantages of both cases.

Form_ManyMovers.cs (menu position Nodes and covers – Many Movers) is the sample of the form, in which not one Mover is used, but SIX !

There are three panels with different objects in this form (figure 51); also in the client area there are graphical objects of two different classes (circles and squares) and several panels; all panels and graphical objects are moveable; panels with the pictures inside are resizable. (The fourth panel with the text is not resizable and was added to demonstrate all four different types of panels’ resizing.) Each panel contains graphical objects of one particular type: circles, squares or houses. The system of Mover objects is organized in such a way.

  • In the client area of the form there is one Mover for each type of moveable objects:
        Mover moverControls, moverCircle, moverSquare;
  • There is a Mover for graphical objects of each panel:
    Mover moverCirclesOnPanel, moverSquaresOnPanel, moverHouses;

For moving of the objects on the panels and their resizing (only for the houses), the three standard events MouseDown, MouseMove and MouseUp are used for each panel. On each panel the moveable objects are supervised only by one Mover, so there is absolutely nothing special in the code of all these events.

For the objects in the client area of the form, the same three mouse events are used, but there are three Movers, operating in the same area, so different effects can be produced, depending on some code variations. Any Mover doesn’t know anything about the objects in the form; the only thing it is dealing with are the covers of the objects, registered in its List. So if you simply write the OnMouseDown() method in such a way

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    moverControls .Catch (mea .Location, mea .Button);
    moverSquare .Catch (mea .Location, mea .Button);
    moverCircle .Catch (mea .Location, mea .Button);
}

then in those places, where the covers for objects of different types overlap, you have a chance to grab simultaneously two or three objects, because each Mover will catch an object, belonging to its List. As the idea of this form was not to organize such a strange movement of objects, but an ordinary one – one object at a time, then this code must be changed. Movers do not check each others status, but the Mover.Catch() method returns a value, indicating if anything was caught or not, so this returned value can be very helpful.

private void OnMouseDown (object sender, MouseEventArgs mea)
{
    if (!moverControls .Catch (mea .Location, mea .Button))
    {
        if (!moverSquare .Catch (mea .Location, mea .Button))
        {
            moverCircle .Catch (mea .Location, mea .Button);
        }
    }
}

In such version, if any Mover has caught an object for moving, then other Movers are not going even to try. One moving object at a time.

While finishing any movement (MouseUp event), there is no need in checking, which Mover was working, and if any of them was working at all at the moment; simply release them all.

private void OnMouseUp (object sender, MouseEventArgs e)
{

    moverControls .Release ();
    moverSquare .Release ();
    moverCircle .Release ();
}

The same thing with the code of MouseMove event; don’t forget to mention all Movers, working in the client area of the form.

private void OnMouseMove (object sender, MouseEventArgs mea)
{
     moverControls .Move (mea .Location);
     moverSquare .Move (mea .Location);
     moverCircle .Move (mea .Location);

     if (moverControls .Caught  ||  moverSquare .Caught  ||  moverCircle .Caught)
     {
         Invalidate ();
     }
}

In such a way, everything in the Form_ManyMovers.cs works fine, but in some situations you may notice something strange with the mouse cursor. The covers of the moveable objects in this form use different cursor shapes to inform about their readiness for movement: circles and squares use the Cursors.Hand, the controls show different cursors, but for the ordinary movement they show Cursors.SizeAll. Move any panel so that its border will be on any colored square. Panels are always atop any squares or circles, so you expect that if you press in such situation anywhere near the control’s border, then the control will move and not the square underneath. The program works correctly, because the decision about the possible movement is made on the correct order of Movers: moverControls, then moverSquare, and then moverCircle. But if nothing is grabbed for moving, then all these Movers are checked in a row, and the decision about the cursor shape is decided by the last Mover. And though, if the mouse pressed, then the panel is going to be moved, but the cursor at the same time signals about the readiness of the square underneath for movement! To avoid this discrepancy, the code of the OnMouseMove() method must be slightly changed in such a way.

private void OnMouseMove (object sender, MouseEventArgs mea)
{
    if (!moverControls .Move (mea .Location) && !moverControls .Sensed)
    {
        if (!moverSquare .Move (mea .Location) && !moverSquare .Sensed)
        {

            moverCircle .Move (mea .Location);
        }
    }

The Form_ManyMovers.cs is a simple, but an artificial sample of using several Mover objects in one form. But there are other forms, in which in similar situations different number of Movers is used.

Class Skyscrapers is not mentioned any more in this article, but can be seen in the TuneableGraphics application; PieChart object can be seen, for example, in Form_Medley.cs (figure 28). The tuning forms for both classes are described in TuneableGraphics_Description.doc, but as both classes are used in applications, it’s easier to compare them in working applications. Both classes have similar design of their tuning forms, which are constructed of moveable / resizable groups and can be customized by users. Both classes can use a lot of colors for data presentation, so in both tuning forms there are samples of these colors that can be moved and reordered. But one of these tuning forms is designed with a single Mover, which works with all the frames and colored samples; another form uses separate Movers for frames and samples. I am purposely not mentioning here, which of the forms use a single Mover; you can play with both tuning forms and see that there is no difference in behaviour. Until I’ll look into the code, I’ll not remember myself, which of the forms is working with a single Mover.

Occasionally I design forms with more than one Mover; here are several things that help me to make the decision about the needed number:

  • If there are moveable / resizable objects only on “one level” in the form (controls and graphical objects in client area), I try to use one Mover.
  • If the moveable objects are even on “one level”, but some of them exist all the time and others are added and deleted during the existence of the form, then it’s easier to divide them between two Movers so, that one of them is working with the constant List<> and another – with the changeable List<> of elements.
  • If there are moveable / resizable objects on the “second level” (on a panel), then I use separate Mover for these objects; one Mover per objects on each panel.

These are not the strict rules, but only suggestions. If I would develop the same Form_ManyMovers.cs not as a sample, but as a part in the real application, I would use four different Movers instead of six: one per panel and one for all graphical objects and controls in the main area; there are no advantages at all of having three Movers there.

Summary

Cover Summary

  • A cover consists of an arbitrary number of nodes. The minimum number of nodes is 1. A cover may consist of a single node. Number of nodes is unlimited. Covers of a special type (N-node covers) may include hundreds of nodes.
  • Nodes can be of different shape: circular, strip with semicircles at the ends, and convex polygon. Circular nodes are defined by a center point and radius. Strip nodes are defined by two middle points at the ends of a strip and radius of semicircles; the strip’s width is equal to the diameter of semicircles. Convex polygon is defined by the apexes.
  • Nodes of a cover may overlap or they can be placed apart. The order of nodes in the cover can be very important, as nodes are checked for moving according with this order. In the areas of overlapping, moving / resizing of an object is determined by the first selected node.
  • Nodes can be moved individually thus allowing to reconfigure an object.
  • Nodes can be enlarged to cover as much of the object’s area as possible. Such enlarged nodes are often used for moving of the whole object.
  • A cover may consist of the nodes that are not moved individually, but an attempt to move such node results in moving of the whole object (the MoveNode() method only calls the Move() method). This makes an object moveable, but not resizable.
  • Each of the nodes has its own parameters. It is easy to allow resizing along one direction but prohibit it along another; this means organizing a limited reconfiguration.
  • It doesn’t matter that some covers may represent graphical objects and others controls or groups of controls. All covers are treated in the same way, thus allowing the user to change easily the inner view of any application.
  • Covers can be visualized, though the best design makes moving / resizing of objects obvious without such visualization. Visualization of cover includes possible filling of the nodes’ inner area and drawing of the nodes’ perimeter.

Moving / Resizing Summary

  • To make any graphical object moveable / resizable, it must be derived from the GraphicalObject class and three methods must be written for such object: DefineCover(), Move(), and MoveNode().
  • DefineCover() method defines the cover of an object as a set of nodes; each node has an individual number.
  • Move() method describes the forward movement of an object as a whole.
  • MoveNode() method describes the individual movement of the nodes; if the moving of a node results in the moving of a whole object, then the Move() method is called from inside the MoveNode() method. An individual movement of a node may cause the relocation of some or even all other nodes; in such cases the DefineCover() method is often called from inside the MoveNode() method.
  • Any combination of elements can organize a set of synchronously moving objects.
  • To organize moving / resizing process, there must be an object of the Mover class.
    Mover mover;
  • To prevent the accidental moving of elements out of view, the parent form must be mentioned during the Mover’s initialization
        mover = new Mover (this);
  • Three levels of moving the objects across the form’s borders can be organized: moving outside is not allowed, moving allowed only across the right and lower borders, or moving allowed across all four borders.
  • Mover will supervise the whole moving / resizing process only for objects that are included into its List<>.
        mover .Add (…);
        mover .Insert (…);
  • For the complicated objects, consisting of the parts, which can be moved both synchronously and independently, and for the objects, for which the set of such parts can be changed, it is much better to develop an IntoMover() method, which is used instead of manual registering of all the moveable parts and which guarantees the correct registering of an object and all its parts regardless of a set of constituents.
  • Moving and resizing are done with a mouse, and the whole process is organized via three standard mouse events: MouseDown, MouseUp and MouseMove.
  • MouseDown starts moving / resizing by grabbing one of the nodes. The only mandatory line of code in this method is
        mover .Catch (…);
  • MouseUp ends moving / resizing by releasing any object that could be involved in the process. The only mandatory line of code in this method is
        mover .Release ();
  • MouseMove moves the whole object or a single node. There is one mandatory line of code in this method, but in order to see the movement, the Paint method must be called
            if (mover .Move (mea .Location))
            {
                Invalidate ();
            }
  • If covers must be shown, then one of the available drawing methods must be used, for example,
            mover .DrawCovers (grfx);
  • If needed, several Mover objects can be used to organize the whole moving / resizing process. Each Mover deals only with the objects from its own List<>.

Final Remarks and Some Ideas

What moveable / resizable graphics can bring into most of existing and new applications? Extrapolation is used as a one way instrument, but…. How about looking back and trying to imagine our PC world with the Windows system stripped of its flexibility? Can you imagine a version of Windows where the system (instead of you) decides what and where to show? That’s what we have now on the inner level, inside the applications. I am absolutely sure that after the idea of moveable / resizable graphics spreads, our modern-day fixed applications will look like DOS applications do to us today.

I started to design on the basis of moveable graphics very complicated applications for scientific projects, and from time to time there happened some funny misunderstanding between me and the people for whom I was designing such kind of applications for years. I work now in the world of moveable / resizable objects; it is something of the type, described by Roger Zelazny in the Land of Chaos; it may look like chaos to somebody from another, solid world, but there are the rules, and the inhabitants of this land know the rules and how to use them at their best. My colleagues, who were familiar only with the applications of solid, fixed design, first try to explain their requests in the standard way “Let’s put the list of functions with the data on the left and their images on the right…”, but this went only until they saw the first draft of a program. Those are very talented people, and when they received an application, which gave them much higher flexibility in their work, they understood immediately the new level at which they could work.

The most complicated applications on the basis of moveable / resizable graphics now work in such a way:

  • All the elements are moveable either individually or in groups. Graphical objects are moved by any inner point; controls are moved by any point of their borders. Forward movement is always started by the left button.
  • If it is needed, any element is made resizable. Resizing of graphical objects is usually done by any point of their border or by special points that are suitable for it; resizing of controls is done by the special areas on the borders, which are easy to find, for example, by corners. Resizing is always started by the left button.
  • If graphical element needs to be rotated, then the rotation can be started at any inner point. Rotation is always started by the right button.
  • If an element is not moveable, then it is purposely organized in such a way by the designer, but this is an exception.

The idea of moveable / resizable graphics is not simply new; it is revolutionary. But make a first step, and soon there will be no unmoveable parts in your applications.

I designed the Test_MoveGraphLibrary application to show in detail this new technique of turning objects (graphical and controls) into moveable and resizable. To make things absolutely clear, I tried to keep all the sample classes and all the forms for demonstration as simple as possible, that’s why each form works with only one particular class of moveable / resizable objects. However, in real applications you would often like to use different moveable objects together. To show that it is not a problem to do so, I put into Form_Main.cs the objects of absolutely different types that are used for explanations in other parts of this application. It’s really strange to see in one form the standard scientific plotting Y(x), houses, the colored pie chart, and a couple of colored polygons. They have nothing in common except that their design is based on the same described algorithm, and they can all live in the same space (form) without any conflicts.

One of the latest additions to this application is the Form_Medley.cs, which allows you to put together and deal with an arbitrary number of different objects of financial graphics. That is the prototype of the real system for data analysis, in which user can visualize the needed data in any form and do with the objects on the screen whatever he wants. There are no restrictions from the designer either in number of the objects, their shape or any parameter of visualization. WHAT, WHEN, and HOW to show is determined by user and ONLY by USER. There is no other way to design such a flexible and powerful system of visualization except on the basis of moveable / resizable elements.

The idea of easily turning all graphics into moveable / resizable is absolutely new. Though the whole mechanism of design of new objects with such features was made very simple, and the work with such objects is even easier, still it is an absolutely new thing. For better understanding of what this thing will allow, look into the TuneableGraphics application, which includes a lot of different samples. The idea of that application was not to develop something extraordinary for one particular area; the main idea was to demonstrate the possibility of amazing results for absolutely different objects and areas. Though I have to mention that more and more parts of both applications become similar. This happens because on design of some very interesting new parts I want not only to demonstrate them in working application (TuneableGraphics), but I want to explain how this and similar things can be done, and add them to the Test_MoveGraphLibrary project.

Maybe the area that can benefit most of all from the using of moveable / resizable graphics will be the “financial” graphics. Millions of people analyze the financial data and the effectiveness of their analysis greatly depends on how close the view of all these numerous plots is to their expectation of the best and the most informative view. Nothing can be better than the system that allows everyone to look on the plots with the best view personally for him (or her). When you analyze a lot of information on the screen, then the best system is the one that allows you to resize and reorganize the whole view at any moment without stopping the application and going somewhere else for extra tuning.

The area of scientific and engineering applications, which triggered my work on moveable / resizable graphics, can receive much more than simple benefits from this new type of plotting. By using this new plotting, programmers can redesign the most complicated systems, turn them from designer-driven into user-driven applications, and bring the analysis of the most difficult engineering and scientific problems to another level. Instead of developing tiny details within several scenarios for calculations, programmers will have to design instruments, which will allow any possible scenario or variant of research work in one or another particular scientific / engineering area. I hope that, based on this new technique, the scientific / engineering applications can be turned more from being the big powerful calculators into the research instruments.

Implementation of moveable / resizable graphics can bring into life some results, about which I wasn’t even thinking, while starting this work. One of such results is the unparallel level of forms’ customization. Up till now the best results in this area were achieved via dynamic layout, but everything there depends on the level of original design. With the new ideas, applied both to graphical objects and controls, the forms’ transformation can be done by any user according with his own preferences and purposes and not predetermined by the designer’s view. So the results will be the best at any moment for each particular user.

The best indication of how much these moving / resizing of objects are needed in a lot of applications came from such observation. My colleagues from our department of mathematical modeling use several of my programs (all of them are absolutely user-driven) and some of the outside programs. They constantly switch their attention from one program to another, and now they try to move and resize the objects in all of them. Certainly, the outside programs will not allow to do anything of such type, and the colleagues become very upset by this fact. They began to fill, how much such feature is needed, and how their work has changed with such a possibility.

To describe this new type of programs, I use all the time the term user-driven applications. There can be another term – personal applications, and I am not sure, which of them is more precise. The purpose of each application is not changed and it continues to work according with its main goal (problems of some scientific area, financial analysis, and so on), but any user can make such drastic changes that even the designer would hardly recognize his product. Never mind; the application works, it solves the problems, for which it was designed, and it takes exactly the view, which user prefers. That is the main thing. Application is personalized.

The consequences of using the moveable / resizable elements are definitely much more important than simply adding a useful feature to existing graphics. And it is nearly impossible to estimate all these consequences without starting to use such elements and writing the programs, which will especially benefit on such features. To estimate the difference between a new feature and the consequences of using it, let’s look at another one (from my point of view very similar) situation.

We are living in the houses that are fixed on some pieces of property and have fixed outside sizes and inner planning. Some changes can be made from time to time, but they need a lot of efforts and money. And in cases when we really need bigger or smaller house, we are usually forced to relocate, which is not always the best thing to do but always have a lot of sociological consequences (discussion of them is not the purpose of this paper.) Now there is the new unknown company in the market, which is selling the new adjustable buildings, advertising that WITHOUT ANY EXTRA COST you can receive (at the same price!) the house which you can stretch (or shrink, if you want some extra place around) at any moment. Just touch the wall with the wand and tell it to move. (Again, let’s not discuss here, what the solid well known constructors would like to do with the new developer. This is not the theme for discussion in programming.) I am absolutely sure that at the beginning 110 percent of the people would be suspicious. The ordinary questions would be “And what is going to happen to all the pipes and wires?”, “If I want to move the wall, what am I going to do with my flowerbed?” The explanation of that construction company, that everything is going to be fine, exactly what they want, will not lessen the suspicions. But the point that I want to mention here is not the benefits of the family, who would be courageous enough to sign the contract with the new developer. The benefits would be certainly huge, but the consequences would be much wider. Just try to think about it for a minute. (This is certainly out of this paper’s discussion.) If we would have such construction, it would change our whole life, the life of the society as a whole. The consequences are definitely much more important than the mechanism that allowed to achieve them.

I think that the design of applications on the basis of movable / resizable elements can change a lot in the programming.

References

  1. Orwell, G., Animal farm, 1945.
  2. Hawking, S. The universe in a nutshell. Bantam Press, 2001.
  3. Perrault, C., Cendrillon, 1697.
  4. Petzold C. Programming Microsoft Windows Forms. Microsoft Press, 2006.

Programs and Documents

Several programs and documents are designed for better explanation of moveable and resizable graphics and its use.

Moveable_Resizable_Objects.doc The detailed description of the design and use of moveable / resizable objects; it is the current document. The explanation is based on the samples and codes from the Test_MoveGraphLibrary project.
Applications_on_MoveableResizable_elements.doc An article about the design of applications on moveable / resizable elements (both graphical and controls) and new possibilities of forms’ customization.
NewDesignParadigm.doc A short article about the ideas and consequences of using moveable / resizable graphics in complicated programs; the text mostly consists of the abstracts from the Moveable_Resizable_....
Test_MoveGraphLibrary.zip Contains the whole project from Visual Studio with a lot of samples and useful code. The source files are written in C#; all the samples in the Moveable_Resizable_Objects.doc are from this project. If you want only to run this application, then two files are needed: Test_MoveGraphLibrary.exe and MoveGraphLibrary.dll.
MoveGraphLibrary.dll The library.
MoveGraphLibrary_Classes.doc Description of the classes, included into the MoveGraphLibrary.dll.
MoveGraphLibrary_OlderClasses.doc Description of the older classes, still included into MoveGraphLibrary.dll.
MoveGraphLibrary_Graphics.doc Description of plotting, implemented in the MoveGraphLibrary.dll; description of all the tuning dialogs, which are also included into this library.
TuneableGraphics.exe This application demonstrates the moveable / resizable objects from absolutely different areas.
TuneableGraphics_Description.doc Description of the TuneableGraphics application.

All files are available at www.SourceForge.net in the project MoveableGraphics (names are case sensitive there!). The files are renewed from time to time (usually every month).

List of forms in the Test_MoveGraphLibrary Application

Form Purpose, description Figure Chapter

Form_AddChatoyantPolygon

Use of the CommentedControl class.

29

From bricks to blocks

Form_ApartmentHouse

Explanation of the simple covers.

7

Cover samples

Form_BarCharts

Sample of a complicated financial graphics.

 

 

Form_BoardsAndBalls

The first sample with synchronous and individual movements of the parts. The first use of the IntoMover() and RenewMover() methods.

17

Complicated moveable objects

Form_Calculator

The changes of the forms, consisting exclusively of the controls.

43 - 45

More than customization

Form_ChatoyantPolygons

Cover uses all three types of nodes. All possible types of changes: moving, scaling, reconfiguring, and rotation.

11

Cover samples

Form_CircleInRotation

An addition of rotation to the circle with the N-node cover.

15

Rotation

Form_CombiRegularPolygons

Changing of the cover for the same objects, while an application is running.

 

Cover samples

Form_DataSelection

Demonstration of the Group objects for rearranging of the forms.

46

More than customization

Form_DefineNewBarChart

 

 

 

Form_DefineNewPieChart

 

 

 

Form_DefineNewRing

 

 

 

Form_DorothyHouse

Another sample of rotation.

16

Rotation

Form_GraphsAndComments

How the complicated scientific/engineering applications must be organized.

27

Complicated applications

Form_InfinitiveLoop

Covers, based on a set of points.

2

Use of standard covers

Form_LinkedElements

Use of LinkedRectangles and Group classes.

 

 

Form_Main

Different moveable objects in one form.

 

 

Form_ManyMovers

Use of many Movers in one form.

50

One, two, many… Movers

Form_ManyPlots

Real scientific application. Labels to the comments in rotation. The system of objects’ identification.

25

Y(x) plots and more

Form_Medley

A sample of the complicated financial application.

28

Complicated applications

Form_MultiScalePlot

Use of multiple scales with the MSPlot object.

 

 

Form_NNodeCovers

Explanation of the N-node covers.

12

N-node covers

Form_NodeShapes

The first introduction of the nodes.

1

Nodes and the simplest covers

Form_OneMovablePlot

First sample of the moveable MSPlot object. Complicated case of the IntoMover() method.

24

Y(x) plots and more

Form_PanelsAndGroups

Different cases of groups’ organization: resizable, nonresizable, moveable by any inner point.

31 - 37

From bricks to blocks

Form_PerforatedPolygons

Use of the DependedFrame objects.

 

 

Form_PersonalInformation

Demonstration of the form, based on CommentedControl objects.

47

More than customization

Form_PersonalInfo_DependentFrame

Demonstration of the form, based on DependentFrame objects.

48

More than customization

Form_PersonalInfo_LTP

Demonstration of the form, based on CommentedControlLTP objects.

47

More than customization

Form_PieCharts

Work with any number of the PieChart objects.

 

 

Form_Polyline

Explanation of compensation for rotation on the sample of the Polyline class.

14

Rotation

Form_Profiles

A link between two different classes of moveable objects; one simply resides on another.

26

Y(x) plots and more

Form_RectangleControlCase

Covers for the controls; FramedControl class.

6

Use of standard covers

Form_RectangleGeneralCase

Covers, based on Rectangle.

5

Use of standard covers

Form_RegPolyWithComments

Another sample of the linked parts, involved in different movements.

19

Complicated moveable objects

Form_RegularPolygons

Different covers for different resizing of the same objects.

8,9,10

Cover samples

Form_RingSets

Work with any number of the RingSets objects.

 

 

Form_SpottedText

Different movements of the objects, belonging to the same class.

21, 22

Complicated moveable objects

Form_TextSamples

Explanation of the TextMR class.

13

Rotation

Form_Town

Design your own town.

 

 

Form_UnmovablePlots

Unmoveable plots in the form with the working Mover.

23

Y(x) plots and more

* Together this article and the related project Test_MoveGraphLibrary give the most detailed explanation of the algorithm, which allows to turn any screen object into moveable / resizable, and show a lot of samples that explain the use of this revolutionary technique in different areas of programming. There are two much smaller articles “Forms’ (dialogues’) customization, based on moveable / resizable elements” and “How to move, resize, and reconfigure objects in the working applications”. The projects for those two articles use, to high extent, the codes from the big one.

* In reality, whatever you see at the screen is a graphical object, but as was declared in the famous book [1] decades ago: “All animals are equal, but some animals are more equal than others”. Controls are those “more equal” elements on the screen, and their behaviour is absolutely different from the ordinary graphical objects.

* Earlier versions of the design used mostly small areas for resizing (reconfiguring) and the lengthy connections between these small areas for moving. On visualization, the whole system of such elements looked like a contour. As you’ll see from further explanations, now the majority of the objects can be moved by any point; the system of sensitive areas doesn’t look like contour any more, and I stopped using this term. Beginning from version 5.01, there are no more Contours and Connections; if you’ll find these words anywhere in the text, it means a remnant of an old description and an overlook on my part. Connection completely disappeared, but the strip nodes can easily perform what connections were doing in the previous versions. With the elimination of connections, Contour turned into Cover, which is the exact idea of this algorithm.

* There are very few places (forms) in this application, where I decided to strip the comments of their rotation. For example, this is done in the first form, with which this program starts. There is no need to write special code for such situation; I simply use the object of the TextM class instead of the TextMR class.

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

 
QuestionVB.NET code Pinmembertratak13-Jan-14 2:12 
GeneralMy vote 5 PinprofessionalSEO Outsourcing9-Aug-13 7:03 
GeneralRe: My vote 5 PinmemberSergeyAndreyev12-Aug-13 10:16 
GeneralMy vote of 5 [modified] Pinmembertrne21-May-12 10:00 
GeneralRe: My vote of 5 PinmemberSergeyAndreyev12-Aug-13 10:08 
GeneralMy vote of 5 PinmemberMember 342587316-Mar-11 5:40 
GeneralRe: My vote of 5 PinmemberSergeyAndreyev16-Mar-11 6:30 
GeneralAn Excellent library PinmemberTSKNaidu24-Jan-11 21:21 
GeneralRe: An Excellent library PinmemberSergeyAndreyev25-Jan-11 4:09 
GeneralRe: An Excellent library PinmemberTSKNaidu25-Jan-11 6:20 
GeneralRe: An Excellent library PinmemberSergeyAndreyev25-Jan-11 8:07 
GeneralRe: An Excellent library PinmemberTSKNaidu25-Jan-11 19:35 
Hello! Sergey,
 
Thanks for accepting our request to compile your library to work with .net Compact Framework.
 
We are not that meticulous like you in writing the documentation! But, please find below the steps required to compile your library to work with .net CF versions 2.0 or 3.5.
 
1. Using the utility 'CheckAsm', we found that your library is referencing the following .net assemblies:
A) System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeytoken=b77a5c561934e089
B) System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
C) System, Version=2.0.0.0., Culture=neutral, PublicKeyToken=b77a5c561934e089
 
2. The above .net full framework assemblies need to be replaced either with the .net CF version 2.0 or with the version 3.5 assemblies.
2.1. To do this, in your library project in Visual Studio 2005, remove the references to the above assemblies.
2.2. Add references to the .net CF assemblies for either version 2.0 or version 3.5.
2.3. On a Windows OS, these .net CF files are typically installed in the folder 'C:\Program Files\Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE' for version 2.0 or 'C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE' for version 3.5.
2.4. Build the library for either v2.0 or v3.5.
2.5. If the framework methods being used in your library are supported in the referenced version of .net assembly, the build will be successful.
2.6. If the build fails due to unsupported methods, it is going to be a lot of work.
2.7. For example, one of the methods (CoverNode) in your library uses the framework struct 'System.Drawing.PointF', which is not supported in .net CF. In this case, you either end up maintaining two separate versions of your library in the source code control system or do a conditional compilation based on the targeted platform. In either case, it is going to be a lot of work.
 
3. Conditional compilation is done as follows (steps use Visual Studio 2005 as reference):
3.1. Set the conditional compilation symbol, for e.g.,DOTNETFF, in the property page 'Project->[application]Properties->Build->Conditional compilation symbols'.
3.2. Use the #if and #endif preprocessor directives to "wrap" the code that needs to be conditionally compiled.
3.3. Here is a brief example of wrapping of the code that gets compiled only for .net full framework:
#if DOTNETFF
public void GetMouseCoordinates(PointF pntM)
{
//method implementation
}
#endif

3.4. The above piece of code is conditionally compiled only when the symbol DOTNETFF is either set in the property page as noted in section 3.1 or by using a #define directive while compiling without Visual Studio 2005 IDE.
 

Thanks again for your perseverance and willingness to consider our request.
 
best regards.
QuestionResizableRectangle minimum size (25,25)? PinmemberMahmoud Reda Seireg28-Aug-10 14:34 
AnswerRe: ResizableRectangle minimum size (25,25)? PinmemberSergeyAndreyev29-Aug-10 6:57 
GeneralMy vote of 1 Pinmemberdl4gbe8-Aug-10 6:07 
GeneralMy Vote of 1 Pinmemberdl4gbe8-Aug-10 6:05 
GeneralRe: My Vote of 1 PinmemberSergeyAndreyev8-Aug-10 11:20 
GeneralЗдравствуйте, Андрей. PinmemberEvgeniy Stepanow19-May-10 10:12 
GeneralRe: Здравствуйте, Андрей. PinmemberSergeyAndreyev20-May-10 0:42 
GeneralMy vote of 1 PinmemberNoName_ark27-Dec-09 0:43 
GeneralRe: My vote of 1 PinmemberSergeyAndreyev30-Dec-09 2:20 
GeneralGimmick PinmemberMr.PoorEnglish17-Nov-09 11:23 
GeneralRe: Gimmick PinmemberSergeyAndreyev22-Nov-09 4:22 
GeneralRe: Gimmick PinmemberMr.PoorEnglish22-Nov-09 6:19 
Questionanimation of moving of object at mouse click event? Pinmemberqwertyuioo17-Nov-09 5:19 

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
Web03 | 2.8.140709.1 | Last Updated 9 Oct 2009
Article Copyright 2009 by SergeyAndreyev
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid