IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

Rubber Band Selection Outlines in Avalon

Friday 6 February, 2004, 08:28 PM

There was a recent discussion on the Avalon newsgroup about drawing with XOR in Avalon. (In fact the scope was somewhat broader - the original question was whether WinFX was going to expose all the functionality available under Win32. XOR in Avalon was given as a specific example, because a lot of people got annoyed about the fact it is not supported in GDI+ on the current versions of .NET.)

In case you're not familiar with it, XOR drawing is a popular technique for reversible drawing operations. Specifically, if you've drawn something using XOR, it's possible to 'undraw' it again by simply repeating the exact same drawing operation. This is a useful property if you want to draw some kind of temporary overlay on top of something already on screen. XOR became a popular technique for drawing 'rubber band' selection outlines. (I.e., rectangles that you can drag out with the mouse. They are sometimes known as 'rubber band' outlines because you can change their size - stretch them - with the mouse.)

Although a popular technique, XOR drawing has some shortcomings. Rather than enumerating them here, I'll just point you to an article I wrote a few years ago for Reach magazine describing the problems with XOR, and the alternatives available with .NET and GDI+.

Avalon doesn't currently support XOR drawing. In fact it really wouldn't make much sense. XOR drawing starts from the fundamental assumption that you are drawing pixels into a frame buffer. But in Avalon, you're a long way removed from the frame buffer. There's one there somewhere in the guts of the graphics card, but the primitives you get to work with are rather higher level. And with a composition-based user interface, XOR drawing is a whole lot less useful. For example, what would it mean to XOR something onto a Canvas which was partially occluded by some other visual element that was higher in the Z order, but which was partially transparent? It's possible to come up with a reasonable answer as to what should happen in such cases, but you completely lose the one benefit XOR offers: reversability.

But this non-support of XOR is a Good Thing. It means that developers will have to use the superior alternative that Avalon offers!

There is no need for XOR. At least, not if your goal is to do the kinds of things that XOR was typically used for in Win32. (It was used mostly for mouse drag operations.) If you want to draw a 'rubber band' selection outline in Avalon, it's actually a whole lot simpler than using XOR: just bung a rectangle into the visual tree! To undraw it, take it back out again. Or, even simpler, leave it in the tree permanently, and just toggle its Visibility property to show and hide it. Here is some suitable markup for a drag rectangle:

<Canvas ID="mainPanel" DockPanel.Dock="Fill">
  ... Put any other UI you like here ...
  
  <Rectangle ID="dragRect" Visibility="Hidden"
             Stroke="Blue" StrokeThickness="2pt" Fill="Navy" />
<Canvas>

Note that this <Rectangle> element has initially been set to Visibility="Hidden". This is because when the application starts up, you typically don't want a drag rectangle to be shown.

Then all you need to do are add some mouse event handlers. In the MouseLeftButtonDown event, handler make the rectangle visible, note down the initial mouse position, and set a flag to indicate that you're dragging. In the MouseLeftButtonUp handler, make it invisible again and clear the dragging flag. And in the MouseMove handler, update the size and position of the rectangle. That last one is the only remotely tricky one - because the rectangle's Width and Height properties must be positive, there is some mildly tedious work to be done to make sure you always position the rectangle appropriately. Here's one way. (There may well be more elegant ways, but this does the trick.)

private void OnMouseMove(object sender, MouseEventArgs e)
{
  if (dragInProgress)
  {
    Point p = e.GetPosition(mainPanel);
    double w = p.X - startPoint.X;
    double h = p.Y - startPoint.Y;
    double x = startPoint.X;
    double y = startPoint.Y;
    if (w < 0)
    {
      x = p.X;
      w = -w;
    }
    if (h < 0)
    {
      y = p.Y;
      h = -h;
    }

    Canvas.SetLeft(dragRect, new Length(x));
    Canvas.SetTop(dragRect, new Length(y));
    dragRect.Width = new Length(w);
    dragRect.Height = new Length(h);
  }
}

RubberBand application You can download a full example from here. (For those of you who find this via a web search a long time after I wrote it, bear in mind that this is designed to work with the PDC build of Longhorn, i.e. Build 4051.) When running, it should look like the picture to the right. It is not the simplest possible illustration of the technique. (Sorry! I got carried away.) It has a couple of extra features. First, there is a slider control that allows the opacity of the drag rectangle's fill to be set. In the picture here, it has been made partially transparent, as you can see. (The drag rectangle is the blue box the covers the text, the polygon, and part of the ellipse.) There's also a checkbox that lets you leave the rectangle visible after letting go of the mouse. (That was mainly to make getting a screenshot easier!) And finally, just to show that it can be done, one of the elements in the main window area is above the drag rectangle in the Z order. This means it will always be visible above the drag rectangle even if you choose to make it completely opaque.

The drag rectangle is defined as a markup element exactly as shown above. So if you want to modify its appearance (e.g. give it a more interesting fill than a solid colour) then that's easily done. You could even locate the ubiquitous .wmv file that was used in all the Avalon demos at the PDC. :-)

Note that this just shows the basic technique: using elements in the visual tree for overlays such as drag rectangles. Nothing happens when you let go of the mouse. (So if you are expecting to see the objects in the window be selected somehow, lower your expectations and then try again.) Also, this may not be how things will be done in the long term. The PDC build of Avalon has an intriguing class called AnnotationPanel which suggests that there might be specific support for adding overlays. However, this class is documented as "Not recomended for use at this time. Implementation in process." So I guess we have to wait until Beta 1 to find out how that's going to work.

Copyright © 2002-2013, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk