Использование объектов DrawingVisual

В данной теме представлен обзор использования объектов DrawingVisual на визуальном уровне WPF.

Объект DrawingVisual

DrawingVisual — это упрощенный класс рисования, который используется для отрисовки фигур, изображений и текста. Этот класс считается упрощенным, так как не предоставляет средств для работы с разметкой и обработку событий, что повышает его производительность. Поэтому этот класс идеально подходит для фоновых рисунков или клипов.

Контейнер размещения DrawingVisual

Чтобы использовать объекты DrawingVisual, необходимо создать для них контейнер размещения. Объект контейнера размещения должен быть производным от класса FrameworkElement, который обеспечивает поддержку средств для работы с разметкой и обработки событий, которых не хватает классу DrawingVisual. Объект контейнера размещения не отображает видимых свойств, так как его основное назначение — включать дочерние объекты. Однако свойству Visibility контейнера размещения должно быть присвоено значение Visible; в противном случае ни один из его дочерних элементов не будет виден.

При создании объекта контейнера размещения для визуальных объектов необходимо сохранить ссылки визуального объекта в коллекции VisualCollection. Для добавления визуального объекта в контейнер размещения используйте метод Add. В следующем примере создается объект контейнера размещения и в его коллекцию VisualCollection добавляются три визуальных объекта.

// Create a host visual derived from the FrameworkElement class.
// This class provides layout, event handling, and container support for
// the child visual objects.
public class MyVisualHost : FrameworkElement
{
    // Create a collection of child visual objects.
    private VisualCollection _children;

    public MyVisualHost()
    {
        _children = new VisualCollection(this);
        _children.Add(CreateDrawingVisualRectangle());
        _children.Add(CreateDrawingVisualText());
        _children.Add(CreateDrawingVisualEllipses());

        // Add the event handler for MouseLeftButtonUp.
        this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
    }
' Create a host visual derived from the FrameworkElement class.
' This class provides layout, event handling, and container support for
' the child visual objects.
Public Class MyVisualHost
    Inherits FrameworkElement
    ' Create a collection of child visual objects.
    Private _children As VisualCollection

    Public Sub New()
        _children = New VisualCollection(Me)
        _children.Add(CreateDrawingVisualRectangle())
        _children.Add(CreateDrawingVisualText())
        _children.Add(CreateDrawingVisualEllipses())

        ' Add the event handler for MouseLeftButtonUp.
        AddHandler MouseLeftButtonUp, AddressOf MyVisualHost_MouseLeftButtonUp
    End Sub

Примечание.

Полный код примера, из которого был взят предыдущий пример кода, см. в разделе Проверка нажатия с помощью примера DrawingVisuals.

Создание объектов DrawingVisual

Объект DrawingVisual создается без графического содержимого. Чтобы добавить текстовое, графическое содержимое или изображение, можно извлечь контекст объекта DrawingContext и добавить в него нужный визуальный элемент. DrawingContext возвращается путем вызова метода RenderOpen объекта DrawingVisual.

Чтобы нарисовать прямоугольник в DrawingContext, используйте метод DrawRectangle объекта DrawingContext. Существуют аналогичные методы для рисования других объектов. Завершив добавление графического содержимого в DrawingContext, вызовите метод Close, чтобы закрыть DrawingContext и сохранить содержимое.

В следующем примере создается объект DrawingVisual и в его контексте DrawingContext рисуется прямоугольник.

// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
    DrawingVisual drawingVisual = new DrawingVisual();

    // Retrieve the DrawingContext in order to create new drawing content.
    DrawingContext drawingContext = drawingVisual.RenderOpen();

    // Create a rectangle and draw it in the DrawingContext.
    Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
    drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);

    // Persist the drawing content.
    drawingContext.Close();

    return drawingVisual;
}
' Create a DrawingVisual that contains a rectangle.
Private Function CreateDrawingVisualRectangle() As DrawingVisual
    Dim drawingVisual As New DrawingVisual()

    ' Retrieve the DrawingContext in order to create new drawing content.
    Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()

    ' Create a rectangle and draw it in the DrawingContext.
    Dim rect As New Rect(New Point(160, 100), New Size(320, 80))
    drawingContext.DrawRectangle(Brushes.LightBlue, CType(Nothing, Pen), rect)

    ' Persist the drawing content.
    drawingContext.Close()

    Return drawingVisual
End Function

Создание переопределений для членов класса FrameworkElement

Объект контейнера размещения отвечает за управление своей коллекцией визуальных объектов. Для этого в контейнере размещения должно быть реализовано переопределение членов производного класса FrameworkElement.

Ниже приводятся два члена класса, которые необходимо переопределить.

  • GetVisualChild: возвращает дочерний элемент по указанному индексу из коллекции дочерних элементов.

  • VisualChildrenCount: получает количество визуальных дочерних элементов внутри этого элемента.

В следующем примере реализовано переопределение двух членов класса FrameworkElement.


// Provide a required override for the VisualChildrenCount property.
protected override int VisualChildrenCount
{
    get { return _children.Count; }
}

// Provide a required override for the GetVisualChild method.
protected override Visual GetVisualChild(int index)
{
    if (index < 0 || index >= _children.Count)
    {
        throw new ArgumentOutOfRangeException();
    }

    return _children[index];
}


' Provide a required override for the VisualChildrenCount property.
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
    Get
        Return _children.Count
    End Get
End Property

' Provide a required override for the GetVisualChild method.
Protected Overrides Function GetVisualChild(ByVal index As Integer) As Visual
    If index < 0 OrElse index >= _children.Count Then
        Throw New ArgumentOutOfRangeException()
    End If

    Return _children(index)
End Function

Поддержка проверки нажатия

Объект контейнера размещения может предоставлять средства обработки событий, даже если он не отображает ни одного видимого свойства. Однако его свойству Visibility необходимо задать значение Visible. Это позволяет создать процедуру обработки событий для контейнера размещения. Эта процедура сможет перехватывать события мыши, например отпускание левой кнопки мыши. Затем процедура обработки событий может реализовать проверку нажатия, вызвав метод HitTest. Параметр метода HitTestResultCallback ссылается на определенную пользователем процедуру, которую можно использовать для определения результата проверки нажатия.

В следующем примере реализована поддержка проверки нажатия для объекта узла контейнера и его дочерних объектов.

// Capture the mouse event and hit test the coordinate point value against
// the child visual objects.
void MyVisualHost_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    // Retrieve the coordinates of the mouse button event.
    System.Windows.Point pt = e.GetPosition((UIElement)sender);

    // Initiate the hit test by setting up a hit test result callback method.
    VisualTreeHelper.HitTest(this, null, new HitTestResultCallback(myCallback), new PointHitTestParameters(pt));
}

// If a child visual object is hit, toggle its opacity to visually indicate a hit.
public HitTestResultBehavior myCallback(HitTestResult result)
{
    if (result.VisualHit.GetType() == typeof(DrawingVisual))
    {
        if (((DrawingVisual)result.VisualHit).Opacity == 1.0)
        {
            ((DrawingVisual)result.VisualHit).Opacity = 0.4;
        }
        else
        {
            ((DrawingVisual)result.VisualHit).Opacity = 1.0;
        }
    }

    // Stop the hit test enumeration of objects in the visual tree.
    return HitTestResultBehavior.Stop;
}
' Capture the mouse event and hit test the coordinate point value against
' the child visual objects.
Private Sub MyVisualHost_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
    ' Retrieve the coordinates of the mouse button event.
    Dim pt As Point = e.GetPosition(CType(sender, UIElement))

    ' Initiate the hit test by setting up a hit test result callback method.
    VisualTreeHelper.HitTest(Me, Nothing, New HitTestResultCallback(AddressOf myCallback), New PointHitTestParameters(pt))
End Sub

' If a child visual object is hit, toggle its opacity to visually indicate a hit.
Public Function myCallback(ByVal result As HitTestResult) As HitTestResultBehavior
    If result.VisualHit.GetType() Is GetType(DrawingVisual) Then
        If (CType(result.VisualHit, DrawingVisual)).Opacity = 1.0 Then
            CType(result.VisualHit, DrawingVisual).Opacity = 0.4
        Else
            CType(result.VisualHit, DrawingVisual).Opacity = 1.0
        End If
    End If

    ' Stop the hit test enumeration of objects in the visual tree.
    Return HitTestResultBehavior.Stop
End Function

См. также