当前位置: 首页 > article >正文

WPF中的Adorner基础用法详解与实例

WPF中的Adorner基础用法详解与实例

Adorner(装饰器)是WPF中一个强大的功能,它允许开发者在现有UI元素之上叠加额外的视觉效果或交互功能,而不会影响原有布局系统。本文将详细介绍Adorner的基础概念、核心用法以及实际应用示例。

一、Adorner基本概念

1. 什么是Adorner

Adorner是WPF中一种特殊类型的FrameworkElement,用于向用户提供可视化提示。它位于AdornerLayer中,这是一个始终位于装饰元素或装饰元素集合上方的呈现图面。Adorner具有以下特点:

  • 独立于被装饰元素的布局系统,不会影响原有UI布局
  • 始终显示在被装饰元素之上,无法通过z-order改变其层级
  • 可以接收输入事件,但也可以通过设置IsHitTestVisible属性将事件传递给下层元素

2. Adorner的常见应用场景

Adorner在WPF中有多种用途,包括但不限于:

  1. 向UI元素添加功能控点(如调整大小、旋转、重新定位的把手)
  2. 提供视觉反馈以指示各种状态(如焦点状态、错误提示)
  3. 在UI元素上叠加视觉效果(如微信消息的数字角标)
  4. 从视觉上遮盖或重写UIElement的一部分或全部
  5. 实现类似Popup的浮动工具栏效果

二、Adorner核心用法

1. 创建自定义Adorner

要创建自定义Adorner,需要继承Adorner类并重写相关方法。最常见的是重写OnRender方法,使用DrawingContext绘制视觉效果。

public class SimpleAdorner : Adorner
{
    public SimpleAdorner(UIElement adornedElement) : base(adornedElement) { }
    
    protected override void OnRender(DrawingContext drawingContext)
    {
        Rect adornedElementRect = new Rect(this.AdornedElement.RenderSize);
        
        // 绘制红色边框
        Pen renderPen = new Pen(new SolidColorBrush(Colors.Red), 1.0);
        drawingContext.DrawRectangle(null, renderPen, adornedElementRect);
        
        base.OnRender(drawingContext);
    }
}

2. 将Adorner添加到元素

添加Adorner的标准流程:

  1. 获取目标元素的AdornerLayer
  2. 创建自定义Adorner实例
  3. 将Adorner添加到AdornerLayer中
// 获取目标元素的AdornerLayer
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(targetElement);

// 创建并添加Adorner
if(adornerLayer != null)
{
    adornerLayer.Add(new SimpleAdorner(targetElement));
}

3. 使用VisualCollection创建复杂Adorner

对于需要包含多个可视化子元素的复杂Adorner,可以使用VisualCollection:

public class ComplexAdorner : Adorner
{
    private VisualCollection _visuals;
    private Canvas _canvas;
    
    public ComplexAdorner(UIElement adornedElement) : base(adornedElement)
    {
        _visuals = new VisualCollection(this);
        _canvas = new Canvas();
        
        // 添加子元素到Canvas
        Rectangle rect = new Rectangle { Fill = Brushes.Red, Width = 10, Height = 10 };
        _canvas.Children.Add(rect);
        
        _visuals.Add(_canvas);
    }
    
    protected override int VisualChildrenCount => _visuals.Count;
    protected override Visual GetVisualChild(int index) => _visuals[index];
    
    protected override Size ArrangeOverride(Size finalSize)
    {
        _canvas.Arrange(new Rect(finalSize));
        return finalSize;
    }
}

三、Adorner实用示例

1. 基础装饰效果

示例:为按钮添加文字装饰

public class TextAdorner : Adorner
{
    public TextAdorner(UIElement adornedElement) : base(adornedElement) { }
    
    protected override void OnRender(DrawingContext drawingContext)
    {
        FormattedText text = new FormattedText(
            "New!!",
            CultureInfo.CurrentCulture,
            FlowDirection.LeftToRight,
            new Typeface("Arial"),
            12,
            Brushes.Red);
        
        drawingContext.DrawText(text, new Point(AdornedElement.RenderSize.Width - 20, 0));
    }
}

// 使用方式
AdornerLayer layer = AdornerLayer.GetAdornerLayer(myButton);
layer.Add(new TextAdorner(myButton));

2. 交互式Adorner

示例:创建可调整大小的Adorner

public class ResizeAdorner : Adorner
{
    private VisualCollection _visuals;
    private Rectangle _resizeHandle;
    
    public ResizeAdorner(UIElement adornedElement) : base(adornedElement)
    {
        _visuals = new VisualCollection(this);
        
        _resizeHandle = new Rectangle
        {
            Width = 10,
            Height = 10,
            Fill = Brushes.Blue,
            Cursor = Cursors.SizeNWSE
        };
        
        _resizeHandle.MouseLeftButtonDown += OnResizeHandleMouseDown;
        _visuals.Add(_resizeHandle);
    }
    
    private void OnResizeHandleMouseDown(object sender, MouseButtonEventArgs e)
    {
        // 实现调整大小逻辑
    }
    
    protected override Size ArrangeOverride(Size finalSize)
    {
        _resizeHandle.Arrange(new Rect(
            finalSize.Width - 10,
            finalSize.Height - 10,
            10, 10));
        
        return finalSize;
    }
    
    // 必须重写以下方法
    protected override int VisualChildrenCount => _visuals.Count;
    protected override Visual GetVisualChild(int index) => _visuals[index];
}

3. 动画Adorner

示例:创建带动画边框的Adorner

public class AnimatedBorderAdorner : Adorner
{
    private double _animationProgress;
    private AnimationClock _animationClock;
    
    public AnimatedBorderAdorner(UIElement adornedElement) : base(adornedElement)
    {
        StartAnimation();
    }
    
    private void StartAnimation()
    {
        DoubleAnimation animation = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(2)));
        animation.RepeatBehavior = RepeatBehavior.Forever;
        _animationClock = animation.CreateClock();
        _animationClock.CurrentTimeInvalidated += OnAnimationUpdate;
    }
    
    private void OnAnimationUpdate(object sender, EventArgs e)
    {
        _animationProgress = _animationClock.CurrentTime.Value.TotalSeconds / 2;
        InvalidateVisual(); // 强制重绘
    }
    
    protected override void OnRender(DrawingContext drawingContext)
    {
        Rect rect = new Rect(AdornedElement.RenderSize);
        Pen pen = new Pen(
            new LinearGradientBrush(Colors.Red, Colors.Yellow, _animationProgress * 360),
            2 * _animationProgress);
        
        drawingContext.DrawRectangle(null, pen, rect);
    }
}

4. 类似Popup的Adorner

示例:实现类似Word的选中文本工具栏

public class TextToolbarAdorner : Adorner
{
    private VisualCollection _visuals;
    private Grid _toolbar;
    
    public TextToolbarAdorner(UIElement adornedElement, Point position) : base(adornedElement)
    {
        _visuals = new VisualCollection(this);
        _toolbar = new Grid
        {
            Width = 120,
            Height = 30,
            Background = Brushes.LightGray
        };
        
        // 添加工具栏按钮等
        Button boldBtn = new Button { Content = "B" };
        _toolbar.Children.Add(boldBtn);
        
        _toolbar.Margin = new Thickness(position.X, position.Y - 35, 0, 0);
        _visuals.Add(_toolbar);
    }
    
    // 必须重写的方法
    protected override int VisualChildrenCount => _visuals.Count;
    protected override Visual GetVisualChild(int index) => _visuals[index];
    protected override Size ArrangeOverride(Size finalSize)
    {
        _toolbar.Arrange(new Rect(finalSize));
        return finalSize;
    }
}

// 使用示例
private void OnTextSelected(object sender, RoutedEventArgs e)
{
    Point selectionPosition = GetSelectionPosition(); // 获取选中文本位置
    AdornerLayer layer = AdornerLayer.GetAdornerLayer(textBox);
    layer.Add(new TextToolbarAdorner(textBox, selectionPosition));
}

四、Adorner高级主题

1. AdornerLayer的工作原理

AdornerLayer是Adorner的容器,WPF中通常由以下两种方式提供:

  1. AdornerDecorator:显式创建Adorner层
  2. ScrollContentPresenter:WPF内部隐式添加

在视觉树中,AdornerLayer通常位于最顶层,确保Adorner始终显示在其他内容之上。

2. 性能优化建议

  1. 尽量减少Adorner的重绘:对于复杂Adorner,避免频繁调用InvalidateVisual()
  2. 合理使用VisualCollection:对于包含多个子元素的Adorner,使用VisualCollection管理
  3. 适时移除不需要的Adorner:避免保留不再使用的Adorner

3. 与Popup的对比

特性AdornerPopup
布局系统独立布局系统参与主布局系统
层级控制始终在最顶层可通过ZIndex控制
事件处理可控制是否拦截事件通常拦截事件
使用场景装饰、视觉反馈浮动菜单、对话框

五、总结

Adorner是WPF中一个功能强大且灵活的特性,它允许开发者在现有UI元素之上添加各种视觉效果和交互功能,而不会破坏原有的布局结构。通过本文的介绍,您应该已经掌握了Adorner的基本用法和常见应用场景。

在实际项目中,Adorner可以用于实现:

  1. 视觉提示和状态反馈(如焦点框、错误提示)
  2. 交互增强(如调整大小、旋转把手)
  3. 自定义装饰效果(如角标、动画边框)
  4. 浮动工具栏和上下文菜单

通过合理利用Adorner,可以大大增强WPF应用程序的用户体验和视觉效果。


http://www.kler.cn/a/611752.html

相关文章:

  • True strength lies in embracing vulnerability as a gateway to growth.
  • 23种设计模式-责任链(Chain of Responsibility)设计模式
  • AigcPanel v0.8.1 焕新上线:界面升级,新增 Spark - TTS、Fish Speech 模型支持
  • [Effective C++]条款24:若所有参数皆需类型转换,请为此采用non-menber函数
  • 【go微服务】跨语言RPC接口工具--protobuf语法与原理解读以及应用实战
  • [MRCTF2020]套娃
  • 实战 | 基于 SpringBoot + UniApp 打造国际版打车系统:架构设计与性能优化全解析
  • SQL优化 | OceanBase是否遵循最左匹配原则?(三)
  • MDC的原理是什么?
  • 计算机二级(C语言)考试高频考点总汇(二)—— 控制流、函数、数组和指针
  • 向量数据库的适用场景与局限性分析
  • Java爬虫如何解析返回的JSON数据?
  • Dynamic Soft Contrastive Learning for Time Series Anomaly Detection 解读
  • 【2025】基于springboot+uniapp的企业培训打卡小程序设计与实现(源码、万字文档、图文修改、调试答疑)
  • 套接字Socket
  • 算法-深度优先搜索
  • ubuntu单机部署redis集群
  • HarmonyOS NEXT 鸿蒙中关系型数据库@ohos.data.relationalStore API 9+
  • IP 分片重组与 TCP 会话重组
  • 二分查找模板--从题目中讲解三大二分模板