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

打印 上一主题 下一主题

主题 1733|帖子 1733|积分 5199

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

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

什么是Adorner?

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


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

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

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

1. 创建自定义Adorner类

起首需要从Adorner基类派生本身的类:
  1. public class ComplexAdorner : Adorner
  2. {
  3.     private VisualCollection _visualChildren;
  4.    
  5.     public ComplexAdorner(UIElement adornedElement)
  6.         : base(adornedElement)
  7.     {
  8.         _visualChildren = new VisualCollection(this);
  9.         
  10.         // 初始化你的装饰元素
  11.         InitializeAdorner();
  12.     }
  13.    
  14.     private void InitializeAdorner()
  15.     {
  16.         // 在这里创建和添加你的可视化元素
  17.     }
  18.    
  19.     // 必须重写的方法
  20.     protected override int VisualChildrenCount => _visualChildren.Count;
  21.    
  22.     protected override Visual GetVisualChild(int index) => _visualChildren[index];
  23.    
  24.     protected override Size ArrangeOverride(Size finalSize)
  25.     {
  26.         // 在这里布局你的子元素
  27.         return finalSize;
  28.     }
  29. }
复制代码
2. 添加可视化元素

在InitializeAdorner方法中,我们可以添加各种可视化元素:
  1. private void InitializeAdorner()
  2. {
  3.     // 添加一个半透明矩形背景
  4.     var background = new Rectangle
  5.     {
  6.         Fill = new SolidColorBrush(Color.FromArgb(50, 0, 0, 255)),
  7.         RadiusX = 5,
  8.         RadiusY = 5
  9.     };
  10.     _visualChildren.Add(background);
  11.    
  12.     // 添加一个文本标签
  13.     var textBlock = new TextBlock
  14.     {
  15.         Text = "Adorner Text",
  16.         Foreground = Brushes.White,
  17.         Background = Brushes.Black,
  18.         Padding = new Thickness(5)
  19.     };
  20.     _visualChildren.Add(textBlock);
  21.    
  22.     // 添加一个关闭按钮
  23.     var closeButton = new Button
  24.     {
  25.         Content = "X",
  26.         Width = 20,
  27.         Height = 20,
  28.         Background = Brushes.Red,
  29.         Foreground = Brushes.White
  30.     };
  31.     closeButton.Click += CloseButton_Click;
  32.     _visualChildren.Add(closeButton);
  33. }
复制代码
3. 实现布局逻辑

在ArrangeOverride方法中定义子元素的布局:
  1. protected override Size ArrangeOverride(Size finalSize)
  2. {
  3.     // 背景覆盖整个装饰元素
  4.     var background = _visualChildren[0] as Rectangle;
  5.     background.Arrange(new Rect(finalSize));
  6.    
  7.     // 文本标签放在左上角
  8.     var textBlock = _visualChildren[1] as TextBlock;
  9.     textBlock.Arrange(new Rect(10, 10, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height));
  10.    
  11.     // 关闭按钮放在右上角
  12.     var closeButton = _visualChildren[2] as Button;
  13.     closeButton.Arrange(new Rect(finalSize.Width - 30, 10, 20, 20));
  14.    
  15.     return finalSize;
  16. }
复制代码
4. 添加交互逻辑

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

在代码中利用自定义Adorner:
  1. // 获取要装饰的元素
  2. var elementToAdorn = myControl; // 替换为你的UI元素
  3. // 获取或创建AdornerLayer
  4. var layer = AdornerLayer.GetAdornerLayer(elementToAdorn) ?? new AdornerLayer();
  5. if (layer.GetAdorners(elementToAdorn) == null)
  6. {
  7.     // 创建并添加Adorner
  8.     var adorner = new ComplexAdorner(elementToAdorn);
  9.     layer.Add(adorner);
  10. }
复制代码
完整示例:可调解大小的Adorner

下面是一个更复杂的示例,创建一个可以调解大小的Adorner:
  1. public class ResizableAdorner : Adorner
  2. {
  3.     private VisualCollection _visualChildren;
  4.     private Thumb _topLeft, _topRight, _bottomLeft, _bottomRight;
  5.     private Rectangle _border;
  6.    
  7.     public ResizableAdorner(UIElement adornedElement)
  8.         : base(adornedElement)
  9.     {
  10.         _visualChildren = new VisualCollection(this);
  11.         
  12.         BuildAdorner();
  13.     }
  14.    
  15.     private void BuildAdorner()
  16.     {
  17.         _border = new Rectangle
  18.         {
  19.             Stroke = Brushes.Blue,
  20.             StrokeThickness = 2,
  21.             StrokeDashArray = new DoubleCollection(new double[] { 2, 2 })
  22.         };
  23.         _visualChildren.Add(_border);
  24.         
  25.         BuildThumb(ref _topLeft, Cursors.SizeNWSE);
  26.         BuildThumb(ref _topRight, Cursors.SizeNESW);
  27.         BuildThumb(ref _bottomLeft, Cursors.SizeNESW);
  28.         BuildThumb(ref _bottomRight, Cursors.SizeNWSE);
  29.         
  30.         _bottomLeft.DragDelta += HandleBottomLeft;
  31.         _bottomRight.DragDelta += HandleBottomRight;
  32.         _topLeft.DragDelta += HandleTopLeft;
  33.         _topRight.DragDelta += HandleTopRight;
  34.     }
  35.    
  36.     private void BuildThumb(ref Thumb thumb, Cursor cursor)
  37.     {
  38.         thumb = new Thumb
  39.         {
  40.             Width = 10,
  41.             Height = 10,
  42.             Background = Brushes.Blue,
  43.             Cursor = cursor
  44.         };
  45.         _visualChildren.Add(thumb);
  46.     }
  47.    
  48.     private void HandleBottomLeft(object sender, DragDeltaEventArgs e)
  49.     {
  50.         var element = AdornedElement as FrameworkElement;
  51.         if (element != null)
  52.         {
  53.             element.Width = Math.Max(0, element.Width - e.HorizontalChange);
  54.             element.Height = Math.Max(0, element.Height + e.VerticalChange);
  55.         }
  56.     }
  57.    
  58.     // 其他Handle方法类似...
  59.    
  60.     protected override Size ArrangeOverride(Size finalSize)
  61.     {
  62.         _border.Arrange(new Rect(finalSize));
  63.         
  64.         _topLeft.Arrange(new Rect(-5, -5, 10, 10));
  65.         _topRight.Arrange(new Rect(finalSize.Width - 5, -5, 10, 10));
  66.         _bottomLeft.Arrange(new Rect(-5, finalSize.Height - 5, 10, 10));
  67.         _bottomRight.Arrange(new Rect(finalSize.Width - 5, finalSize.Height - 5, 10, 10));
  68.         
  69.         return finalSize;
  70.     }
  71.    
  72.     protected override int VisualChildrenCount => _visualChildren.Count;
  73.     protected override Visual GetVisualChild(int index) => _visualChildren[index];
  74. }
复制代码
性能考虑

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

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

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

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

郭卫东

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表