Анимация внутри украшения (вызов 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));
    }
}

person Jason Anderson    schedule 24.02.2009    source источник


Ответы (2)


Ответ на ваш вопрос: используйте InvalidateVisual для вызвать повторный вызов OnRender

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

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

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

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

person Franci Penov    schedule 24.02.2009

Очень важно понимать, что 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();            
}
person David Jeske    schedule 10.06.2017