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

从 WPF 到 Avalonia 的迁移系列实战篇4:控件模板与 TemplatedControl

从 WPF 到 Avalonia 的迁移系列实战篇4:控件模板与 TemplatedControl

我的GitHub仓库Avalonia学习项目包含完整的Avalonia实践案例与代码对比。
我的gitcode仓库是Avalonia学习项目

在前几篇中,我们聊过了依赖属性、路由事件、资源等迁移相关的基础,这一篇我们进入 自定义控件开发
如果你在 WPF 中做过复杂控件,就一定绕不开 Control + ControlTemplate 的组合。

那么问题来了:

Avalonia 中,我们该如何实现类似的机制?

答案就是 —— TemplatedControl

为了直观演示,我写了一个小 Demo:自定义一个 三角形控件(TriangleControl),让它自动闪烁。我们分别用 WPFAvalonia 来实现,最后对比一下两者的异同。


一、为什么要用 TemplatedControl?

在 UI 框架里,我们有两种常见的自定义控件方式:

  1. UserControl

    • 逻辑 + UI 写在一起
    • 简单场景够用,但换皮肤、换模板比较困难
  2. TemplatedControl(WPF 中就是 Control)

    • 逻辑(C#)和外观(XAML 模板)解耦
    • 控件类只负责属性和逻辑,不关心 UI 长什么样
    • 外观完全交给 ControlTemplate 控制

例如 WPF 的 Button,内部逻辑并不知道它有边框还是圆角,这些都由 ControlTemplate 决定。
Avalonia 完全继承了这一思想,只不过对应的基类换成了 TemplatedControl


二、WPF 版 TriangleControl

在 WPF 中,我们继承 Control,通过 OnApplyTemplate() 获取模板里的元素,然后对它做动画。

控件类

public class TriangleControl : Control
{private Path? _path;static TriangleControl(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TriangleControl),new FrameworkPropertyMetadata(typeof(TriangleControl)));}public static readonly DependencyProperty FillProperty =DependencyProperty.Register(nameof(Fill), typeof(Brush),typeof(TriangleControl), new PropertyMetadata(Brushes.Black));public Brush Fill{get => (Brush)GetValue(FillProperty);set => SetValue(FillProperty, value);}public override void OnApplyTemplate(){base.OnApplyTemplate();if (_path != null){// 避免重复应用模板导致多次动画_path.ClearValue(UIElement.OpacityProperty);}_path = GetTemplateChild("PART_Path") as Path;if (_path != null){var blinkAnimation = new DoubleAnimation{From = 1.0,To = 0.2,Duration = TimeSpan.FromSeconds(0.5),AutoReverse = true,RepeatBehavior = RepeatBehavior.Forever};_path.BeginAnimation(UIElement.OpacityProperty, blinkAnimation);}}
}

样式模板

<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:controls="clr-namespace:WpfDemo.controls"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Style TargetType="{x:Type controls:TriangleControl}"><Setter Property="Width" Value="50" /><Setter Property="Height" Value="50" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type controls:TriangleControl}"><Viewbox Stretch="Fill"><PathData="M 0,1 L 0.5,0 L 1,1 Z"Fill="{TemplateBinding Fill}"Stretch="Uniform"x:Name="PART_Path" /></Viewbox></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>

效果:一个三角形控件,透明度循环闪烁。


三、Avalonia 版 TriangleControl

在 Avalonia 中,流程几乎一模一样:

  • 继承 TemplatedControl
  • 属性用 StyledProperty<T>
  • 模板元素通过 e.NameScope.Find<T>() 获取
  • 动画系统换成 Animation + KeyFrame

控件类

public class TriangleControl : TemplatedControl
{private Path? _path;public static readonly StyledProperty<IBrush> FillProperty =AvaloniaProperty.Register<TriangleControl, IBrush>(nameof(Fill), Brushes.Black);public IBrush Fill{get => GetValue(FillProperty);set => SetValue(FillProperty, value);}protected override void OnApplyTemplate(TemplateAppliedEventArgs e){base.OnApplyTemplate(e);_path = e.NameScope.Find<Path>("PART_Path");if (_path != null){var animation = new Animation{Duration = TimeSpan.FromSeconds(1),IterationCount = IterationCount.Infinite,Easing = new SineEaseInOut(),Children ={new KeyFrame{Cue = new Cue(0),Setters = { new Setter(Visual.OpacityProperty, 1.0) }},new KeyFrame{Cue = new Cue(0.5),Setters = { new Setter(Visual.OpacityProperty, 0.2) }},new KeyFrame{Cue = new Cue(1),Setters = { new Setter(Visual.OpacityProperty, 1.0) }}}};animation.RunAsync(_path, CancellationToken.None);}}
}

样式模板

<Stylesxmlns="https://github.com/avaloniaui"xmlns:controls="clr-namespace:AvaloniaDemo.Controls"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Design.PreviewWith><controls:TriangleControl /></Design.PreviewWith><Style Selector="controls|TriangleControl"><Setter Property="Width" Value="50" /><Setter Property="Height" Value="50" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="controls:TriangleControl"><Viewbox Stretch="Uniform"><PathData="M 0,1 L 0.5,0 L 1,1 Z"Fill="{TemplateBinding Fill}"Stretch="Uniform"x:Name="PART_Path" /></Viewbox></ControlTemplate></Setter.Value></Setter></Style>
</Styles>

效果:与 WPF 一致,三角形控件透明度闪烁。


四、WPF vs Avalonia 对比分析

下面是一个详细对比表格:

功能点WPFAvalonia
基类ControlTemplatedControl
依赖属性DependencyPropertyStyledProperty<T>
应用模板方法OnApplyTemplate()OnApplyTemplate(TemplateAppliedEventArgs)
查找模板元素GetTemplateChild("PART_X")e.NameScope.Find<T>("PART_X")
模板绑定{TemplateBinding ...}{TemplateBinding ...}(完全一致)
动画系统Storyboard / DoubleAnimationAnimation / KeyFrame

可以看到:

  • 整体思想几乎完全一致,因此从 WPF 迁移过来没有学习门槛
  • Avalonia 在语法上更简洁,比如属性直接用 StyledProperty,不需要 DependencyProperty.Register 那么啰嗦
  • 动画系统的 API 不同,但用法也很直观

五、心得体会

通过这个 Demo,我们发现:

  • WPF 和 Avalonia 在自定义控件的核心思路上保持了高度一致性

  • 只要你熟悉 WPF 的 ControlTemplate 模型,迁移到 Avalonia 基本无缝

  • 不同点主要体现在:

    1. API 命名(DependencyPropertyStyledProperty
    2. 模板元素查找方式
    3. 动画系统

因此,掌握 TemplatedControl = 掌握 Avalonia 自定义控件的核心

✍️ 结语
如果你正打算从 WPF 迁移到 Avalonia,不妨从这种简单的自定义控件开始练手。理解了 TemplatedControl,你就掌握了 Avalonia 的控件扩展基础。

我的GitHub仓库Avalonia学习项目包含完整的Avalonia实践案例与代码对比。
我的gitcode仓库是Avalonia学习项目

http://www.xdnf.cn/news/1406701.html

相关文章:

  • UniApp 基础开发第一步:HBuilderX 安装与环境配置
  • 【AI智能体技术】如何学习多智能体系统知识并实现SOTA算法?
  • SDL3.0 学习随笔:其一
  • 自底向上了解CPU的运算
  • 嵌入式常见架构
  • 【MYSQL】从混乱到清晰:联合查询帮你打通数据孤岛
  • 算法:插入排序
  • 公益免费二级域名
  • 解锁Tensor Core性能:深入探索CUDA Warp矩阵操作
  • Junior Engineer浅谈CAS
  • 【百度】C++开发(25届提前批 一面)面经
  • 时序数据库
  • GitHub 热榜项目 - 日榜(2025-08-31)
  • 使用cursor claude sonnet4的一些感受
  • PY32F002不小心设置了SWD复用的恢复
  • Chrome++插件与GreenChrome:增强Chrome浏览器功能
  • Spring Boot 3.0 应用 HTTP 到 HTTPS 技术改造方案
  • 《潮汐调和分析原理和应用》之四S_Tide使用2
  • Java中不太常见的语法-总结
  • 架构进阶——解读 69页 方法轮IT规划培训 架构-重点-细节【附全文阅读】
  • Shell编程核心入门:参数传递、运算符与流程控制全解析
  • 2025年9月计算机二级C++语言程序设计——选择题打卡Day11
  • 学习日志41 python
  • Linux/UNIX系统编程手册笔记:文件I/O、进程和内存分配
  • vue2下拉菜单
  • 【小宁学习日记5 PCB】电路定理
  • 9. 函数和匿名函数(一)
  • 快消品牌如何用 DAM 管理万张素材?
  • 【光照】[光照模型]是什么?以UnityURP为例
  • C++的反向迭代器