Feb 11 2009

Playing with Blocks: Episode 3

Published by Mark at 2:12 am under Features, Programming

In my last post on the Silverlight drawing project I said I was going to stay away from the design and talk about code next. Quite a bit has changed, though, since then. For one thing the DrawStylus class has fallen victim to shrinking relevance and has been removed. The operations I initially envisioned for it kept leaping to other classes until it was nothing more than a forlorn wrapper around the mouse events. Oh well. Sometimes you have to be ruthless. Some additional classes have appeared, notably the StyleBox and StyleControl, which have about the same relationship as ToolBox and Tool, but are focused on editing styles. StyleBox derived from a StackPanel, and I’ve found that works very well, so I will probably be going back and changing ToolBox to derive from one as well.

Here’s a look at the current object model as it has been evolving over the last week and half. One of the things that I really enjoy about solo projects like this is having the time to conceive features and then really think through how to fit them into the framework in a generic and reusable way. It’s an organic growth process that leads to a Cambrian explosion once you get a critical mass of the architecture in place.

flipbook-model_2.png

Needless to say, I am not at the Cambrian phase yet, but I’m hopeful. With that out of the way I can turn to something with a bit more fun factor, and what could be more fun than moving stuff around on screen? For the rest of this post I’m going to dive from the heights of abstraction all the way down to the smallest thing that will appear on the application’s window: a drag handle.

“Drag handle” is my name for the little widgets that appear on the edges of graphical objects in a drawing application. They are used for moving, resizing, or skewing objects by clicking on them with the mouse and dragging them, usually with the change to the object showing in realtime as the handle moves. Take a look at my Silverlight Gradient Editor. If you look at the gradient sample on the left you’ll see two orange squares attached to the line showing the direction of the gradient. Click on one and move it around. That’s a drag handle, or in my case, a DragHandle. There is a surprising amount of plumbing necessary to implement a little widget like this. You need the basic event handling, a way of measuring movement, a way of transforming coordinates, and a way of mapping the resulting values onto the properties of things you want the handle to control. In the end I am not sure whether the DragHandle class involves writing less code to do all these things, but it involves writing simpler, higher level code in a consistent way.

A DragHandle has a few key properties. First, you need to be able to click on it and drag it around. So it’s a control, with a shape, that handles mouse events and updates its own position. Second, you need to be able to specify some constraints on how it moves. In the gradient editor the two orange DragHandles are constrained to move within the area of the sample box only. If you change the brush type to RadialGradientBrush, you’ll see an ellipse with three kinds of DragHandles: one in the center that can move anywhere within the sample box; one at the minimum Y coordinate of the ellipse that can move in the vertical between the center and the edge of the box; and one at the maximum X coordinate that can move in the horizontal between the center and the edge of the box.

A third property is that you need some convenient way to wire the DragHandle up to the thing it is supposed to control. In the case of the editor’s LinearGradientBrush each DragHandle is connected to an end point of a Line object. The RadialGradientBrush example is more complicated: the center DragHandle is wired to the center of the ellipse, but also has to update the positions of the other two DragHandles, whereas they merely need to adjust the ellipse Y and X radii. Both are also wired to the properties of the brush that is being edited. This wiring involves another class called DragHandleConnector that I’ll get to in the second part of this. The full definitions of these classes is too big a topic for a post here. I hope to hit the highlights. See the links at the bottom of the post to download the source code files.

DragHandle is a UserControl, and the XAML markup is very simple, containing only a grid and child Canvas on which I position the handle shape. That Canvas may not in fact be necessary, and it’s possible I’ll get rid of it somewhere down the road. Here’s the markup:

  1. <UserControl x:Class="DrawControls.DragHandle"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  4. <Grid>
  5. <Canvas x:Name="HandleCanvas">
  6. </Canvas>
  7. </Grid>
  8. </UserControl>

As you can see, not much to it. All of the fun stuff happens in the code-behind, and in the connector class, so let’s start with the code behind. DragHandle is a basic UserControl that exposes dependency properties for a number of key attributes. These include the shape to be used for the handle, the brush to fill the shape with, the brush and width for the outlining stroke, the cursor to display when the mouse is over the handle, etc.

dh_properties.png

As every WPF programmer knows, implementing DP’s is one of the joys of Windows development on the 3.5 framework. However tedious, they do make the class much more useful and accessible. The meaning of many of these properties is self-evident, however a couple are worth discussing in more detail. The HandleDragConstraint property takes a value from the DragConstraint enum, which specifies the possible motion constraints on a DragHandle. I considered a number of different ways to represent these constraints, but settled on an enumeration as the clearest way to express them. I also considered making them “OR-able”, and I can think of some scenarios where that would be handy, but for now each of these constraints is mutually exclusive of the rest.

  1. public enum DragConstraint
  2. {
  3. None,
  4. EW,
  5. NS,
  6. NESW,
  7. SENW,
  8. NE,
  9. NW,
  10. SE,
  11. SW,
  12. E,
  13. N,
  14. S,
  15. W
  16. };

The screen is divided into cardinal compass directions, where up (-Y) is North. Movement constraints are relative to the HandleOrigin, which is another dependency property. The HandleOrigin can either be set directly, or set by passing a Point to the DragHandle constructor. In either case it remains constant until explicitly reset. Other properties and methods rely on HandleOrigin. For example, the SnapToOrigin() method will move the handle to the origin. I’ll talk more about the mechanism for enforcing movement constraints relative to the origin a little further on.

Another interesting dependency property is ReferenceElement. The purpose of this property is to set the element whose coordinate space will provide a frame of reference for drag events. When handling mouse events in Silverlight (or WPF for that matter), the MouseEventArgs class provides a method called GetPosition(UIElement) that returns a point representing where the mouse is. The single parameter specifies the UIElement the reported coordinates will be relative to. If it is null then they are relative to the entire plugin area. In most cases a DragHandle will be trying to stick itself to a shape on a canvas, and we’ll want to translate movement of the handle to movement on the canvas. By setting the ReferenceElement to the Canvas of interest we can simplify that task, however, as you’ll see below more manipulation of the reported movements is necessary to get any real usefulness out of this class.

The rest of the dependency properties are for things like appearance, and don’t require a lot of explanation, so I’ll move on to talk about the mechanisms that make some of these properties and methods work for the DragHandle class, beginning with motion constraints.

The HandleDragConstraint property defaults to DragConstraint.None. If it is set the OnHandleDragConstraint PropertyChanged event handler gets called, and in turn calls a method named SetDragConstraint. The purpose of this method is to determine which of a set of small movement evaluation functions should be called when the user tries to drag the handle. It does this using a select statement, as shown below.

  1. private void SetDragConstraint( DragConstraint d )
  2. {
  3. switch ( d )
  4. {
  5. case DragConstraint.None:
  6. _evalMove = null;
  7. break;
  8.  
  9. case DragConstraint.E:
  10. _evalMove = new MovementEvalCallback(Eval_MoveE);
  11. break;
  12.  
  13. case DragConstraint.W:
  14. _evalMove = new MovementEvalCallback(Eval_MoveW);
  15. break;
  16.  
  17. case DragConstraint.N:
  18. _evalMove = new MovementEvalCallback(Eval_MoveN);
  19. break;
  20.  
  21. case DragConstraint.S:
  22. _evalMove = new MovementEvalCallback(Eval_MoveS);
  23. break;
  24.  
  25. // remainder of select cases skipped
  26. }
  27. }

The delegate _eval is a private member of the DragHandle class, and can be in one of two states: either it is null, and there is no movement constraint, or it points to a valid eval method that should be called when the user moves the handle. The chain of events begins with the method that handles MouseMove events on the shape that represents the handle. This method first gets the mouse position relative to the reference element, and then calls a method named RequestDragTo(ref Point position). The RequestDragTo method evaluates the user’s movement, and returns true if it is allowed, or false if it is not. If the method returns true the passed position may have been modified, which is why it is passed by reference. Let’s take a look at RequestDragTo.

  1. private bool RequestDragTo(ref Point dest)
  2. {
  3. if (dest == HandleOrigin)
  4. return true;
  5.  
  6. if (dest.X < HandleMinX || dest.X > HandleMaxX ||
  7. dest.Y < HandleMinY || dest.Y > HandleMaxY)
  8. return false;
  9.  
  10. return (null != _evalMove ? _evalMove(ref dest) : true);
  11. }

RequestDragTo applies three rules to the incoming position. First, if the position is the same as the HandleOrigin it just returns true, since the handle can always move there. Second, if the movement is outside the bounding box specified by HandleMinX, HandleMinY, HandleMaxX, and HandleMaxY the method returns false, since the handle can never move there. If neither of those rules applies, the third rule requires the method to return true if _eval is null, or call _eval if it is non-null. If _eval is called it is passed the destination point by reference, and will return true if the movement is allowed, or false otherwise, and as described above it may modify the destination point. Each of the individual eval methods is very simple. I’ll show two of them by way of example.

  1. private bool Eval_MoveE(ref Point dest)
  2. {
  3. if (dest.X >= HandleOrigin.X)
  4. {
  5. dest.Y = HandleOrigin.Y;
  6. return true;
  7. }
  8. else return false;
  9. }
  10.  
  11. private bool Eval_MoveNESW(ref Point dest)
  12. {
  13. dest.Y = HandleOrigin.Y - (dest.X - HandleOrigin.X);
  14. return true;
  15. }
  16.  

As you can see these two methods behave slightly differently. The first is an asymmetric constraint, as it allows movement only in one cardinal direction; in this case from center to the East. So this method first applies a bounds rule, and then modifies dest.Y to make sure it is the same as HandleOrigin.Y. The second method doesn’t need bounds as it allows movement in two cardinal directions across the center, and movement too far in either direction will be caught in RequestDragTo when it violates the bounding box. So all this method does is coerce Y to move as much as X.

In addition to the dependency properties discussed above the DragHandle class exposes events that provide the main interface for client code, as well as an event arguments class to provide information to handlers.

  1. public class DragHandleEventArgs : EventArgs
  2. {
  3. public Point Position { get; set; }
  4.  
  5. public Point Origin { get; set; }
  6. }
  7.  
  8. public event EventHandler<DragHandleEventArgs> Click;
  9.  
  10. public event EventHandler<DragHandleEventArgs> BeginDrag;
  11.  
  12. public event EventHandler<DragHandleEventArgs> Drag;
  13.  
  14. public event EventHandler<DragHandleEventArgs> EndDrag;
  15.  

Using these events the calling app’s code can be executed when the handle is clicked, when it begins to move, when it moves, and when it stops moving; fairly typical stuff for this kind of control. However, using these events directly would still require the application to put a lot of plumbing in place. The DragHandle as described so far is still a pretty dumb control. It can be dragged, and it will tell you when it’s dragged, and that’s about it. If you want the control to… ahem, control something, you have to provide the means to translate the changes in the DragHandle into changes to that something’s properties.

Take another look at the gradient editor. When you drag the orange handles around on the gradient sample box several things are happening. First, the end point of the line that is under that handle is being adjusted to move with it. Second, the properties of the LinearGradientBrush are being adjusted to reflect the change. No big deal, except that the drag handle and the line move in the coordinate space of the canvas, while the changes to the brush are in a 0 – 1.0 normalized coordinate space. To further mess with your head, the Line object doesn’t expose its end points as Points. Rather it uses X1, Y1, X2, and Y2. Why this is so I can’t say, anymore then I can say why occasionally a calf with two heads is born. They just are. So it isn’t simply a matter of assigning the X/Y values of the DragHandle’s position to the properties of the thing we want to control. We need more flexibility than that. In my next post I will discuss the thing that gives it to us: the DragHandleConnector class.

Source Code:

DragHandle.xamlDragHandle.xaml.cs

Pass It On:
  • Digg
  • Furl
  • del.icio.us
  • Google Bookmarks
  • StumbleUpon
  • Technorati
  • Reddit
  • NewsVine
  • Live
  • Print

No responses yet

Trackback URI | Comments RSS

Leave a Reply

3d home architect design suite deluxe 8 download . acd see download . acronis disk director suite 9.0 . acronis true image 10 . acronis true image home 2009 download . adobe acrobat 5 . adobe acrobat 6 download . adobe acrobat 7.0 pro download . adobe acrobat 7.0 professional . adobe acrobat 8 pro . adobe acrobat 8 professional download . adobe acrobat 8 standard . adobe acrobat 8.0 professional download . adobe acrobat 9 download . adobe acrobat 9 pro extended download . adobe acrobat reader 5 . adobe acrobat reader 8 download . adobe after effects 7.0 . adobe after effects cs3 professional . adobe after effects cs4 . adobe after effects download . adobe captivate 3 . adobe contribute cs4 download . adobe creative suite 3 design premium . adobe creative suite 4 . adobe creative suite 4 design premium download . adobe creative suite 4 master collection download . adobe creative suite 4 master collection mac . adobe cs2 master collection . adobe cs3 design premium . adobe cs4 design premium . adobe cs4 download . adobe cs4 master collection download . adobe cs4 master collection mac . adobe cs4 web premium . adobe dreamweaver 8 . adobe dreamweaver cs2 download . adobe dreamweaver cs3 . adobe dreamweaver cs4 download . adobe fireworks cs3 . adobe flash 9 . adobe flash cs2 . adobe flash cs4 professional . adobe flash cs4 professional mac . adobe illustrator cs2 . adobe illustrator cs4 mac download . adobe indesign 2.0 . adobe indesign cs4 download . adobe photoshop 12 download . adobe photoshop 6 . adobe photoshop cs . adobe photoshop cs2 download . adobe photoshop cs3 download . adobe photoshop cs3 extended . adobe photoshop cs4 extended . adobe photoshop cs4 extended mac . adobe premiere 6.5 download . adobe premiere pro cs3 . adobe premiere pro cs4 download . adobe presenter 7 download . apple final cut express 5 download . autodesk 3ds max . autodesk 3ds max 2010 . autodesk autocad 2007 . autodesk autocad 2008 . autodesk autocad 2010 download . autodesk autocad architecture 2009 download . autodesk autocad electrical 2010 download . autodesk autocad inventor lt 2010 . autodesk autocad inventor professional suite 2010 (32 bit) . autodesk autocad inventor professional suite 2010 (64 bit) . autodesk autocad mechanical 2010 . autodesk autosketch 9 . autodesk inventor 2008 . autodesk inventor professional 2009 download . avid media composer 2.8 download . cakewalk sonar 7 producer edition . corel painter 10 . corel painter x download . corel photoimpact x3 download . corel video studio pro x2 download . download 2003 microsoft office . download acronis disk director suite 10 . download adobe acrobat 6.0 professional . download adobe acrobat pro . download adobe acrobat reader 6 . download adobe captivate 2 . download adobe contribute 3 . download adobe creative suite 3 . download adobe cs3 . download adobe cs3 master collection . download adobe cs3 master collection mac . download adobe dreamweaver cs4 mac . download adobe fireworks cs4 . download adobe flash 8 . download adobe flash cs3 professional . download adobe flash cs3 professional mac . download adobe illustrator 9.0 . download adobe illustrator cs3 . download adobe illustrator cs4 . download adobe indesign 4 . download adobe indesign cs . download adobe indesign cs3 . download adobe photoshop 7 . download adobe photoshop 7.0 . download adobe premiere 2.0 . download apple final cut express 4 mac . download apple final cut studio . download autodesk 3d studio max 2009 . download autodesk 3d studio max design 2009 . download autodesk autocad 2009 . download autodesk autocad architecture 2010 . download autodesk mudbox 2009 . download corel draw 11 mac . download corel dvd copy 6 plus . download corel video studio 12 . download dreamweaver 4 . download dreamweaver cs2 . download dreamweaver cs3 . download dreamweaver cs4 . download dreamweaver mx 2004 . download intuit quickbooks 2009 premier . download mcafee total protection 2009 . download microsoft autoroute 2007 europe . download microsoft frontpage 2002 . download microsoft money 2004 . download microsoft money 2007 . download microsoft money 2008 . download microsoft money plus 2008 . download microsoft office 2003 professional . download microsoft office 2004 for mac . download microsoft office 2008 for mac . download microsoft office 2008 mac . download microsoft office 97 . download microsoft office enterprise 2007 . download microsoft office for mac . download microsoft office visio professional 2003 . download microsoft office xp . download microsoft streets and trips 2009 . download microsoft windows vista business (64bit) . download microsoft windows vista home basic with sp2 (32 bit) . download microsoft windows vista home premium with sp2 (32 bit) . download microsoft windows vista home premium with sp2 (64 bit) . download microsoft windows vista ultimate (64bit) . download microsoft works 4.5 . download nero 10 . download nero 2009 . download nero 8 ultra edition . download nero burn . download parallels desktop 4.0 for mac . download pctools spyware doctor 5.5 . download quarkxpress 8 . download roxio creator 2009 ultimate . download roxio creator plus . download sony vegas pro 8 . download steinberg nuendo 3 . download symantec winfax pro 10.0 . download vmware workstation 6.5 ace . download vmware workstation 7 . download window xp professional . download windows 7 release . download windows office xp . download windows vista 64 bit . download windows vista ultimate . download windows xp pro sp2 . download windows xp sp1 . download xilisoft video converter ultimate 5.1 . graphisoft archicad 12 . guitar pro 5 mac download . guitar pro 6 . i.r.i.s. readiris pro 11 download . intuit quicken rental property manager 2009 . macromedia dreamweaver . mathworks matlab r2008a . microsoft autoroute 2008 download . microsoft autoroute europe 2009 download . microsoft autoroute express download . microsoft digital image suite 2006 download . microsoft encarta premium 2007 . microsoft encarta premium 2009 . microsoft frontpage 2.0 . microsoft frontpage 2003 download . microsoft frontpage 2007 . microsoft mappoint 2009 north america download . microsoft money 2005 . microsoft money 2007 home & business . microsoft money 2009 . microsoft money plus download . microsoft money premium . microsoft office 2000 download . microsoft office 2002 . microsoft office 2003 enterprise