In the previous part we looked at the MVC core, this time we’ll highlight the ideas and the code related to ‘tools’. A tool is basically a series of actions the user performs and which results in a command on the undo-stack. For example:
and so on. The abstract tool concept originates from the fact that if you don’t package a series of actions in a separate class you inevitably have a complex logical flow in the overriden mouse-methods of the surface. So, instead of having various if-then-else statements in the MouseDown, MouseMove and MouseUp handlers you simply loop over all registered tools and whichever tool is ‘on’ can react accordingly. This approach has the additional advantage that you can
Below is a snippet of the mouse handlers which are called by the surface corresponding to the mouse events on this surface. Note that the loop in these handlers are broken as soon as one of the tools have opted to handle the event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public void MouseDown(MouseEventArgs e) { foreach (ITool tool in toolList) { if (tool is Core.IMouseListener) if ((tool as Core.IMouseListener).MouseDown(e)) return; } } public void MouseMove(MouseEventArgs e) { foreach (ITool tool in toolList) { if (tool is Core.IMouseListener) if ((tool as Core.IMouseListener).MouseMove(e)) return; } } public void MouseUp(MouseEventArgs e) { foreach (ITool tool in toolList) { if (tool is Core.IMouseListener) if ((tool as Core.IMouseListener).MouseUp(e)) return; } } |
Most of the tools have some kind of visual feedback, on creating a rectangle you would see for example a rubberband which previews the end-result. For this purpose a few classes are necessray which specializes the ITool interface (depicted above) and all tools which need a rubberband are inheriting from it. The whole rubberband (sometimes called ‘marching ants’) affair is also taken separately in other constructs (a static helper class) and the process of creating tools becomes in the end a game of putting some blocks together. In fact, a dummy tool with visual feedback would look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class DummyTool : RubberCreationToolBase { #region Constructor ///<summary> ///Default constructor ///</summary> public DummyTool():base("Dummy Tool") { } #endregion public override void OnExecute(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) { GhostManager.Surface = sender as DiagramSurface; base.OnExecute(sender, e); } public override void DrawGhost(System.Windows.Rect rec) { GhostManager.DrawRectangularGhost(rec); } public override void OnRubberCreated(System.Windows.Rect rec) { Surface.Controller.Output("The '" +this.Name + "' tool finished but no realshape was added."); } } |
The overridable DrawGhost method allows you to give whatever feedback you wish while the overridable OnRubberCreated allows you to do whatever you wish after the rubber was creates, i.e. after the user released the mouse. It’s precisely in this method that one should package the action (say, the creation of a shape) in a command and push it onto the undo-stack. In the dummy tool above only an informative message is displayed.
Of course, in order to manage the available tools and to connect them to the UI you need some more code in the form of some tool manager. The tool manager is just a wrapped generic list created through Unity inside the surface class. Note, in this context, that the concept of a tool only makes sense on the UI level and is, hence, not re-usable in other surfaces (e.g. a web-interface).
In the next part we’ll discuss the other end of the architecture; data abstraction and the serialization of diagrams.
We re-invented Live Pivot and took away the limitations along the way.
Read more
Hi, this is great stuff ! I’m developing a (very specific) CAD user interface. I’ve been using WPF (both 2D and 3D) while trying to stick to M-V-P.
Dan Creviers blog series on M-V-VM have been enlightening but weren’t *exactly* what I needed for the kind of UI I’m developing. So I’ve been struggling to find clean a way to “do” more complicated UIs (with multiple selection, tools etc…). It’s reasurring to see some of my ideas reflected (and implemented !) in your articles.
Please keep going with this architecture series. I’m riveted !
By Sven June 18, 2008 - 9:35 amThanks for the encouraging words. The usage of WPF for complicated UI’s is not very much documented and Microsoft in particular has little guidance. While there are frameworks for business-like applications (like Prism), you are indeed on your own if you wish to make solid stuff for interactive interfaces. Note however that what I’m writing is ‘right’ for our aims and is not necessarily a blessing in general, but I’m glad someone can benefit from our insights in any case since it’s not always very clear who’s on the other side of the blog and what others could gain from one’s own research and ideas.
By Me June 22, 2008 - 6:59 pmI’d be glad to hear about the stuff you’re creating.