|Adorner layer, adorners and thumbs|
Today is the beginning of day 9. I have spent this long trying to understand how to draw selection handles on graphics. This is an absurd amount of time. I was originally tickled by the ease with which thumbs and adorners worked, but the more I got into the details - on things like resizing multiple items - the more I began to struggle. This could be a symptom of my advancing age. It could also be a symptom of something else.
To ease my mind, I hunt in the indexes of the books I have. Petzold: Applications = Code + Markup, 1000 pages, no mention of adorners or thumbs. Nathan: WPF Unleashed 640 pages, no mention of adorners or thumbs. Petzold: 3d, 430 pages, no mention of adorners or thumbs. Xu: Practical WPF Graphics, 650 pages, no mention of adorners or thumbs... I am beginning to see a pattern.
In fact, the only way to learn about them is to look at the MSDN example of a resizingadorner and by hunting through the code in Diagram Designer on CodeProject. The link I provide to the latter is for part 1 of 4. This, rather than the MSDN effort is by far the best, though I wonder, and I wonder greatly, how the author Sukram/Markus found out all of this arcane information.
It seems to me that WPF has a definite way. There seems to be a way in which things should be done, and the old way, in which things shouldn't be done. For example, in the old way, I would draw the handles on the drawing canvas, and the code to manipulate them and the objects they acted upon, would be a little intertwined. I am a great believer in a separation of concerns, so I like the fact that all of the manipulation code can easily be separated from other things. The problem is that understanding it seems to be far more difficult. To this end, I shall begin with a simple example, selection by a rubberband. Most of my code is derived (dare I say copied?) from Diagram Designer.
To create a rubber band on a canvas, you can draw the rubber band on the canvas quite easily. If you do so, your code for the rubber band will be messed up with other code for selection by clicking and dragging of the thumbs you may eventually create. To separate this code from other more general canvassy code, you must draw the rubber band on an adorner layer. The rubber band is an object derived from the Adorner class, and the visual for the rubber band is a rectangle.
The complication is this. The rectangle cannot be positioned by setting left/right unless it is on a canvas. This means a canvas must be placed on the adorner layer. I finally realized this on day 8.
It looks like this:
The Drawing canvas is the canvas containing the drawing which has elements to be selected.
The Adorner layer is created to hold the adorner. It doesn't do much.
The Adorner is bound to the adorned element, in this case the Drawing canvas, so has the size of the drawing canvas.
The Canvas is created and added to the adorner. In this case, the Canvas has the same size as the Drawing canvas.
The Rectangle is placed on the Canvas and its width and height are reset with every mouse move.
It may all seem a bit much when the rectangle could quite easily - and with less investment in time to understand it - be added to the Drawing canvas. Doing it this way does open up a better way of keeping the code for manipulation of objects separate to the drawing canvas code.
Here are some pictures of it in action.
As you can see, I have implemented four different selection types: Rectangle, Ellipse, Polygon and Free.
I would like to offer some code to demonstrate what is involved. Unfortunately, I have only my test applications which has gone through so many iterations it has become a god-awful mess, which you are welcome to. To see the rubber band in action, click final in the radio buttons on the left and drag away. The selection is broken, so visuals do not get selected properly.
To alter the selection type, look for the line of code: RubberbandAdorner2 adorner = new RubberbandAdorner2(this, rubberbandSelectionStartPoint, RubberBandType.Free ); and the class of interest in this case is RubberbandAdorner2. This solution file also contains all of the other efforts I have expended to try and understand adorners, although they may only add to the confusion.
Click here for the project files