使用 DrawingVisual 对象

本主题概述如何在 WPF 可视化层中使用 DrawingVisual 对象。

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

注意

有关从中提取上述代码示例的完整代码示例,请参阅使用 DrawingVisual 的命中测试示例

创建 DrawingVisual 对象

创建 DrawingVisual 对象时,该对象没有绘图内容。 可以通过检索对象的 DrawingContext 并在其中进行绘制来添加文本、图形或图像内容。 通过调用 DrawingVisual 对象的 RenderOpen 方法返回 DrawingContext

要在 DrawingContext 中绘制一个矩形,请使用 DrawingContext 对象的 DrawRectangle 方法。 对于绘制其他类型的内容,存在类似的方法。 完成将内容绘制到 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 类实现成员替代。

下表介绍了必须重写的两个成员:

在下面的示例中,实现了两个 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

另请参阅