装饰器概述

装饰器是一种特殊类型的 FrameworkElement,用于向用户提供视觉提示。 装饰器有很多用途,可用来向元素添加功能句柄,或者提供有关某个控件的状态信息。

关于装饰器

Adorner 是绑定到 UIElement 的自定义 FrameworkElement。 装饰器在 AdornerLayer 中呈现,它是始终位于装饰元素或装饰元素集合之上的呈现表面。 装饰器的呈现独立于装饰器绑定到的 UIElement 的呈现。 装饰器通常使用位于装饰元素左上部的标准 2D 坐标原点,相对于其绑定到的元素进行定位。

装饰器的常见应用包括:

  • UIElement 添加功能句柄,使用户能够以某种方式操作元素(调整大小、旋转、重新定位等)。
  • 提供视觉反馈以指示各种状态,或者响应各种事件。
  • UIElement 上叠加视觉装饰。
  • 以视觉方式遮盖或覆盖 UIElement 的一部分或全部。

Windows Presentation Foundation (WPF) 为装饰视觉元素提供了一个基本框架。 下表列出了装饰对象时使用的主要类型及其用途。 下面是几个用法示例:

说明
Adorner 一个抽象基类,从中继承所有的具体装饰器实现。
AdornerLayer 一个类,表示一个或多个装饰元素的装饰器的呈现层。
AdornerDecorator 一个类,使装饰器层与元素集合相关联。

实现自定义装饰器

Windows Presentation Foundation (WPF) 提供的装饰器框架主要用于支持创建自定义装饰器。 可通过实现从抽象 Adorner 类中继承的类创建自定义装饰器。

注意

Adorner 的父级是呈现 AdornerAdornerLayer,而不是被装饰的元素。

下面的示例展示了实现简单装饰器的类。 示例装饰器只是用圆圈装饰 UIElement 的角。

// Adorners must subclass the abstract base class Adorner.
public class SimpleCircleAdorner : Adorner
{
  // Be sure to call the base class constructor.
  public SimpleCircleAdorner(UIElement adornedElement)
    : base(adornedElement)
  {
  }

  // A common way to implement an adorner's rendering behavior is to override the OnRender
  // method, which is called by the layout system as part of a rendering pass.
  protected override void OnRender(DrawingContext drawingContext)
  {
    Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);

    // Some arbitrary drawing implements.
    SolidColorBrush renderBrush = new SolidColorBrush(Colors.Green);
    renderBrush.Opacity = 0.2;
    Pen renderPen = new Pen(new SolidColorBrush(Colors.Navy), 1.5);
    double renderRadius = 5.0;

    // Draw a circle at each corner.
    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, renderRadius, renderRadius);
    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, renderRadius, renderRadius);
    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, renderRadius, renderRadius);
    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, renderRadius, renderRadius);
  }
}
Public Class SimpleCircleAdorner
    Inherits Adorner
    Sub New(ByVal adornedElement As UIElement)
        MyBase.New(adornedElement)
    End Sub

    Protected Overrides Sub OnRender(ByVal drawingContext As System.Windows.Media.DrawingContext)
        MyBase.OnRender(drawingContext)
        Dim adornedElementRect As New Rect(AdornedElement.DesiredSize)
        Dim renderBrush As New SolidColorBrush(Colors.Green)
        renderBrush.Opacity = 0.2
        Dim renderPen As New Pen(New SolidColorBrush(Colors.Navy), 1.5)
        Dim renderRadius As Double
        renderRadius = 5.0

        'Draw a circle at each corner.
        drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, renderRadius, renderRadius)
        drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, renderRadius, renderRadius)
        drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, renderRadius, renderRadius)
        drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, renderRadius, renderRadius)
    End Sub
End Class

下图显示了应用于 TextBox 的 SimpleCircleAdorner:

Screenshot that shows an adorned text box.

装饰器的呈现行为

请务必注意,装饰器不包括任何继承呈现行为,确保装饰器呈现是装饰器实施者的责任。 实现呈现行为的一种常见方法是覆盖 OnRender 方法并使用一个或多个 DrawingContext 对象根据需要呈现装饰器的视觉效果(如上例所示)。

注意

放置在装饰器层中的任何内容将呈现在设置的其他任何样式的顶部。 换言之,装饰器始终以可见的方式位于顶部,无法使用 z 顺序重写。

事件和命中测试

装饰器像任何其他 FrameworkElement 一样接收输入事件。 因为装饰器的 z 顺序总是高于它所装饰的元素,所以装饰器接收可能用于底层装饰元素的输入事件(例如 DropMouseMove)。 装饰器可以侦听某些输入事件,并通过重新引发这些事件将它们传递到基础装饰元素。

要对装饰器下的元素启用直通命中测试,请将装饰器上的命中测试 IsHitTestVisible 属性设置为 false。 有关命中测试的详细信息,请参阅视觉层中的命中测试

装饰单个 UIElement

若要将装饰器绑定到特定的 UIElement,请按照以下步骤操作:

  1. 调用静态方法 GetAdornerLayer 以获取要装饰的 UIElementAdornerLayer 对象。 GetAdornerLayer 从指定的 UIElement 开始向上遍历可视化树,并返回它找到的第一个装饰层。 (如果未发现装饰器层,该方法将返回 null。)

  2. 调用 Add 方法将装饰器绑定到目标 UIElement

以下示例将 SimpleCircleAdorner(如上所示)绑定到名为 myTextBox 的 TextBox

myAdornerLayer = AdornerLayer.GetAdornerLayer(myTextBox);
myAdornerLayer.Add(new SimpleCircleAdorner(myTextBox));
myAdornerLayer = AdornerLayer.GetAdornerLayer(myTextBox)
myAdornerLayer.Add(New SimpleCircleAdorner(myTextBox))

注意

目前不支持使用 Extensible Application Markup Language (XAML) 将装饰器绑定到另一个元素。

装饰面板的子级

若要将装饰器绑定到 Panel 的子元素,请按照以下步骤操作:

  1. 调用 static 方法 GetAdornerLayer 为要装饰其子元素的元素查找装饰层。

  2. 依次枚举父元素的子级并调用 Add 方法,以将装饰器绑定到每个子元素。

以下示例将 SimpleCircleAdorner(如上所示)绑定到名为 myStackPanel 的 StackPanel 的子级

foreach (UIElement toAdorn in myStackPanel.Children)
  myAdornerLayer.Add(new SimpleCircleAdorner(toAdorn));
For Each toAdorn As UIElement In myStackPanel.Children
    myAdornerLayer.Add(New SimpleCircleAdorner(toAdorn))
Next

另请参阅