People communicate with computers on two different levels.

On the upper level you see a very flexible system of windows: you can move them, resize, overlap, or put side by side. However, starting an application immediately removes all the flexibility and you can work only inside the scenario developed by the designer of the program; you cannot move graphics or controls nor resize them. I have designed an extremely powerful mechanism that makes graphical objects moveable and resizable. My technique not only significantly improves existing applications, but it takes them to another level. This article explains in detail the construction and use of moveable and resizable graphical objects.

When you switch on your PC, you usually sink into the world of rectangular windows. You can easily move 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. Consider this 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 which users can click on to move a window, and borders that allow users to resize a window. Being moveable and resizable are standard features of all the windows and you eliminate these features only for special purposes.

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 an 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 you have only one type of object, 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.

Controls and Graphical Objects

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 you can move or resize controls, but that you can organize moving and resizing without problems.

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, a programmer can make it look as if a graphic is resizable as one resizes a form (dialog). Simply paint on top of a panel and make the panel the 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 the application. Another solution for resizing of rectangular graphical objects is the use of bitmap operations, but in most cases you can’t use this solution 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-you can only use them with 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 you 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.

No one has actually declared these axioms 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 work like this: you are free to move around the city or country, but somebody will tell 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. You could fairly easily do this trick 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, but in some situations it works.

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 application 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 graphics 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 graphics, 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 having non-moveable graphics-everyone must work with designer-driven applications.

“In science, finding the right formulation of a problem is often the key to solving it…” [Hawking, S. The Universe in a Nutshell. Bantam Books, 2001]. When I started to work on the problem of making graphics moveable and resizable, I began with the analysis of the features that 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, 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 graphics separately from the consequences of using such graphics. Classes and algorithms, used for the design, may be different, but if there is any form of moveable and resizable graphics, then it is the base for an absolutely new paradigm-user-driven applications.
  • It is a theoretical idea of a “nice to have” type. At www.SourceForge.net in the MoveableGraphics project you’ll find a whole package of applications and documents that help to explain moveable/resizable graphics.

Design of Moveable Graphics

In part 1 of the article I describe the ideas of organizing moveable/resizable graphics. I will cover applying these ideas to some complicated cases and also the consequences of using such graphics, for example, for forms’ customization in part 2.

Basic Requirements

As a programmer and a designer of very complicated systems, I would prefer that Visual Studio already had the ability to create moveable/resizable graphics. Unfortunately it isn’t there (YET!), so let me describe an imaginary scenario of what I really need and would like to have:

  • I need an easy way to declare any object in the form (dialog) moveable and resizable.
  • Easy doesn’t mean primitive. The configuration of my objects can be at any level of complexity; changing the configuration may 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.
  • These features-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 and I can use it while working with the application. 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.
  • The new classes must be easily declared moveable and resizable and it must be easy to add these features to already existing classes; just touch them (or touch the keyboard several times) and the objects will become moveable.
  • Programmers must be able to add these new features when they want to; adding these features shouldn’t be an “all or nothing” case. Programmers can use objects of the same classes with or without these new features in order to start using them piece by piece and, in doing so, they can see what they will bring into existing complicated applications.
  • Using moveable graphics must be as simple as with the windows on the upper level: press and move, press and reconfigure, and even press and rotate, which is not organized for windows, but can be extremely useful for many graphical objects. And if it is useful, make it easy to organize the graphics 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 this article about the future of programming in 2025. The biggest secret is that all these things are designed and are working now; a programmer can use these features now.

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 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#. Test_MoveGraphLibrary contains all of the code and samples from the application.

Basic Idea-Contour Presentation

There are two ways to add moveable/resizable features to an object: either 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 that declares three crucial methods. (Closer to the end of this article, after describing all standard techniques and some special cases, I’ll write about the back door around the must be case.)

public abstract class GraphicalObject
{
  public abstract void DefineContour ();
  public abstract void Move (int cx, int cy);
  public abstract bool MoveContourPoint (int i, 
                 int cx, int cy, Point ptMouse, 
                 MouseButtons catcher);
}

My idea of making graphical objects moveable and resizable is based on contour presentation; it is the core of the whole design and any object that is going to be involved in moving and/or resizing must have a contour. For graphical objects the contour is organized in the DefineContour() method.

Any graphical object that needs to be resizable and/or moveable will have a contour consisting of nodes and their connections; these two types of contour elements are used for different purposes. A contour does not duplicate the shape of the object; on rare occasions it can, but that is really rare. A contour looks more like a skeleton, which allows the flesh around it to move as a single body, but for many objects contour is placed outside, and for these objects, the contour looks like a frame; however, a contour may consist of the disjoint sets of nodes and connections and it will still be a single contour. There are many possibilities because contours were designed to cover any possible scenario of any real or imaginary object finding its way into the programming world.

Nodes (ContourApex class) are used as sensitive areas at the ends of connections; some nodes can be moved separately, thus providing reconfiguration or resizing of the object. The MoveContourPoint() method must include the code only for nodes that can be moved individually. Each node has its sensitive area, though for special cases, when this node must be excluded from the individual movement, this area will be null. When the node’s area is not null and the mouse cursor is moving across this area, you can change the shape of the cursor to indicate that the user can grab and move the node. There are some conformity rules for possible movements of the object and the shape the mouse cursor can become, but there is some flexibility in defining these; I’ll talk about them in describing the design of the real contour. Sizes and forms of the sensitive nodes can vary; the simplicity of using this moveable graphics depends on the designer’s decision in organizing nodes.

Connections (class ContourConnection) are used for grabbing and moving the whole object; this movement is described in the Move() method. Each connection has its own sensitive area. When the mouse cursor is moving across this area, you can change the shape of the cursor to indicate that the entire object can be grabbed and moved. The form of the sensitive area is defined by the ratio between the length of connection and the width of the area. If the length is much bigger than the width, the area will look like a strip; if the width is much bigger than the length, it will be a circle; the intermediate variants have a “sausage” shape.

Very often the entire area of the graphical object is used for some mouse-generated events that are aimed at starting different actions with the object or different changes. The moving and resizing implementation is based on mouse-generated events at nodes and connections, thus the addition of contour may be the cause of the conflict between the old commands and new requirements. To minimize this conflict, nodes are usually small and connections are thin, though there are special situations, when sensitive areas are designed to cover maximum size of the object.

Nodes and connections, as they were described, can be the construction elements of the widest variety of contours; each design depends on the goal of the particular graphical object. Here’s how all this works in the real sample.

SimpleHouse-A Classical Moveable and Resizable Object

There was a time when you could take a pen and a sheet of paper and design your own house. No restrictions, just imagination. (And no bills, repairs, taxes-I want to go back!) If you liked your first house, you could put another one nearby; you could construct a street, a village, a town (if you had enough space on the paper). There was only one problem: if you drew anything wrong, you had to find a way to erase it or you had to abandon the whole project and start a new one on a new sheet of paper. Not a bad idea, but it’s a pity to abandon the nice village just because one cottage is the wrong color. So I am going to design moveable and resizable buildings and all their colors will be changeable.

For this exercise, I am going to design the class SimpleHouse: this is part of the Test_MoveGraphLibrary project. You can find the code for this class in the SimpleHouse.cs file and objects for this class are used in Form_Houses.cs (menu position Houses). I call these houses simple, because the basic form of the house will be a rectangle plus a triangular roof (Figure 1); the majority of real houses falls under this description. Houses can be wide or narrow (at least two windows), high or low (at least one floor). The roof can be also high or low and its top can move to one side or another. I decided that a good place for the house number will be somewhere on the roof; don’t worry, the postman in the town will be Astrid Lindgren’s Carlson; if you don’t know-this nice guy can fly:

Figure 1:  SimpleHouse object.
Figure 1: SimpleHouse object.
public class SimpleHouse : GraphicalObject
{
  int nNumber;
  Font font;
  Rectangle rcHouse;
  Point ptTop;
  int roomSize;

Before writing any code, you must decide about the placement and type of the contour. This is a real game for kids, so the spots for resizing and moving (nodes and connections) must be absolutely obvious without any extra visualization. Corners of the house plus the point at the top of the roof look like good places to click them and move, thus changing the sizes of the house, so these five points look like ideal places for the nodes. The borders of the house plus the edges of the roof look like good places to grab and move the whole house to a new place (relocation is really easy), so these lines will be the connections. Usually there can be several different variants of contour for any object and later I’ll show some samples. In the case of SimpleHouse, there can be other variants, but once I decide where the nodes and connections go, then you can define the contour (Listing 1).

Designing the contour consists of three steps:

Initialization of any new ContourApex uses five parameters:

  •     int nVal: Each node of the contour must have a unique number; the numbers must be from the range [0, nodes-1]; the order of numbers doesn’t matter. I began from the top-left corner, went clockwise for the other corners, and then added the rooftop. All the numbers must be different, as later I use them for initialization of connections.
    
  •     Point ptReal: This is a real point on the screen; in my sample it is either a corner of the house or the rooftop.
    
  •     Size szRealToSense: This is a shift from the real point to the middle of the node associated with this point. In the case of these houses, I don’t need to move the nodes anywhere from the real points because these nodes will be not shown at all; however, there are situations when it is very helpful to move the node slightly aside and not close the real image of the object. The decision of moving nodes aside from real points is used, for example, in case of very complicated and informative engineering plotting, where I don’t want the contour to close even a tiny part of the plot.
    
  •     MovementFreedom mvt: Possible sole movements of the node.
    
  •     Cursor cursorShape: Cursor shape above the node.
    

Regardless of what you put as the fourth parameter for the particular ContourApex, all nodes will move around when a user moves the whole object; MovementFreedom parameter describes only the opportunities for separate movements of this node when you want it (or not) to participate in reconfiguration. The possible values are:

public enum MovementFreedom {
    None,    // is not used for reconfiguration
    NS,    // can move only Up or Down
    WE,    // can move only Left or Right
    Any };    // can move in all directions

The last parameter in the initialization of ContourApex defines the shape of the mouse cursor when the mouse is above the node. There are standard expectations, for example when the cursor has the form of Cursors.SizeWE, the object underneath can be moved only left or right-don’t create confusion by setting a very strange pair of the last two parameters. At the same time I didn’t want to put strict rules on the cursor’s shape based on possible movement, so you can decide what to do with this fifth parameter.

The type of contour I have created for SimpleHouse is the most common case of the contours; it has several nodes and several lengthy enough connections between them. There can be special and very useful types of contours, which differ from this standard one; I’ll describe them later in detail.

Note: Though the whole idea of moveable and resizable graphics is based on contour presentation, the DefineContour() method is usually the only place where you will have to think about the contour! The rule is: organize the contour and forget about it. There is one exception for this rule; I’ll write about it a bit further when describing individual movements of the nodes.

Move (cx, cy) is the method for moving the whole object forward by a number of pixels, which are 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 (size). While moving the whole object, I am not changing its size, so I only have to change the position of these basic elements. In the case of the SimpleHouse, I have two such elements: Rectangle rcHouse and Point ptTop:

public override void Move (int cx, int cy)
{
    rcHouse .X += cx;
    rcHouse .Y += cy;
    ptTop += new Size (cx, cy);
}

MoveContourPoint (i, cx, cy, ptMouse, catcher) is the method for moving the individual node. The method returns a bool value, indicating whether the required movement is allowed; in 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 DefineContour() inside this method, and then it doesn’t matter what value is returned from MoveContourPoint(). This may happen even for forward movement, when movement of one node affects the relocation of other nodes, and it usually happens with rotation, when all nodes must be relocated. This is the exception of the previously mentioned rule, that contour is not even thought about anywhere outside the method of its definition.

Method MoveContourPoint() has five parameters, but not all of them are used each time:

  •     int i: Identification number of the node-the same number that was used in the DefineContour() method to identify this node; if the node is not involved in separate movements, then you don’t need to mention it in the MoveContourPoint() method.
    
  •     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 in the application logic the object can be grabbed by any mouse button, then simply ignore this parameter; if the move is allowed by only one button, then use this parameter; in case the node can be involved in two types of movement (forward movement and rotation), this is an extremely useful parameter to distinguish between them.
    

Usually the method for an individual node’s movement will be the longest of all three methods; however, there are interesting situations when the method you use will consist exactly of one line. Because this method must include the code for moving each node (for the majority of objects), it can be long, but relatively uncomplicated, as more often than not the code for different nodes is partly the same. For SimpleHouse, I will only discuss parts of the code for two different nodes: the top-left corner (Listing 2) and the rooftop. In the full code of this method you can see that the code for two corners on each side of the house is partly the same.

As I mentioned before, here you are doing nothing with the contour itself, but only checking the possibility of proposed movements of the real points associated with the nodes. For the top-left corner of the house I am:

  • Comparing the proposed new height of the house with the minimum allowed height; if the top of the house is moving, then I also have to move the rooftop.
  • Comparing the proposed new width of the house with the minimum allowed width; if the width of the house is going to be changed, then I have to determine the new position of the rooftop; I am trying to keep it in relatively the same position, but the rooftop cannot be closer to any side than the predefined minRoofSide.

The piece of code for the rooftop (identification node 4) is similar: I have to compare the proposed new rooftop position with the minimum allowed roof height and with the minimum allowed distances to the sides of the house:

else if (i == 4)     // Rooftop
{
  if (ptTop .Y + cy <= rcHouse .Top - minRoofH)
  {        // compare with minumum roof height
    ptTop .Y += cy;
    bRet = true;
  }
  if (rcHouse.Left +minRoofSide <= ptTop.X + cx &&
      ptTop.X + cx <= rcHouse.Right - minRoofSide)
  {
    ptTop .X += cx;
    bRet = true;
  }
}

I’ve just described the typical case for organizing a moveable and resizable graphical object. First I designed the contour consisting of several nodes and their connections, and then developed methods for moving the whole object and resizing it. Now that you can move and resize SimpleHouse objects I will show you how to organize the whole process.

And the Boss Is Mover

The easiest way to move any object around the screen is to drag it, so for the whole process you only use three mouse events: MouseDown, MouseMove, and MouseUp.

From the user’s point of view, the whole process must be simple: press, move (or rotate), and release. And this is the only way users are going to tolerate this process; any additional complication will not be acceptable. The designer who is planning to include moveable/resizable objects into his application certainly understands that he must store, analyze, and process all the following information for each object: positions of the nodes and connections, sensitive areas, whether they overlap, order of drawing, type of movement, etc. At the beginning I formulated the basic requirements to the whole moving/resizing process and wished for a class (better in Visual Studio, but unfortunately it’s not there) that would provide all the desired features. Such a class exists! It is ready to do everything. Its only requirement is that the object involved in moving and resizing is either derived from GraphicalObject or is a control (I’ll write about this case in part 2):

public class Mover

The name declares only a part of what this class is capable of; in addition to moving it is also doing all the resizing and giving information about all possible things associated with the process. The Mover class is described in MoveGraphLibrary_Classes.doc.

Now I’ll show you how to move from a single house to the town from the file Form_Houses.cs. I want all the houses in the town to be moveable and resizable, so I designed them as derived from GraphicalObject:

public class SimpleHouse : GraphicalObject

Next I declare the company responsible for all movements and reconfiguring of the houses and also starting the construction of the town:

Mover Movers = new Mover ();
List<SimpleHouse> Town = new List<SimpleHouse> ();

The new house will pop up in the town when you click the New house button:

private void Click_btnNewHouse (object sender,
                                EventArgs e)
{
  SimpleHouse newhouse = new SimpleHouse (nNew, …,
                    rc);  // initialize new house
  Town .Insert (0, newhouse); 
          // insert the new house into the List<>
  Movers .Add (newhouse); 
            // register the new house as moveable
  Invalidate ();
}

I slightly shortened the real code here to show only the lines that are really significant:

  • Initialize the object of the new house.
  • Insert the new house into the official town’s list of buildings. I want every new house to be shown on top of all previous, so I always insert the new house at position zero, and the painting is going from the opposite end (look at the Paint() method).
  • Register the new house as moveable. This is mandatory if you want the house to become moveable/resizable! The contour gives an object the ability to be involved in the moving/resizing process; this line adds the object to the list of moveable/resizable objects:
Movers .Add (newhouse);

Several important things about Mover:

  • Mover works only with the objects that it was asked to take care of. Mover has its own List of these objects; you, as a programmer, have access to the elements of this List via standard indexing or you can use standard List<> methods (look into MoveGraphLibrary_Classes.doc for the description of implemented methods).
  • Mover doesn’t know anything about the real objects; Mover works only with the contours of the objects, which were included in its list. It is the programmer’s responsibility to make parallel changes in the outer world (there can be several lists, arrays, or anything else) and the List inside Mover. Consider the situation if Mover tries to take care of an object that a user has already deleted from the real world (from the form). You don’t have to declare all objects on the form as moveable/resizable; as the programmer you must decide to give this feature to all, some, or none of the objects, so you have to check that Mover works on a correct List. The developed set of methods makes any needed task easy (delete, add, insert).

You can see the real power of Mover in the code of three mouse events. On the MouseDown event I try to grab the house either for moving or for reconfiguring; I decided that you can only start this process with the left mouse button-right-click is used for other things:

private void OnMouseDown (object sender,
                          MouseEventArgs mea)
{
  if (mea .Button == MouseButtons .Left)
  {                 // start moving/resizing
    Movers .CatchMover (mea .Location); 
  }
  else if (mea .Button == MouseButtons .Right) {
    …
    ContextMenuStrip = contextMenuOnHouse;
  }
  if (!Movers .MoverCaught)
  {    // if not clicked for moving, bring to top
    BringToTop (mea .Location);
  }
}

It doesn’t matter how many moveable/resizable objects are in the form; you only need one line to start the moving/resizing process:

Movers .CatchMover (mea .Location);

This is the only piece of code in the OnMouseDown() method that is really important for starting any moving/resizing process; all other lines of code in this method simply do additional things such as opening the context menu or bringing to the top the house that was clicked on.

The MouseUp event will finish any moving/resizing process and release any previously grabbed object. The code is always extremely short:

private void OnMouseUp (object sender,
                        MouseEventArgs e)
{
    Movers .ReleaseMover ();
}

The third event-MouseMove-has to the real moving/resizing, but the code is not complicated even here:

private void OnMouseMove (object sender,
                          MouseEventArgs mea)
{
    Cursor cursor = Cursors .Default;
    foreach (SimpleHouse house in Town) {
        if (house .Inside (mea .Location)) {
            Cursor .Current = Cursors .Hand;
            break;
        }
    }
    Movers .MovingMover (mea .Location); 
                        // real moving/resizing
    if (Movers .MoverCaught) {
        Invalidate ();
    }
}

The first half of this method-the bigger part-has nothing to do with moving and only changes the cursor’s shape if the mouse is over any house. The real moving occurs in one line of code:

Movers .MovingMover (mea .Location); 

Your must check the Movers situation. If the Mover signals that it has caught some object from its list, it triggers a repainting. To avoid screen flicker, don’t forget to switch ON double-buffering in the form; it has nothing to do with the design of moveable/resizable graphics, but it is simply a nice feature from Visual Studio.

And that is all of your construction-You should now have a town of moveable and resizable houses. In the real application you’ll find some additional and very useful things including the ability to change all the colors, save the image to the Clipboard for printing, save the picture into a file and restore it from the file. The main work of organizing the SimpleHouse as a moveable/resizable object is done and working.

SimpleHouse has a classical contour of several nodes with lengthy connections between them, but you’ll soon encounter other interesting and useful situations that demand special contour design. This article looks at some of them.

Case A: Same Nodes-Different Connections

Suppose you want an object to be resizable in only one direction, such as a scale that the user can only resize horizontally. For purposes of this example, the contour must be out of the scale’s image (Figure 2). If you needed an absolutely nonresizable but moveable scale, it would be enough to have four nodes close to the corners of the object; however, because of the request for horizontal resizing there needs to be two additional nodes on the left and right sides that differ from the other four.

Figure 2:  Scale-an object that you can resizable only horizontally.
Figure 2: Scale-an object that you can resizable only horizontally.

Here are the variables:

int cxL, cxR, cyT, cyB;       // all four borders
              // of the scale's rectangular area
int cyM;      // middle of the sides
int shift;    // the distance of moving the nodes
              // outside the scale's rectangle

You can organize the array of nodes like so:

apexes = new ContourApex [6] { 
  new ContourApex (0, new Point (cxL, cyT),
                   new Size (-shift, -shift),
         MovementFreedom.None, Cursors.SizeAll), 
  new ContourApex (1, new Point (cxR, cyT),
                   new Size ( shift, -shift), 
         MovementFreedom.None, Cursors.SizeAll), 
  new ContourApex (2, new Point (cxR, cyM),
                   new Size ( shift, 0), 
         MovementFreedom.WE, Cursors.SizeWE), 
  new ContourApex (3, new Point (cxR, cyB),
                   new Size ( shift, shift), 
         MovementFreedom.None, Cursors.SizeAll), 
  new ContourApex (4, new Point (cxL, cyB),
                   new Size (-shift, shift), 
         MovementFreedom.None, Cursors.SizeAll), 
  new ContourApex (5, new Point (cxL, cyM),
                   new Size (-shift, 0), 
         MovementFreedom.WE, Cursors.SizeWE) 
};

I numbered the nodes from the top-left corner going clockwise. For the four corner nodes (these are the nodes with MovementFreedom.None), the last parameter (cursor’s shape) doesn’t matter. Also, while showing the contour, these nodes will not display at all-as if they do not exist (Figure 2). Still you need these nodes since the connections can go only between the nodes. If you want to have the contour around the object, you have to use all six nodes.

The contour’s initialization is based on the array of nodes:

contour = new Contour (apexes);

You may be surprised to see that while organizing this contour I didn’t organize the array of connections. Instead, I used the Contour constructor with one parameter. In this case, the array of connections is organized automatically by linking each node in the array with the next one, and then linking the last node with the first.

The contour you designed and the scale is shown on Figure 2. If you want to have the same horizontally resizable scale, but without the requirement for the contour to be out of the scale, you can construct another contour using the same two small nodes in the middles of the sides-the single connection between them can go right through the middle of the scale:

apexes = new ContourApex [2] { 
   new ContourApex (0, new Point (cxR, cyM),
                       new Size (shift, 0), 
           MovementFreedom .WE, Cursors .SizeWE),
   new ContourApex (1, new Point (cxL, cyM),
                       new Size (-shift, 0), 
           MovementFreedom .WE, Cursors .SizeWE)
};

For resizing the object these two solutions are absolutely identical: there are two small nodes on the sides and each of them can be grabbed and moved left or right. The code for the Move() method will be the same, as it is the same object. The MoveContourPoint() method will also be the same (the only difference is in the used identification numbers). You only need to write the code for the nodes that are involved in resizing.

The difference in using identical scales with different contours becomes apparent in the process of moving the whole object. In the first case (contour around the object), the user can only move the scale by grabbing the areas close to the border of the object. In the second case (contour through the middle of the scale), the area for grabbing the object will be a thin horizontal strip somewhere in the middle. I say somewhere because, based on the changing texts and possibility of using different fonts, this strip will change its place. It is difficult to find these nodes in the middle of the sides without special visualization; the better solution would be to place the nodes at the ends of the main line. Then at least there would be no problems in finding them.

When the scale’s main line is always visible, the best solution is to put the nodes at the ends of the main line. In many cases, however, you can show the scale without the main line: just ticks and text. For a user to find the nodes at the ends of the invisible line would be a really tricky thing. What I want to emphasize is that there are always different ways to organize contours. You must decide the best and most obvious solution for the users who are going to work with the objects.

Both samples in this case still belong to the most common case of contours with several small nodes and connection(s) between them. Other special cases of contours and their design will make some impact on the code for the MoveContourPoint() method.

Case B: Moveable, But not Resizable

You use moveable, but not resizable objects, very often. The described system of nodes and connections can produce different contours for such objects, all of which will work perfectly-the designer must choose one of the solutions. Let’s look at the square shape objects; you’ll find the description of these objects in the TwoNodesSquare.cs file and they are used in Form_ColoredSquares.cs.

As you can assume from the name of the file (TwoNodesSquare.cs), the contour is based on two standard, small nodes. However, you can forget about the sizes of the nodes(!), because they won’t be used at all; declare the nodes as not used for movement so they will be automatically set to null:

public override void DefineContour ()
{
  Point ptM = Auxi_Geometry .Middle (rc); 
                     // middle point of Rectangle
  ContourApex [] apexes = new ContourApex [2] { 
    new ContourApex (0, new Point (ptM .X - 1,
                        ptM .Y), new Size (0, 0), 
        MovementFreedom.None, Cursors.SizeAll), 
    new ContourApex (1, new Point (ptM .X + 1,
                        ptM .Y), new Size (0, 0), 
        MovementFreedom.None, Cursors.SizeAll) 
  };
  contour = new Contour (apexes);
  contour .LineSensitivity = 
        Math .Min (rc .Width, rc .Height) / 2 - 1;
}

Place these two nodes in the middle of the square next to each other. Neither of the nodes is moveable, so the method of moving nodes individually simply returns false (I mentioned previously that on some occasions this method can consist of one line). The connection between the two nodes is very short (in this case-2 pixels), but I increased the sensitivity of this connection up to the sides of the square, so the sensitive area of the connection will be very close to a circle inscribed into a square, thus it will cover nearly 80 percent of the square. I doubt that any user of the program will notice that they cannot grab the square in the small areas close to the corners; you can drag the object from anywhere else:

public override void Move (int cx, int cy)
{
  rc .X += cx;
  rc .Y += cy;
}
public override bool MoveContourPoint (   )
{
  return (false);
}

Later I added to the same object another variant of the same type of contour (null nodes, increased sensitivity of connections), but with four nodes instead of two. All four nodes are still unmovable separately, they stay far away from each other, and the sensitivity of each connection is only half as much as in the case of two nodes. However, these four nodes are located in such a way-each is halfway from the corner to the middle of the square-that the combined sensitive area covers 95 percent of the whole square. In Form_ColoredSquares.cs, you can add the new objects of the TwoNodesSquare class to the view; the number of contour nodes (two or four) will be one of the parameters while initializing the new object. Note this very interesting feature-objects of the same class may have different types of contours! This number of nodes is marked on the colored square while it is painted, so for each of the squares you’ll know the number of nodes without trying to remember them. You can define the same size squares with different types of contours and compare for yourself whether there will be a big difference in moving these graphical objects around the screen.

Case C: Another Moveable, but not Resizable

There is one more type of contour design for moveable but not resizable squares. This solution may be funnier, but it will cover the whole square:

public override void DefineContour ()
{
  ContourApex [] ca = new ContourApex [1];
  ca [0] = new ContourApex (0, ptCenter,
                            new Size (0, 0),
            MovementFreedom .Any, Cursors .Hand);
  ca [0] .SenseAreaSize = size;
                        // change the node's size
  contour = new Contour (ca, null);
}
public override void Move (int cx, int cy)
{
  ptCenter += new Size (cx, cy);
}
public override bool MoveContourPoint (int i,
                   int cx, int cy, Point ptMouse,
                   MouseButtons catcher)
{
  bool bRet = false;
  if (catcher == MouseButtons .Left)
  {
    Move (cx, cy);
    bRet = true;
  }
  return (bRet);
}

Here you have a unique contour consisting of a single node; certainly there are no connections, but the sensitive area (the size) of this single node is changed in such a way as to cover the whole square. Because there are no connections, moving the object is based on the move of this single node. If it is allowed, call the Move() method from inside the MoveContourPoint() method. The node’s sensitive area covers 100 percent of the square, so you can start moving by pressing at any inner point.

Two last cases are for moveable and not resizable square objects. Usually non-resizable objects require special design for the MoveContourPoint() method, and the code of this method will be much shorter than for resizable objects.

Case D: Different Shape of Nodes

To make this system of nodes and connections even more flexible, you can change the shape of any node from square to circle:

public override void DefineContour ()
{
  ContourApex [] ca = new ContourApex [1];
  ca [0] = new ContourApex (0, ptC,
                            new Size (0, 0),
            MovementFreedom .Any, Cursors .Hand);
  ca [0] .SenseAreaSize = nRadius;
  ca [0] .SenseAreaForm = ContourApexForm .Circle;
                       // change the node's shape
  contour = new Contour (ca, null);
}

These contours, consisting of the single circle node, are used in the RegularPolygon.cs file. Look at Form_RegularPolygons.cs to see how it works. In special case B above you saw an example of an object that could receive different types of contours; here you have an example of different objects that have the same contour. From the programmer’s point of view all these regular polygons are objects of the same type (same class), but for a user, they are absolutely different (they look different).

Case E: Rectangular Contour

The majority of objects that you deal with in a lot of applications have a rectangular shape. There is nothing special about organizing the contour for a rectangular object, as the most obvious solution is to put the contour somewhere close to the outer borders of this rectangle. The difference can be in the proximity and the possibilities for transformation that you would like to apply to this contour. Because this type of contour is widely used, there is a special constructor for it:

public Contour (Rectangle rc, ContourResize 
                resize, int shift)

The second parameter defines the possible transformations of this contour:

public enum ContourResize { None, NS, WE, Any };

The last parameter defines the outside shift from the rectangle. By using this type of initialization you can develop the contour around the scale (described in case A) like this:

public override void DefineContour ()
{
  contour = new Contour(Area, 
                           ContourResize.WE, 4);
}

Case F: Freely Moving Points

There is another widely used case: a set of nodes that can move separately in all directions and which are placed exactly at the points they are related to:

public Contour (Point [] pts, int [,] connects) or
public Contour (Point [] pts)

The first array contains all the points where the nodes will reside; the second array is for the connections. The second version of this initialization is for the set of nodes connected by the infinitive loop. For example, you would use this type of contour with all freely moving nodes in the SimpleHouse class; you can describe the same contour in a different way. Here again I’ve numbered the nodes from the top-left corner of the house going clockwise, and then added in the rooftop:

contour = new Contour (new Point [] {
       new Point (rcHouse .Left, rcHouse .Top),
       new Point (rcHouse .Right, rcHouse .Top),
       new Point (rcHouse .Right, rcHouse .Bottom),
       new Point (rcHouse .Left, rcHouse .Bottom),
                                     ptTop }, 
     new int [,] { {0, 1}, {1, 2}, {2, 3}, {3, 0},
                   {0, 4}, {4, 1} }
                      );

Case G: Resizable, but not Moveable

All the previous samples were about moveable objects, either resizable or not; however, from time to time you need an unmovable object to be resizable. If you think about this combination of features in terms of a nodes/connections presentation, then it should be obvious that the contour of the object must have a set of nodes and each node will move according to specific limitations; however, the set of connections must be empty, thus eliminating any possibility of moving the whole object:

contour = new Contour (ContourApex[] 
                       apexes, null);

This type of object is widely used in engineering applications when a user needs to quickly set the profile of some device or use a function and start the calculations that have thus defined y(x) function as a parameter. This definition of the function by several mouse clicks is so widely required that I included a special Profile class in the MoveGraphLibrary.dll. You’ll see samples of using this class in both of the demonstration applications and I’ll write about this class in part 2 of this article in the section called Y(x) Plots and More.

Contour Summary

You now know that you can use contours to make objects moveable/resizable. The diversity of objects calls for different types of contours, and here is a summary of their design:

  • A contour can consist of any number of nodes and their connections.
  • The minimum number of nodes is 1. A contour may consist of a single node without any connections.
  • A contour may consist of a set of disjoint parts; each part may consist of arbitrary connected nodes or it can be a single node.
  • Connections may exist only between nodes; there must be a node on both ends of any connection.
  • You can move nodes individually thus allowing you to reconfigure the object.
  • By grabbing any connection you can move the whole object.
  • A contour may consist of a series of connections between empty nodes. This makes an object moveable, but not resizable.
  • Each of the nodes has its own parameters. By connecting nodes of different types, for example, 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 contours may represent graphical objects and others controls or groups of controls (I’ll describe these in part 2). All contours are treated in the same way, thus allowing the user to easily change the inner view of any application.

With the combinations of nodes’ shapes, nodes’ sizes, and the sizes of sensitive areas around connections, you can organize any required contour.

This is part 1 of the article, describing the moveable/resizable graphics. Here I wrote about organizing contours that make objects moveable/resizable. I referred not only to the classical types of contours, but also to special cases that allow use of this technique with the widest variety of objects. In part 2 I’ll write about complicated cases of moveable/resizable graphics, e.g., engineering plotting and about objects involved in both forward moving and rotation. I’ll also explain how you can apply the same technique to controls and how you can base the customization of the forms on moveable/resizable objects.

Listing 1: Defining the contour for SimpleHouse

public override void DefineContour ()
{
  ContourApex [] ca = new ContourApex [5];
  ca [0] = new ContourApex (0,
            new Point (rcHouse .Left, rcHouse .Top),
            new Size (0, 0), MovementFreedom .Any, Cursors .Hand);
  ca [1] = new ContourApex (1,
            new Point (rcHouse .Right, rcHouse .Top),
            new Size (0, 0), MovementFreedom .Any, Cursors .Hand);
  ca [2] = new ContourApex (2,
            new Point (rcHouse .Right, rcHouse .Bottom),
            new Size (0, 0), MovementFreedom .Any, Cursors .Hand);
  ca [3] = new ContourApex (3,
            new Point (rcHouse .Left, rcHouse .Bottom),
            new Size (0, 0), MovementFreedom .Any, Cursors .Hand);
  ca [4] = new ContourApex (4, ptTop,
            new Size (0, 0), MovementFreedom .Any, Cursors .Hand);
  ContourConnection [] cc = new ContourConnection [6] {
     new ContourConnection (0, 1), new ContourConnection (1, 2),
     new ContourConnection (2, 3), new ContourConnection (3, 0),
     new ContourConnection (0, 4), new ContourConnection (1, 4) };
  contour = new Contour (ca, cc);
}

Listing 2: Moving the top-left corner of the house

public override bool MoveContourPoint (int i, int cx, int cy,
                              Point ptM, MouseButtons catcher)
{
  bool bRet = false;
  if (catcher == MouseButtons .Left)  // resizing with left button
  {
    if (i == 0)     // Top-left corner
    {
      if (rcHouse .Height - cy &gt;= minHeight) 
      {                          // compare with minumum height
        rcHouse .Y += cy;
        rcHouse .Height -= cy;
        ptTop .Y += cy;          // roof height is not changing
        bRet = true;
      }
      if (rcHouse .Width - cx &gt;= minWidth)
                                 // compare with minumum width
      {
        rcHouse .X += cx;
        rcHouse .Width -= cx;
        ptTop .X += cx;
        ptTop .X = Math .Min (Math .Max (rcHouse .Left +
           minRoofSide, ptTop .X), rcHouse .Right - minRoofSide);
        bRet = true;
      }
    }