郭卫东 发表于 2025-4-1 15:47:58

在WPF中利用VisualCollection创建复杂Adorner详解

在WPF中利用VisualCollection创建复杂Adorner详解

什么是Adorner?

Adorner是WPF中一种特别的UI装饰元素,它可以叠加在其他UI元素上提供额外的视觉结果或交互功能,而不会影响原有元素的布局。常见的应用场景包罗:


[*]选择框(当选中元素时出现的边框)
[*]调解大小手柄
[*]拖放指示器
[*]解释标记
VisualCollection简介

VisualCollection是WPF中用于管理一组Visual对象的集合类。在创建复杂Adorner时,它特别有用,因为:

[*]可以高效管理多个可视化子元素
[*]自动处置惩罚视觉树的添加和移除
[*]提供对子元素的索引访问
创建复杂Adorner的步调

1. 创建自定义Adorner类

起首需要从Adorner基类派生本身的类:
public class ComplexAdorner : Adorner
{
    private VisualCollection _visualChildren;
   
    public ComplexAdorner(UIElement adornedElement)
      : base(adornedElement)
    {
      _visualChildren = new VisualCollection(this);
      
      // 初始化你的装饰元素
      InitializeAdorner();
    }
   
    private void InitializeAdorner()
    {
      // 在这里创建和添加你的可视化元素
    }
   
    // 必须重写的方法
    protected override int VisualChildrenCount => _visualChildren.Count;
   
    protected override Visual GetVisualChild(int index) => _visualChildren;
   
    protected override Size ArrangeOverride(Size finalSize)
    {
      // 在这里布局你的子元素
      return finalSize;
    }
}
2. 添加可视化元素

在InitializeAdorner方法中,我们可以添加各种可视化元素:
private void InitializeAdorner()
{
    // 添加一个半透明矩形背景
    var background = new Rectangle
    {
      Fill = new SolidColorBrush(Color.FromArgb(50, 0, 0, 255)),
      RadiusX = 5,
      RadiusY = 5
    };
    _visualChildren.Add(background);
   
    // 添加一个文本标签
    var textBlock = new TextBlock
    {
      Text = "Adorner Text",
      Foreground = Brushes.White,
      Background = Brushes.Black,
      Padding = new Thickness(5)
    };
    _visualChildren.Add(textBlock);
   
    // 添加一个关闭按钮
    var closeButton = new Button
    {
      Content = "X",
      Width = 20,
      Height = 20,
      Background = Brushes.Red,
      Foreground = Brushes.White
    };
    closeButton.Click += CloseButton_Click;
    _visualChildren.Add(closeButton);
}
3. 实现布局逻辑

在ArrangeOverride方法中定义子元素的布局:
protected override Size ArrangeOverride(Size finalSize)
{
    // 背景覆盖整个装饰元素
    var background = _visualChildren as Rectangle;
    background.Arrange(new Rect(finalSize));
   
    // 文本标签放在左上角
    var textBlock = _visualChildren as TextBlock;
    textBlock.Arrange(new Rect(10, 10, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height));
   
    // 关闭按钮放在右上角
    var closeButton = _visualChildren as Button;
    closeButton.Arrange(new Rect(finalSize.Width - 30, 10, 20, 20));
   
    return finalSize;
}
4. 添加交互逻辑

可以为Adorner添加交互功能,例如上面的关闭按钮:
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
    var layer = AdornerLayer.GetAdornerLayer(AdornedElement);
    if (layer != null)
    {
      layer.Remove(this);
    }
}
5. 利用Adorner

在代码中利用自定义Adorner:
// 获取要装饰的元素
var elementToAdorn = myControl; // 替换为你的UI元素

// 获取或创建AdornerLayer
var layer = AdornerLayer.GetAdornerLayer(elementToAdorn) ?? new AdornerLayer();
if (layer.GetAdorners(elementToAdorn) == null)
{
    // 创建并添加Adorner
    var adorner = new ComplexAdorner(elementToAdorn);
    layer.Add(adorner);
}
完整示例:可调解大小的Adorner

下面是一个更复杂的示例,创建一个可以调解大小的Adorner:
public class ResizableAdorner : Adorner
{
    private VisualCollection _visualChildren;
    private Thumb _topLeft, _topRight, _bottomLeft, _bottomRight;
    private Rectangle _border;
   
    public ResizableAdorner(UIElement adornedElement)
      : base(adornedElement)
    {
      _visualChildren = new VisualCollection(this);
      
      BuildAdorner();
    }
   
    private void BuildAdorner()
    {
      _border = new Rectangle
      {
            Stroke = Brushes.Blue,
            StrokeThickness = 2,
            StrokeDashArray = new DoubleCollection(new double[] { 2, 2 })
      };
      _visualChildren.Add(_border);
      
      BuildThumb(ref _topLeft, Cursors.SizeNWSE);
      BuildThumb(ref _topRight, Cursors.SizeNESW);
      BuildThumb(ref _bottomLeft, Cursors.SizeNESW);
      BuildThumb(ref _bottomRight, Cursors.SizeNWSE);
      
      _bottomLeft.DragDelta += HandleBottomLeft;
      _bottomRight.DragDelta += HandleBottomRight;
      _topLeft.DragDelta += HandleTopLeft;
      _topRight.DragDelta += HandleTopRight;
    }
   
    private void BuildThumb(ref Thumb thumb, Cursor cursor)
    {
      thumb = new Thumb
      {
            Width = 10,
            Height = 10,
            Background = Brushes.Blue,
            Cursor = cursor
      };
      _visualChildren.Add(thumb);
    }
   
    private void HandleBottomLeft(object sender, DragDeltaEventArgs e)
    {
      var element = AdornedElement as FrameworkElement;
      if (element != null)
      {
            element.Width = Math.Max(0, element.Width - e.HorizontalChange);
            element.Height = Math.Max(0, element.Height + e.VerticalChange);
      }
    }
   
    // 其他Handle方法类似...
   
    protected override Size ArrangeOverride(Size finalSize)
    {
      _border.Arrange(new Rect(finalSize));
      
      _topLeft.Arrange(new Rect(-5, -5, 10, 10));
      _topRight.Arrange(new Rect(finalSize.Width - 5, -5, 10, 10));
      _bottomLeft.Arrange(new Rect(-5, finalSize.Height - 5, 10, 10));
      _bottomRight.Arrange(new Rect(finalSize.Width - 5, finalSize.Height - 5, 10, 10));
      
      return finalSize;
    }
   
    protected override int VisualChildrenCount => _visualChildren.Count;
    protected override Visual GetVisualChild(int index) => _visualChildren;
}
性能考虑

当利用VisualCollection创建复杂Adorner时,需要留意:

[*]只管减少可视化元素数量 - 每个Visual都会增加渲染开销
[*]避免频仍更新 - 批量更新比多次小更新更高效
[*]利用合适的缓存策略 - 对于静态元素,可以考虑缓存为位图
[*]实时移除不需要的Adorner - 不利用时从AdornerLayer中移除
总结

通过VisualCollection创建复杂Adorner是WPF中强大的UI扩展技术。它允许你:

[*]在不修改原有控件的环境下添加装饰和功能
[*]创建复杂的交互式覆盖层
[*]保持代码的构造性和可维护性
掌握这项技术可以极大地加强你的WPF应用程序的视觉结果和用户体验。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 在WPF中利用VisualCollection创建复杂Adorner详解