Question

I'm using an Adorner in .NET 3.5, and I'm able to draw by overriding OnRender, but I need the ability to redraw the adorner to change its appearance.

Essentially I'm looking for a way to clear the drawing context and call OnRender again. What's the best way to do this, or is there a better approach?

public class MyAdorner : Adorner
{
    private Brush brush = Brushes.Red;

    public DragArrowAdorner(UIElement adornedElement) : base(adornedElement)
    {}

    public void RedrawWithBrush(Brush newBrush)
    {
        brush = newBrush;

        // redraw..?
    }

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        // some drawing code...
        drawingContext.DrawRectangle(
            brush, 
            null, 
            new Rect(AdornedElement.DesiredSize));
    }
}
Was it helpful?

Solution

The answer to your question is use InvalidateVisual to cause the OnRender to be called again

However, I would suggest instead of doing custom drawing on OnRender yourself to use the standard styling and visual tree templating to build the actual visual of the adorner. This also means you can run standard XAML animations inside it with storyboards.

If you want to go with this approach, in your adorner class you need to:

  • in the constructor either call base.AddVisualChild() or create your own visuals collection with the visuals you want to show in the adorner
  • override ArrangeOverride(Size size) in order to arrange the children properly;
  • override VisualChildrenCount to return the number of children in the adorner visual tree;
  • override GetCisualChild(int index) to return a particular child.

You can take a look at the ResizingAdorner MSDN sample for more info.

OTHER TIPS

It's very important to understand that WPF is not like Windows.Forms. OnRender() should really be called AccumulateDrawingObjects(), because that's what it's doing. WPF accumulates a bunch of drawing objects, which it retains to be able to draw the UI whenever it needs to. The magic of efficiently updating the UI is that you can actually change objects in that visual tree after OnRender().

For example, you can make a DrawingGroup "backingStore", and put that into the DrawingContext during OnRender. Then anytime you want to change the visual, you can DrawingGroup.Open(), put new drawing commands into it, and WPF will efficiently re-render that portion of the UI.

It looks like this:

DrawingGroup backingStore = new DrawingGroup();

protected override void OnRender(DrawingContext drawingContext) {      
    base.OnRender(drawingContext);            

    Render(); // put content into our backingStore
    drawingContext.DrawDrawing(backingStore);
}

// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {            
    var drawingContext = backingStore.Open();
    Render(drawingContext);
    drawingContext.Close();            
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top