在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[index];
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[0] as Rectangle;
background.Arrange(new Rect(finalSize));
// 文本标签放在左上角
var textBlock = _visualChildren[1] as TextBlock;
textBlock.Arrange(new Rect(10, 10, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height));
// 关闭按钮放在右上角
var closeButton = _visualChildren[2] 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[index];
}
性能考虑
当使用VisualCollection创建复杂Adorner时,需要注意:
- 尽量减少可视化元素数量 - 每个Visual都会增加渲染开销
- 避免频繁更新 - 批量更新比多次小更新更高效
- 使用合适的缓存策略 - 对于静态元素,可以考虑缓存为位图
- 及时移除不需要的Adorner - 不使用时从AdornerLayer中移除
总结
通过VisualCollection创建复杂Adorner是WPF中强大的UI扩展技术。它允许你:
- 在不修改原有控件的情况下添加装饰和功能
- 创建复杂的交互式覆盖层
- 保持代码的组织性和可维护性
掌握这项技术可以极大地增强你的WPF应用程序的视觉效果和用户体验。