Designing Moveable Graphics
Part 1 of this article describes the ideas of organizing moveable/resizable graphics. It covers applying these ideas to some moderately complicated cases. Part 2 discusses more about building application that use moveable graphics, for form customization for example.
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 a form (dialog) as moveable and resizable.
- Easy doesn't mean primitive. The object configuration may 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 types of changes, others may need several parameters (sizes) to remain fixed, and parts of the objects may generate restrictions on other parts' changes. Still, the entire variety of possible reconfigurations must be easy to understand and implement.
- These features—moveable and resizable—must be added like an extra "invisible" feature; they must not destroy any image, but it must be obvious that the features are there and available while working with the application. In some cases, I can demand a visual indication of feature availability, but typically, availability should be apparent without any extra lines or marks.
- Not only must new classes be easily declared moveable and resizable, it must also be easy to add these features to already existing classes; by just touching them (or using the keyboard) the objects should become moveable.
- Programmers must be able to add these new features only 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 other words, they can start using them piecemeal, so they can see how the features will affect existing complicated applications.
- Using moveable graphics at the application level must be as simple as moving 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). When it is useful, the process should make it easy to organize 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 not visible and obvious), but they don't. All these features are designed and working now.
I work with C#, so all the code here is in C# and I'll use C# terminology. But the algorithms and designed classes are not limited to C#—you can develop them easily with other languages. The Test_MoveGraphLibrary
contains all the code and samples from the application.
Basic Idea: Contour Presentation
There are two ways to add moveable/resizable features to an object: use an interface or an abstract class. After trying both ways I decided upon an abstract class. Any object that you want to make 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
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,
The core idea for making graphical objects moveable and resizable is based on contour presentation. Any object involved in moving and/or resizing must have a contour. For graphical objects the contour is organized in the DefineContour()
A contour consists of nodes and their connections. These two types of contour elements are used for different purposes. Except on rare occasions, a contour does not duplicate an object's shape. A contour looks more like a skeleton, which allows the flesh around it to move as a single body; however, for many objects, contour is placed outside
. For these objects, the contour looks like a frame. A contour may even consist of disjointed 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.
(the 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 act only for nodes that can be moved individually. Each node has its sensitive area, although for special cases, when a 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 you have some flexibility in defining these. I'll talk about them in describing the design of the real contour. Sizes and forms of sensitive nodes can vary; the simplicity of using such moveable graphics depends on the designer's decisions when organizing nodes.
(the ContourConnection class) are used for grabbing and moving an object as a whole, implemented in the Move()
method. Each connection also has a sensitive area where 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; intermediate variants have a "sausage" shape.
Very often the entire area of the graphical object is used for some mouse-generated events that initiate actions or changes with the object. Moving and resizing is based on mouse-generated events at nodes and connections, thus the addition of contour may be the cause of conflict between old commands and new requirements. To minimize this conflict, nodes are usually small and connections thin, though in special situations you can design sensitive areas that cover the maximum size.
Nodes and connections are the construction elements of a wide variety of contours; each design depends on the goal of the particular graphical object. You'll see how all this works in the real world.