Анимация внутри украшения (вызов OnRender)

Я использую Adorner в .NET 3.5, и я могу рисовать, переопределив OnRender, но мне нужна возможность перерисовать украшение, чтобы изменить его внешний вид.

По сути, я ищу способ очистить контекст рисования и снова вызвать OnRender. Как лучше всего это сделать или есть лучший подход?

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));
    }
}

Ответов (2)

Решение

Ответ на ваш вопрос - использовать InvalidateVisualдля повторного вызова OnRender.

Однако я бы посоветовал вместо того, чтобы самостоятельно рисовать в OnRender, использовать стандартные стили и шаблоны визуального дерева для создания фактического визуального оформления украшения. Это также означает, что вы можете запускать в нем стандартные анимации XAML с помощью раскадровок.

Если вы хотите использовать этот подход, в своем классе украшения вам необходимо:

  • в конструкторе вызовите base.AddVisualChild()или создайте свою собственную коллекцию визуальных элементов с визуальными элементами, которые вы хотите показать в украшении
  • переопределить, ArrangeOverride(Size size)чтобы правильно расположить детей;
  • переопределить, VisualChildrenCountчтобы вернуть количество дочерних элементов в визуальном дереве украшения;
  • переопределить, GetCisualChild(int index)чтобы вернуть конкретного ребенка.

Вы можете взглянуть на образец ResizingAdorner MSDN для получения дополнительной информации.

Очень важно понимать, что WPF не похож на Windows.Forms. OnRender() действительно следует называть AccumulateDrawingObjects(), потому что это то, что он делает. WPF накапливает набор объектов рисования, которые он сохраняет, чтобы иметь возможность рисовать пользовательский интерфейс, когда это необходимо. Магия эффективного обновления пользовательского интерфейса является то , что вы можете изменить объекты в этом визуальном дереве после OnRender() .

Например, вы можете сделать DrawingGroup «backingStore», и положить , что в DrawingContext во время OnRender . Затем в любое время, когда вы захотите изменить визуальный элемент, вы можете DrawingGroup.Open() поместить в него новые команды рисования, и WPF эффективно повторно отобразит эту часть пользовательского интерфейса.

Это выглядит так:

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();            
}