RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Design and Use of Moveable and Resizable Graphics, Part 2 : Page 3

Windows are moveable and resizable; graphics and controls inside applications are not. The second part of this article explains how to design complex moveable and resizable graphical objects and use moveable and resizable elements in your Windows Forms applications.

Y(x) Plots and More
In the first part of this article, I mentioned that the huge demand for moveable/resizable graphics in scientific and engineering applications triggered this work. The results turned out to also be extremely interesting in completely different areas. However, for this particular area the new results are not simply an improvement, they are a revolutionary design for the most complicated applications.

The conflict between the widest variety of constantly changing users' demands and the fixed design of engineering/scientific applications is obvious to both sides. In attempts to soften this conflict, programmers look for improvements in interface design, where there have indeed been huge achievements in this area. But the achievements start with the misguided idea that programmers can design something that would be best for each and every user. You can (and certainly must) give users what you think is best, but also give them the tools to easily construct their own dream environment—that would be the most effective application for their work. That is the main idea of moveable/resizable graphics; scientific and engineering applications may be one of the best to show the real difference in results.

The Test_MoveGraphLibrary application contains three forms that demonstrate different levels of the new features added to standard plotting. These three forms are available via submenus under "Y(x) Plots." All these forms use the FullGrArea class (included in MoveGraphLibrary.dll), which was designed for plotting Y(x) and parametric functions. You can find the detailed description of the FullGrArea class together with the tuning forms, which are also provided, in the file MoveGraphLibrary_Graphics.doc. Here's a look at the steps for turning unmovable standard plots into fully moveable and resizable versions.

The first form in this group is Form_UnmovablePlots.cs which has two standard unmovable FullGrArea class graphical objects. You can easily make these two objects moveable and resizable, because any FullGrArea instance has these features. There is a Mover in this form and it works: the colored information panel in the form is moveable. However, because the two two objects were not added to the Mover list, they are not moveable or resizable. To eliminate all movement in this panel and make it an ordinary form with all fixed elements, simply comment all the code that deals with Movers, including the OnMouseDown() and OnMouseUp() methods and the second part of the OnMouseMove() method. The first part of OnMouseMove()changes the mouse cursor to indicate that it's possible to double-click the left button to open the tuning forms. This tuning mechanism works regardless of whether the FullGrArea object is really moveable; you can see that the variable Movers is not mentioned in the OnMouseDoubleClick() method.

Conversely, to make the unmovable (standard) plots moveable, just include them into the Movers' list. Form_OneMovablePlot.cs, the second form of this group, demonstrates that by adding one line of code:

   Movers.Add (area);
Everything else in this form is exactly the same as in Form_UnmovablePlots (except that it has only one plot rather than two). This form demonstrates how you can quickly change the plotting in any engineering or scientific application from unmovable into moveable, but without changing the main design ideas.

The real power of moveable/resizable graphics for the area of scientific and engineering applications will be unleashed when users get to decide what, when, and how to show the contents. This is the case in Form_MovablePlots.cs.

For any other form of the Test_MoveGraphLibrary application, I can show you a screen image and write definitively that "this is how it looks." However, here you can organize any number of different plots, all of which are moveable and resizable, so there's no way to predict how this form will look. But as a designer, I need to provide the interface by which users will organize and analyze the different functions. This application has one small limitation: users can select the functions only from a predefined set. The main form of the TuneableGraphics application uses the same interface without this limitation; there users can explore any number of arbitrary functions.

From a programming point of view, implementing moveable/resizable plotting for an unlimited number of areas differs only slightly from the previous case of a single plotting area. Here you have the same single Mover and the same simple methods for the MouseDown, MouseUp, and MouseMove events. To organize these without any problems (and mistakes) in moving, visualization, and tuning, there needs to be a reliable link between the function and the area where it is shown. This link is provided by the additional class PlotOnScreen:

   public class PlotOnScreen
      DemoFuncType funcType;
      FullGrArea grArea;
Functions that you decide to show on the screen are organized into a List:

   List<PlotOnScreen> plotInView =
      new List<PlotOnScreen>();
All possible operations—adding functions, painting, moving, tuning, changing the order, deleting—are accomplished using only the objects in this List, so there will be no discrepancies between the real function and its view.

Figure 3. MoveablePlots Form: The form contains moveable plots and panels, shown here with their visualized contours.
Figure 3 shows two plots and two panels; the plots are moveable and resizable. The panels are also moveable, but only the one containing the list of functions is resizable. You can see the difference in the contours of the two panels.

Converting applications from designer-driven into user-driven applications is not a five-minute job; however, simply changing graphics to make them all moveable and resizable is quick. The Test_MoveGraphLibrary application includes two special cases of Y(x) plotting; they have such special features that each one visually represents its own subgroup.

The first case handles simultaneous plotting in the same area of different functions that use different scales. This is not a rare situation; for some complex analysis you have to compare different processes to look for similar behavior or abnormalities. While you can certainly place two plots side-by-side for visual comparison, doing so requires a lot of extra screen space. It would be much better to put them into the same graphical area. You can do that by using the previously mentioned FullGrArea class, because its drawing methods for Y(x) functions can use border values passed as additional parameters. Those methods only partly solve the problem, because a user can change the visible scales but can do nothing with these additional parameters. So a better solution would be to use the MultiScaleGrArea class, which allows you to organize the same graphical area with an unlimited number of scales.

The behavior and tuning forms of the MultiScaleGrArea class are as close as possible to the FullGrArea class, but there is an addition in the contour. Each scale is represented in the contour by an additional segment with two nodes at the ends of its main line; by using these nodes, users can put each scale in any appropriate position in relation to the main plotting area. Because each scale has its own tuning form, the user is now in full control of all the plotting details.

Figure 4. Profile Class Objects: The Profile class handles objects that are resizable but not separately moveable.
The second case is special not only for Y(x) plotting, but for the whole application. The Profile class perfectly fits the idea of presenting contours; however, it is a unique class within MoveGraphLibrary.dll—the only class of resizable, but not moveable objects. This object is not separately moveable (on purpose), but it can be moved along with the FullGrArea class, on which it can reside. Form_Profiles.cs demonstrates two different cases of using Profile: In the top-left corner you can see the Profile object inside a Rectangle; two other Profile objects reside on the FullGrArea object, which is the subject of all standard moving/resizing transformations. Figure 4 shows a view of this form. The code samples used to explain the Profile class are also from the Form_Profiles.cs file.

A Profile object is a set of unconnected nodes (you can easily take out the lines that you see in Figure 4) by using Pens.Transparent). If you do that, you'll have a set of isolated nodes—exactly what the Profile is in reality. Two end nodes are always located on the left and right borders of the rectangle and you can only move them up or down; you can move all intermediate nodes freely between upper and lower boundaries, but cannot move them farther to the sides than the neighboring nodes. Each node is characterized by its location (Point) and two double values from inside the ranges, defined by the borders' values. You can initialize the Profile object 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, 
      ContourApexForm.Circle, 3);
   pts = new Point [] { ... };
   profile [2] = new Profile(rc, 
      area.ValuesOnBorders, pts, 
      ContourApexForm.Square, 3);
As in all other cases, you organize node movement with MouseDown, MouseMove, and MouseUp events; however, several additional lines of code in the OnMouseDown() method handle one more unique Profile aspect. For all other moveable/resizable objects the contour is organized at the moment of initialization and users can transform them only later; with the Profile object users can add or delete intermediate nodes at any time. The contour must be redesigned each time, which is done automatically inside a couple of methods:

   private void OnMouseDown(object sender,
      MouseEventArgs e) 
      if (mea .Button ==
         MouseButtons .Left) 
         for (int i = 0;
            i < profile .Length; i++) 
            int iDot = profile[i].InsideDot(
            if (iDot < 0) {
               iDot =profile[i].InsertNewDot(e.Location);
            if (iDot >= 0) {
               profile[i].Selected = iDot;
         Movers.CatchMover(e .Location);
If the user has not pressed the left mouse button inside a node, there is an additional check to see if it is close enough to any segment between consecutive nodes to insert a new node there. If so, the application adds the new node.

There are also several additional lines of code in the OnMouseMove() method. If the Profile object resides on a FullGrArea object, the profile's transformation depends on whether the host area was moved or resized. In the first case, the Profile is moved in exactly in the same way as the host area; in the second case, the profile's area is changed to the new area of the FullGrArea object.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date