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

news2025/4/3 4:48:04

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.coloradmin.cn/o/2326286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【React】基于 React+Tailwind 的 EmojiPicker 选择器组件

1.背景 React 写一个 EmojiPicker 组件,基于 emoji-mart 组件二次封装。支持添加自定义背景 、Emoji 图标选择!并在页面上展示! 2.技术栈 emoji-mart/data 、emoji-mart : emoji 图标库、元数据 tailwindcss: 原子化 CSS 样式库 antd : 组…

02-Docker 使用

docker:快速构建、运行、管理应用的工具,可以帮助我们下载应用镜像,创建并运行镜像的容器,从而快速部署应用 1、部署mysql 先停掉虚拟机中的MySQL,确保你的虚拟机已经安装Docker,且网络开通的情况下,执行下面命令即可安装MySQL(注意:若服务器上已经有mysql 占用了330…

html5时钟升级!支持切换深浅模式 Canvas实现现代化动态时钟

HTML5 Canvas实现现代化动态时钟 这里写目录标题 HTML5 Canvas实现现代化动态时钟项目介绍技术实现1. 项目架构2. Canvas绘图实现2.1 表盘绘制2.2 刻度绘制2.3 指针绘制 3. 动画效果4. 主题切换 项目亮点技术要点总结项目收获改进方向结语 项目介绍 本项目使用HTML5 Canvas技术…

MOE-1 基本认识

解读一下MOE架构,部分内容图片参考自油管。 首先来简单了解一下什么是MoE(Mixture of Experts,专家混合) MoE(Mixture of Experts)是一种深度学习架构,其核心思想是通过**多个专家网络&#xf…

【C++接入大模型】WinHTTP类封装:实现对话式大模型接口访问

一、类设计概述 近期准备用C做一些大预言模型方面的开发,先期计划实现C调用公共的大模型Web接口,因为之前没做过C的Web开发,经验少,所以对比了一些主流的框架,包括实际测试验证。以下是Windows平台下主流C HTTP库的对…

【银河麒麟高级服务器操作系统 】虚拟机运行数据库存储异常现象分析及处理全流程

更多银河麒麟操作系统产品及技术讨论,欢迎加入银河麒麟操作系统官方论坛 https://forum.kylinos.cn 了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer…

文件分享系统--开源的可视化文件共享管理工具

家里有公网,经常要发文件给别人,文件几个G发送还要云盘或者倒手一次才行,所以弄了个文件分享系统,这个是用字节的 AI Trae 写的,反正反复折腾还是弄出来了。东西挺好用,可以拖拽多个文件上传也可以手动添加…

【力扣刷题实战】寻找数组的中心下标

大家好,我是小卡皮巴拉 文章目录 目录 力扣题目:寻找数组的中心下标 题目描述 解题思路 问题理解 算法选择 具体思路 解题要点 完整代码(C) 兄弟们共勉 !!! 每篇前言 博客主页&#…

LearnOpenGL小练习(QOpenGLWidget版本)

你好,三角形 1.绘制两个彼此相连的三角形 画两个独立的三角形,给出两个三角形顶点,使用GL_TRIANGLES绘图即可。 关键代码 void MyOpenglWgt::initializeGL() {initializeOpenGLFunctions(); // 1. 创建ShaderProgram着色器:加…

基于OpenCV+MediaPipe手部追踪

一、技术栈 1. OpenCV(Open Source Computer Vision Library) 性质:开源计算机视觉库(Library) 主要功能: 图像/视频的基础处理(读取、裁剪、滤波、色彩转换等) 特征检测&#xf…

十五届蓝桥杯省赛Java B组(持续更新..)

目录 十五届蓝桥杯省赛Java B组第一题:报数第二题:类斐波那契数第三题:分布式队列第四题:食堂第五题:最优分组第六题:星际旅行第七题:LITS游戏第八题:拼十字 十五届蓝桥杯省赛Java B…

蓝耘平台API深度剖析:如何高效实现AI应用联动

目录 一、蓝耘平台简介 1.1 蓝耘通义大模型 1.2 蓝耘云计算资源 1.3 蓝耘API与微服务 二、 蓝耘平台应用联动场景 2.1 数据采集与预处理联动 2.2 模型推理与后端服务联动 2.3 跨平台联动 三、蓝耘平台注册体验功能 3.1 注册 3.2 体验蓝耘MaaS平台如何使用海螺AI生成视频…

缓存 “三剑客”

缓存 “三剑客” 问题 如何保证 Redis 缓存和数据库的一致性? 1. 缓存穿透 缓存穿透是指请求一个不存在的数据,缓存层和数据库层都没有这个数据,这种请求会穿透缓存直接到数据库进行查询 解决方案: 1.1 缓存空值或特殊值 查一…

ComfyUi教程之阿里的万象2.1视频模型

ComfyUi教程之阿里的万象2.1视频模型 官网Wan 2.1 特点 一、本地安装1.1克隆仓库1.2 安装依赖(1.3)下载模型(1.4)CUDA和CUDNN 二、 使用体验(2.1)官方例子(2.2)执行过程(…

Leetcode 寻找两个正序数组的中位数

💯 完全正确!!你这段话可以直接当作这道题的**“思路总览”模板答案**了,结构清晰、逻辑严谨、几乎没有遗漏任何关键点👏 不过我可以帮你稍微精炼一下语言,使它在保留你原本意思的基础上更具表达力和条理性…

C#测试Excel开源组件ExcelDataReader

使用微软的com组件Microsoft.office.Interop.Excel读写Excel文件虽然可用,但是列多、行多的时候速度很慢,之前测试过Sylvan.Data.Excel包的用法,如果只是读取Excel文件内容的话,还可以使用ExcelDataReader包,后者是C#开…

手机零售行业的 AI 破局与创新降本实践 | OceanBase DB大咖说

OceanBase《DB 大咖说》第 20 期,我们邀请了九机与九讯云的技术总负责人,李远军,为我们分享手机零售企业如何借力分布式数据库OceanBase,赋能 AI 场景,并通过简化架构实现成本管控上的突破与创新。 李远军于2016年加入…

SpringBoot整合LogStash,LogStash采集服务器日志

LogStash 1. 下载 版本支持兼容表https://www.elastic.co/cn/support/matrix 版本: 7.16.x 的最后一个版本 https://www.elastic.co/downloads/past-releases/logstash-7-16-3 需要提前安装好jdk1.8和ES, 此处不在演示 2. 安装 tar -xvf logstash-7.16.3-linux-x86_64.tar.gz…

目前市场上,好用的校招系统是哪个?

在数字化浪潮的推动下,校园招聘已从传统的“海投简历线下宣讲”模式全面转向智能化、数据化。面对每年数百万应届生的激烈竞争,企业如何在短时间内精准筛选人才、优化招聘流程、降低人力成本?答案或许藏在AI驱动的校招管理系统中。而在这场技…

SharpBrowser:用C#打造超快的个性化开源浏览器!

推荐一个基于.Net 8 和 CefSharp开发的开源浏览器。 01 项目简介 SharpBrowser 是一个用 C# 和 CefSharp 开发的全功能网页浏览器。它声称是最快的开源 C# 网页浏览器,渲染网页的速度比谷歌浏览器还快,因为其使用轻量级的 CEF 渲染器。 经过比较所有可…