art 1 of this article introduced the idea of moveable/resizable graphics, covering contour presentation and explaining the design of common and special types of contours, which you can apply to a wide variety of objects. I used simple examples to illustrate the technique of involving these objects in moving/resizing operations. This part of the article describes more complex cases of moveable/resizable graphics, such as engineering plotting, as well as objects involved in both moving and rotation. You'll see how to apply the same technique to controls, and how you can base form customization on moveable/resizable objects.
With the help of contours, you can make any object in a program moveable/resizable. To get the best results from these new features, users must know that any object inside an application is moveable/resizable, and they must understand how to use the move/resize features.
The first problem is more psychological than technical. How can engineers and scientists imagine that everything in an application is moveable/resizable if they have never seen a single scientific program with moveable plotting? Lewis Carroll's solution to the problem—simply writing "Eat me
" on top—is definitely the best for the user, but not for the programming world. In applications, perhaps the best solution is simply to rely on human nature. In the TuneableGraphics sample application
, you'll find a small button in the top-left corner of each form; some people will click this button, just out of curiosity, and they will see the contours. From there, they will try to find out what the contour lines are for, and everything else will become clear.
Users who are familiar with the idea of moveable/resizable objects and expect all objects to have such features, will be looking for the most likely places for nodes and connections. Even if they don't know these terms, they will still determine the most likely places for moving and reconfiguring objects. The designers' responsibility is to organize contours according to users' expectations. Still, it can be convenient to switch contours on and off in nearly any situation, so that tiny button in the corner serves a useful purpose.
Good contour visualization in any possible situation is a problem in itself. To solve it, you can set some default parameters that will give good results in a majority of situations, and add some methods for changing those parameters.
Contours consist of nodes and their connections. By default:
- A node is a small six-pixel square.
- Contours are displayed in red, which is the color of connections and borders for all nodes.
- The inner areas of all nodes are filled with white.
What were the ideas behind these default parameters? Contours are very helpful, but they are just tools; tools must be obvious, but they can't be the main part of the view. Six pixels square is small enough not to disturb other images, but big enough to grab easily with the mouse. Big images are rarely drawn in red, so this color will be visible in the majority of situations. Filling the inner part of the node with white plus the red border of this tiny area informs the user about this additional but artificial thing. Contours do their work perfectly, but the programmer must give users an easy way (one click) to switch the contours ON and OFF.
You can change all the default contour's settings. Part 1 discussed how to change the shape of the node from a square to a circle. If you don't want to fill the inside of the node with white, you have to switch the node's SenseAreaClearance
flag to false
ContourApex  ca = new ContourApex ;
ca  = new ContourApex (0,
ca  .SenseAreaClearance = false;
There is also an easy way to change the color of the contour, but I'll show you how to do it later. For now, I want to discuss the code for painting contours.
The Paint event usually contains the logic about which objects to draw and in what sequence. Here's the code from one form in the TuneableGraphics
application, which shows a very interesting graphical object—Skyscrapers (see Figure 1
|Figure 1. TunableGraphics Screen: TunableGraphics skyscrapers, with the coordinate system and its contour switched on.|
private void OnPaint(object sender,
Graphics grfx = e.Graphics;
There are two major graphical objects in Figure 1
: Skyscrapers, and XYZcoordinates. Skyscrapers are used for financial analysis, and are not normally moveable and resizable graphical types. XYZcoordinates is a class of moveable/resizable objects. In this case, the Skyscrapers class is under the jurisdiction of XYZcoordinates and does whatever it is told to do by the last one.
file includes the class XYZcoordinates, described along with the other included classes, in the MoveGraphLibrary_Classes.doc
documentation file. You can write your own classes and receive all the benefits of XYZcoordinates by using them together:
public class XYZcoordinates :
As a class derived from GraphicalObject, the XYZcoordinates object has all the features described in the first part of this article: you can see its contour on the picture, the contour of the coordinate system copies the axes, and the nodes are at the ends of the axes and in the crossing of coordinates. Using the three nodes at the ends of the axes, you can change all the dimensions of this picture. Because of the way the skyscrapers plot, taller towers in front will often block shorter towers behind, but by moving the single node at the crossing of axes, you can find the best view for any data set. Unfortunately, that is also where problems can occur.
Contour belongs to XYZcoordinates, but if it were automatically shown together with the object to which it belongs (in this case xyzCoor
), then the Skyscrapers picture would block it from view, making it difficult to locate the node. Even when hidden, it still works in the same way: the changing mouse cursor signals when you have located it. At that point, you can press the mouse button and rotate the whole view, but it's often tricky to locate the tiny node behind the towers.
This is a very important feature of showing contours: as a programmer you have to not only decide about the queuing sequence of any objects in your form, but you also have to decide about the sequence of contours in the same queue. More often than not, contours will not be next to their "parent" objects in this queue. To make it even more complex: you can exclude the "parent" objects from drawing. By switching off one parameter in the Skyscrapers' tuning form, you can take axes out of the picture without changing anything else. However, you still have to decide when it would be best to show contours. The code for drawing the Skyscrapers picture first draws the coordinate system, next the Skyscrapers, and then the contour. Though the contour here belongs to xyzCoor
(an object derived from GraphicalObject), you can see from the last piece of code that the object responsible for drawing the contour is not xyzCoor
, but one of an array of Movers. At first it may look very strange, but here is an explanation for this situation.
Any object derived from GraphicalObject has a contour, but may or may not be included in the list of moveable objects (Mover's list). It is very common to have both moveable and unmovable objects in your form. For example, you can put small sample objects of moveable types (classes) somewhere at the side of the form, but make them unmovable. Clicking one of these samples adds an identical moveable object to the form, but the sample itself stays in the same position all the time. These unmovable samples will have contours, as do all objects of their class, but because they are unmovable it makes no sense to display their contours. Mover has the list of all moveable objects on the form; objects themselves don't know whether they are included in the list of moveable elements. Only Mover knows about all the contours that must be painted.
There may be a lot of moveable objects in your form, and in different situations you may want to show all the contours or only some of them. To show all contours use:
To show a particular contour use:
One common situation is when you want to show the contours of the moveable controls, but not to show the contours of the graphical objects. In this case use:
for (int i = 0; i < Movers .Count; i++)
if (Movers[i].Source == Source.Control)
For some objects you don't need to show contours at all, because users will know that they can grab, rotate, and move all such objects at any time. You can decide whether to display the contour by checking the graphical object's type.
for (int i = 0; i < Movers.Count; i++)
if (Movers[i].SourceGraphical is TextMR))
If you switch on contours in the main form of the Test_MoveGraphLibrary
application or experiment with different forms of the TuneableGraphics
application, you will see that all contours are shown in one color. You can assign each contour its own color; however, because the object delegates its painting to Mover, all contours are shown in the same color. You can change the color for all contours or for any particular contour by using one of Mover's methods:
Movers.Color = Color.Magenta;
Movers[i].Color = Color.Blue;
|Editor's Note: This article was first published in the May/June 2008 issue of CoDe Magazine, and is reprinted here by permission.|