Login | Register   
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 1 : Page 4

In typical modern operating systems and applications, windows are moveable and resizable; graphics and controls inside applications are not. But it doesn't have to be that way.

The Boss Class: Mover
The easiest way to move any object around the screen is to drag it. For the whole process you need only three mouse events: MouseDown, MouseMove, and MouseUp.

From a user's point of view, the whole process must be simple: press, move (or rotate), and release; any additional complication will not be acceptable. A designer planning to include moveable/resizable objects in an application must store, analyze, and process the following information for each object: positions of the nodes and connections, sensitive areas, whether they overlap, order of drawing, type of movement, etc.

The beginning of this article discussed the basic requirements for the whole moving/resizing process and wished for a class that would provide all the desired features. Such a class exists, and it's ready to do everything you need. 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 portion of this class's capabilities, because it also handles resizing, and provides associated information. The Mover class is described in MoveGraphLibrary_Classes.doc.

Now I'll show you how to move from a single house to the town using the code in the file Form_Houses.cs. All the houses in the town should be moveable and resizable, so they're derived from GraphicalObject:

public class SimpleHouse : GraphicalObject

Next, the code declares the company responsible for all house movements and reconfiguration, and start constructing the town:

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

A 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 // insert the new house into the List<> Town.Insert (0, newhouse); // register the new house as moveable Movers .Add(newhouse); Invalidate (); }

The real code is slightly longer; the code here shows only the significant lines. The preceding code:

  • Initializes a new house object: The first line creates and initializes a new SimpleHouse instance.
  • Inserts the new house into the official town's list of buildings: Every new house should appear on top of all previous houses, so the application inserts new houses at position zero, while painting (see the Paint() method) occurs from the opposite end.
  • Registers 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.
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. Programmers have access to the elements of this list via standard indexing or other standard List<> methods (see MoveGraphLibrary_Classes.doc for more complete descriptions of implemented methods).
  • Mover doesn't know anything about the real objects; Mover works only with object contours of objects included in its list. It is the programmer's responsibility to make parallel changes between the outer world (there can be several lists, arrays, or anything else) and the List inside Mover. For example, consider a situation in which Mover tries to move 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; you may decide to give this feature to all, some, or none of the objects. Therefore, it's your responsibility to ensure that Mover works on a correct List. The set of List methods makes any needed task easy (delete, add, insert).
You can see the real power of Mover in the code for the three mouse events. On the MouseDown event it tries to grab a house either for moving or for reconfiguring. In this application, 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 need only one line to start the moving/resizing process:


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

The MouseUp event finishes any moving/resizing process, and releases 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 do the real moving/resizing, but even here, the code is not complicated:

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:


This checks the Mover's situation. If the Mover signals that it has caught some object from its list, it triggers a repaint.

Author's Note: 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.

That's the extent of your construction—you should now have a town of moveable and resizable houses. In the sample application you'll find some additional 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.

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, as discussed in the following sections.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



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