找回密码
 立即注册
首页 业界区 业界 Avalonia:辨析 UserControl 与 TemplatedControl

Avalonia:辨析 UserControl 与 TemplatedControl

广性 2 小时前
Avalonia:UserControl 与 TemplatedControl

Avalonia 中有两种常见控件创建方式——UserControl(用户控件)和 TemplatedControl(模板控件),两者分别有不同的使用场景和特点。
很多教程不会辨析两者区别。如果初学者(比如之前的我)没有分清楚两者,那会不可避免地写出极其恶心别扭且难以理解的代码。
简单概念辨析

根据Avalonia官方文档

  • UserControl
    UserControl 控件是一种 ContentControl,它代表了一组在预定义布局中可重用的控件。
    实际上,UserControl 在 ContentControl 的基础上提供的功能非常有限。不同之处在于,通常不会直接创建
    UserControl 类的实例;相反,通常会为应用程序要显示的每个“视图”创建一个 UserControl 类的新子类。
    UserControl相比于ContentControl几乎没有添加任何方法或属性。UserControl只是一个可以预定义布局的容器。同时其继承于ContentControl,而ContentControl继承于TemplatedControl。因此UserControl 与 TemplatedControl 实际是继承关系。
    Avalonia模板提供的MainView与MainWindow本质上都是UserControl(Window)。
  • TemplatedControl
    模板控件(也称为“Lookless控件”)为在Avalonia中创建自定义控件提供了更高级和可自定义的方法。模板控件将控件的行为和逻辑与其可视外观分离,允许应用程序开发人员通过控件模板进行样式化和模板化。
    TemplatedControl 通过后期添加的模板以定义外观布局。类似于TextBox,包括所有在不同主题如Fluent中表现不同的均为模板控件。
  • Control
    为所有控件的基类。Panel,Border等只有后端代码的控件继承于Control。
核心区别


  • 目的

    • UserControl:面向“组合视图”,把若干现有控件以 XAML + 逻辑组合成一个复用组件,通常用于应用层的具体 UI 单元,而非这里用用那里用用的基础控件。
    • TemplatedControl:面向“可模板化/样式化控件”,提供公开属性和可替换的视觉模板(ControlTemplate),适合库/控件框架中可主题化的控件。

  • 可定制性

    • UserControl:不建议外部样式改变内部结构(适合固定结构)。
    • TemplatedControl:视觉由 Template、Theme、Style 决定,便于主题、样式与模板替换。

  • 生命周期与性能

    • UserControl 在构建时直接加载 XAML,适合快速组合;大量小的 UserControl 可能导致更深的视觉树。
    • TemplatedControl 控件模板延迟实例化和更容易优化、主题化。

何时选哪个


  • 选择 UserControl 当:

    • 需要快速组合几个控件形成页面级或区域级 UI,类似于MainView,不关心内部控件的具体实现。
    • 控件结构固定、不需要外部主题化。

  • 选择 TemplatedControl 当:

    • 要构建一个可重用、可主题化且对外提供可绑定属性的控件(例如库控件、按钮、复合但可替换样式的控件)。
    • 希望为不同主题提供不同视觉实现(把样式放在 Theme 中)。

示例

UserControl


  • MainWindow.axaml
  1. <Window xmlns="https://github.com/avaloniaui"
  2. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  3.    
  4. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  5.    
  6. </ResourceDictionary>xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  7. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  8.    
  9. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  10.    
  11. </ResourceDictionary>xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  12. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  13.    
  14. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  15.    
  16. </ResourceDictionary>xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  17. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  18.    
  19. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  20.    
  21. </ResourceDictionary>xmlns:controls="clr-namespace:ControlDemo.Controls"
  22. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  23.    
  24. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  25.    
  26. </ResourceDictionary>mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
  27. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  28.    
  29. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  30.    
  31. </ResourceDictionary>x:
  32. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  33.    
  34. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  35.    
  36. </ResourceDictionary>Title="ControlDemo">
  37. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  38.    
  39. </ResourceDictionary><controls:UserControl1 PropertyOne="1" PropertyTwo="2">
  40. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  41.    
  42. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  43.    
  44. </ResourceDictionary><controls:UserControl1.Template>
  45. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  46.    
  47. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  48.    
  49. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  50.    
  51. </ResourceDictionary><ControlTemplate>
  52. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  53.    
  54. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  55.    
  56. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  57.    
  58. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  59.    
  60. </ResourceDictionary><TextBlock Text="1"></TextBlock>
  61. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  62.    
  63. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  64.    
  65. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  66.    
  67. </ResourceDictionary></ControlTemplate>
  68. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  69.    
  70. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  71.    
  72. </ResourceDictionary></controls:UserControl1.Template>
  73. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  74.    
  75. </ResourceDictionary></controls:UserControl1>
  76. </Window>
复制代码

  • UserControl1.axaml
  1. <Panel xmlns="https://github.com/avaloniaui"
  2. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  3.    
  4. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  5.    
  6. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  7.    
  8. </ResourceDictionary> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  9. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  10.    
  11. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  12.    
  13. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  14.    
  15. </ResourceDictionary> xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  16. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  17.    
  18. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  19.    
  20. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  21.    
  22. </ResourceDictionary> xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  23. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  24.    
  25. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  26.    
  27. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  28.    
  29. </ResourceDictionary> xmlns:controls="clr-namespace:ControlDemo.Controls"
  30. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  31.    
  32. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  33.    
  34. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  35.    
  36. </ResourceDictionary> mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
  37. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  38.    
  39. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  40.    
  41. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  42.    
  43. </ResourceDictionary> x: x:DataType="controls:UserControl1">
  44. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  45.    
  46. </ResourceDictionary>
  47. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  48.    
  49. </ResourceDictionary><StackPanel>
  50. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  51.    
  52. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  53.    
  54. </ResourceDictionary><TextBlock Text="{ Binding PropertyOne,
  55. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  56.    
  57. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  58.    
  59. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  60.    
  61. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  62.    
  63. </ResourceDictionary>   RelativeSource={ RelativeSource AncestorType={x:Type controls:UserControl1}}}"
  64. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  65.    
  66. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  67.    
  68. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  69.    
  70. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  71.    
  72. </ResourceDictionary>   PointerPressed="PointerPress"/>
  73. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  74.    
  75. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  76.    
  77. </ResourceDictionary><TextBlock Name="Two" Text="{Binding PropertyTwo}"/>
  78. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  79.    
  80. </ResourceDictionary></StackPanel>
  81. </Panel>
复制代码

  • UserControl1.axaml.cs
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Markup.Xaml;
  4. using ReactiveUI;
  5. using System;
  6. using System.ComponentModel;
  7. namespace ControlDemo.Controls;
  8. public partial class UserControl1 : Border
  9. {
  10. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  11.    
  12. </ResourceDictionary>public static readonly DirectProperty<UserControl1, int> PropertyOneProperty = AvaloniaProperty.RegisterDirect<UserControl1, int>(
  13. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  14.    
  15. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  16.    
  17. </ResourceDictionary>nameof(PropertyOne), o => o.PropertyOne, (o, v) => o.PropertyOne = v);
  18. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  19.    
  20. </ResourceDictionary>public int PropertyOne
  21. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  22.    
  23. </ResourceDictionary>{
  24. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  25.    
  26. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  27.    
  28. </ResourceDictionary>get => field;
  29. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  30.    
  31. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  32.    
  33. </ResourceDictionary>set => SetAndRaise(PropertyOneProperty, ref field, value);
  34. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  35.    
  36. </ResourceDictionary>}
  37. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  38.    
  39. </ResourceDictionary>public int PropertyTwo
  40. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  41.    
  42. </ResourceDictionary>{
  43. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  44.    
  45. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  46.    
  47. </ResourceDictionary>get;
  48. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  49.    
  50. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  51.    
  52. </ResourceDictionary>set;
  53. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  54.    
  55. </ResourceDictionary>} = 1;
  56. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  57.    
  58. </ResourceDictionary>
  59. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  60.    
  61. </ResourceDictionary>
  62. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  63.    
  64. </ResourceDictionary>public UserControl1()
  65. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  66.    
  67. </ResourceDictionary>{
  68. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  69.    
  70. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  71.    
  72. </ResourceDictionary>InitializeComponent();
  73. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  74.    
  75. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  76.    
  77. </ResourceDictionary>PropertyTwo = 0;
  78. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  79.    
  80. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  81.    
  82. </ResourceDictionary>Two.Background=Brushes.Red;
  83. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  84.    
  85. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  86.    
  87. </ResourceDictionary>DataContext = this;
  88. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  89.    
  90. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  91.    
  92. </ResourceDictionary>
  93. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  94.    
  95. </ResourceDictionary>}
  96. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  97.    
  98. </ResourceDictionary>private void InitializeComponent()  {
  99. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  100.    
  101. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  102.    
  103. </ResourceDictionary>AvaloniaXamlLoader.Load(this);
  104. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  105.    
  106. </ResourceDictionary>}
  107. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  108.    
  109. </ResourceDictionary>
  110. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  111.    
  112. </ResourceDictionary>private void PointerPress(object? sender, PointerPressedEventArgs e)
  113. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  114.    
  115. </ResourceDictionary>{
  116. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  117.    
  118. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
  119.    
  120. </ResourceDictionary>Console.WriteLine("Pointer press");
  121. <ResourceDictionary xmlns="https://github.com/avaloniaui">
  122.    
  123. </ResourceDictionary>}
  124. }
复制代码
注意 以上代码仅供展示用法 是不可运行的 其中糟糕的的写法千万不要学习

  • 控件声明

    • x:Class 指向后端代码对于的类名,xataType 可提供编译时的强类型绑定提示。
    • xataType 代表强类型的 ViewModel,以便使用编译绑定。 建议全部使用 CompiledBinding,使代码便于理解,绑定指向明确
    • 用户控件不需要继承于UserControl,例如示例代码中控件继承于Border,其必要条件是调用AvaloniaXamlLoader.Load(this);,即将axaml中的逻辑树载入视觉树。
    • AvaloniaXamlLoader.Load(this);不关心axaml文件中的根元素是什么,例如示例代码中的根元素为Panel,而控件实际继承于Border

  • 绑定

    • 相比于模板控件,用户控件不可以直接通过TemplateBinding对控件的属性直接绑定。
    • 推荐使用ViewModel作为DataContext,而非图方便,像示例代码中,将DataContext设为自身,因为这会阻止宿主传入 ViewModel,会破坏外部绑定。。
    • 如果需要绑定到用户控件自身,则可以像示例中使用RelativeSource={ RelativeSource AncestorType={x:Type controls:UserControl1}}}使绑定对象转移到目标用户控件,也可以给用户控件添加Name属性,使用绑定到Name的语法。
    • 相比于模板控件,后端代码对用户控件的绑定更为方便。在axaml中设置Name属性,即可直接在后端代码中直接引用控件对象。
    • 可以直接将事件绑定到后端代码的函数(不可以绑定到委托字段)。

  • 模板

    • 用户控件继承于模板控件,因此用户控件同样也可以模板化,并且会覆盖axaml中定义的内容(但为什么要这么做呢?)

TemplatedControl

TemplatedControl(无外观控件)将行为与视觉彻底分离:控件通过公开的 StyledProperty/DirectProperty 暴露行为和状态,外观由 Template 属性中  ControlTemplate 决定。这使得控件可以被主题化、样式化和重用,且模板延迟实例化有利于性能优化。

  • 关键点

    • 暴露属性:使用 AvaloniaProperty.Register / RegisterDirect 注册 StyledProperty 或 DirectProperty,供模板、样式和绑定使用。
    • 模板绑定:在 ControlTemplate 中使用 TemplateBinding 来绑定控件属性到视觉树元素。
    • 获取模板部件:在控件后端重写 OnApplyTemplate(或 OnTemplateApplied),通过 NameScope 找到带有特定 Name(通常以 PART_ 前缀命名)的元素并为其绑定事件或行为。
    • 适用场景:库级控件、可主题化控件、需要高度可定制外观的控件。

  • TemplatedControl1.axaml.cs
    1. using Avalonia;
    2. using Avalonia.Controls;
    3. using Avalonia.Controls.Primitives;
    4. public class TemplatedControl1 : TemplatedControl
    5. {
    6. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    7.    
    8. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    9.    
    10. </ResourceDictionary>public static readonly StyledProperty<bool> IsCheckedProperty =
    11. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    12.    
    13. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    14.    
    15. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    16.    
    17. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    18.    
    19. </ResourceDictionary>AvaloniaProperty.Register<TemplatedControl1, bool>(nameof(IsChecked));
    20. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    21.    
    22. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    23.    
    24. </ResourceDictionary>public bool IsChecked
    25. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    26.    
    27. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    28.    
    29. </ResourceDictionary>{
    30. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    31.    
    32. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    33.    
    34. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    35.    
    36. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    37.    
    38. </ResourceDictionary>get => GetValue(IsCheckedProperty);
    39. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    40.    
    41. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    42.    
    43. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    44.    
    45. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    46.    
    47. </ResourceDictionary>set => SetValue(IsCheckedProperty, value);
    48. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    49.    
    50. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    51.    
    52. </ResourceDictionary>}
    53. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    54.    
    55. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    56.    
    57. </ResourceDictionary>public override void OnApplyTemplate(TemplateAppliedEventArgs e)
    58. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    59.    
    60. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    61.    
    62. </ResourceDictionary>{
    63. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    64.    
    65. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    66.    
    67. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    68.    
    69. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    70.    
    71. </ResourceDictionary>base.OnApplyTemplate(e);
    72. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    73.    
    74. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    75.    
    76. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    77.    
    78. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    79.    
    80. </ResourceDictionary>// 获取模板中的命名部件(示例名称:PART_Thumb)
    81. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    82.    
    83. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    84.    
    85. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    86.    
    87. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    88.    
    89. </ResourceDictionary>var thumb = e.NameScope.Find<Border>("PART_Thumb");
    90. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    91.    
    92. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    93.    
    94. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    95.    
    96. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    97.    
    98. </ResourceDictionary>if (thumb != null)
    99. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    100.    
    101. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    102.    
    103. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    104.    
    105. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    106.    
    107. </ResourceDictionary>{
    108. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    109.    
    110. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    111.    
    112. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    113.    
    114. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    115.    
    116. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    117.    
    118. </ResourceDictionary>thumb.Background=Brushes.Red;
    119. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    120.    
    121. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    122.    
    123. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    124.    
    125. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    126.    
    127. </ResourceDictionary>}
    128. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    129.    
    130. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    131.    
    132. </ResourceDictionary>}
    133. }
    复制代码
  • TemplatedControl1.axaml
    1. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    2.    
    3. </ResourceDictionary>
    复制代码
  • MainWindow.axaml
    1. <Window xmlns="https://github.com/avaloniaui"
    2. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    3.    
    4. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    5.    
    6. </ResourceDictionary>xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    7. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    8.    
    9. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    10.    
    11. </ResourceDictionary>xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    12. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    13.    
    14. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    15.    
    16. </ResourceDictionary>xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    17. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    18.    
    19. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    20.    
    21. </ResourceDictionary>xmlns:controls="clr-namespace:ControlDemo.Controls"
    22. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    23.    
    24. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    25.    
    26. </ResourceDictionary>mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
    27. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    28.    
    29. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    30.    
    31. </ResourceDictionary>x:
    32. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    33.    
    34. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    35.    
    36. </ResourceDictionary>Title="ControlDemo">
    37. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    38.    
    39. </ResourceDictionary><controls:UserControl1 PropertyOne="1" PropertyTwo="2">
    40. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    41.    
    42. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    43.    
    44. </ResourceDictionary><controls:UserControl1.Template>
    45. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    46.    
    47. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    48.    
    49. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    50.    
    51. </ResourceDictionary><ControlTemplate>
    52. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    53.    
    54. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    55.    
    56. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    57.    
    58. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    59.    
    60. </ResourceDictionary><TextBlock Text="1"></TextBlock>
    61. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    62.    
    63. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    64.    
    65. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    66.    
    67. </ResourceDictionary></ControlTemplate>
    68. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    69.    
    70. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    71.    
    72. </ResourceDictionary></controls:UserControl1.Template>
    73. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    74.    
    75. </ResourceDictionary></controls:UserControl1>
    76. </Window><Window xmlns="https://github.com/avaloniaui"
    77. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    78.    
    79. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    80.    
    81. </ResourceDictionary>xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    82. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    83.    
    84. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    85.    
    86. </ResourceDictionary>xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    87. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    88.    
    89. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    90.    
    91. </ResourceDictionary>xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    92. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    93.    
    94. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    95.    
    96. </ResourceDictionary>xmlns:controls="clr-namespace:ControlDemo.Controls"
    97. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    98.    
    99. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    100.    
    101. </ResourceDictionary>mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
    102. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    103.    
    104. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    105.    
    106. </ResourceDictionary>x:
    107. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    108.    
    109. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    110.    
    111. </ResourceDictionary>Title="ControlDemo">
    112. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    113.    
    114. </ResourceDictionary><controls:UserControl1 PropertyOne="1" PropertyTwo="2">
    115. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    116.    
    117. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    118.    
    119. </ResourceDictionary><controls:UserControl1.Template>
    120. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    121.    
    122. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    123.    
    124. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    125.    
    126. </ResourceDictionary><ControlTemplate>
    127. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    128.    
    129. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    130.    
    131. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    132.    
    133. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    134.    
    135. </ResourceDictionary><TextBlock Text="1"></TextBlock>
    136. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    137.    
    138. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    139.    
    140. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    141.    
    142. </ResourceDictionary></ControlTemplate>
    143. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    144.    
    145. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    146.    
    147. </ResourceDictionary></controls:UserControl1.Template>
    148. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    149.    
    150. </ResourceDictionary></controls:UserControl1>
    151. </Window><Panel xmlns="https://github.com/avaloniaui"
    152. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    153.    
    154. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    155.    
    156. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    157.    
    158. </ResourceDictionary> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    159. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    160.    
    161. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    162.    
    163. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    164.    
    165. </ResourceDictionary> xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    166. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    167.    
    168. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    169.    
    170. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    171.    
    172. </ResourceDictionary> xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    173. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    174.    
    175. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    176.    
    177. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    178.    
    179. </ResourceDictionary> xmlns:controls="clr-namespace:ControlDemo.Controls"
    180. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    181.    
    182. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    183.    
    184. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    185.    
    186. </ResourceDictionary> mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
    187. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    188.    
    189. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    190.    
    191. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    192.    
    193. </ResourceDictionary> x: x:DataType="controls:UserControl1">
    194. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    195.    
    196. </ResourceDictionary>
    197. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    198.    
    199. </ResourceDictionary><StackPanel>
    200. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    201.    
    202. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    203.    
    204. </ResourceDictionary><TextBlock Text="{ Binding PropertyOne,
    205. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    206.    
    207. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    208.    
    209. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    210.    
    211. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    212.    
    213. </ResourceDictionary>   RelativeSource={ RelativeSource AncestorType={x:Type controls:UserControl1}}}"
    214. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    215.    
    216. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    217.    
    218. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    219.    
    220. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    221.    
    222. </ResourceDictionary>   PointerPressed="PointerPress"/>
    223. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    224.    
    225. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    226.    
    227. </ResourceDictionary><TextBlock Name="Two" Text="{Binding PropertyTwo}"/>
    228. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    229.    
    230. </ResourceDictionary></StackPanel>
    231. </Panel><Panel xmlns="https://github.com/avaloniaui"
    232. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    233.    
    234. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    235.    
    236. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    237.    
    238. </ResourceDictionary> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    239. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    240.    
    241. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    242.    
    243. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    244.    
    245. </ResourceDictionary> xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    246. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    247.    
    248. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    249.    
    250. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    251.    
    252. </ResourceDictionary> xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    253. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    254.    
    255. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    256.    
    257. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    258.    
    259. </ResourceDictionary> xmlns:controls="clr-namespace:ControlDemo.Controls"
    260. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    261.    
    262. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    263.    
    264. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    265.    
    266. </ResourceDictionary> mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
    267. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    268.    
    269. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    270.    
    271. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    272.    
    273. </ResourceDictionary> x: x:DataType="controls:UserControl1">
    274. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    275.    
    276. </ResourceDictionary>
    277. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    278.    
    279. </ResourceDictionary><StackPanel>
    280. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    281.    
    282. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    283.    
    284. </ResourceDictionary><TextBlock Text="{ Binding PropertyOne,
    285. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    286.    
    287. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    288.    
    289. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    290.    
    291. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    292.    
    293. </ResourceDictionary>   RelativeSource={ RelativeSource AncestorType={x:Type controls:UserControl1}}}"
    294. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    295.    
    296. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    297.    
    298. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    299.    
    300. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    301.    
    302. </ResourceDictionary>   PointerPressed="PointerPress"/>
    303. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    304.    
    305. </ResourceDictionary><ResourceDictionary xmlns="https://github.com/avaloniaui">
    306.    
    307. </ResourceDictionary><TextBlock Name="Two" Text="{Binding PropertyTwo}"/>
    308. <ResourceDictionary xmlns="https://github.com/avaloniaui">
    309.    
    310. </ResourceDictionary></StackPanel>
    311. </Panel>
    复制代码
  • 要点

    • 如果需要对外公开属性、支持 TemplateBinding、允许主题替换,使用 TemplatedControl。
    • 在后端使用 StyledProperty 暴露状态;在模板中使用 TemplateBinding 实现外观与属性联动。
    • 在 OnApplyTemplate 中拾取 PART_* 元素并连接行为或动画,保持视觉与逻辑分离。

  • TemplateBinding

    • TemplateBinding 只接受单个属性而不是属性路径,且由于性能原因,TemplateBinding 只支持 OneWay 模式,所以如果你想要使用属性路径进行绑定,你必须使用前文提到的RelativeSource。
    • TemplateBinding 没有类型转换功能,axaml中绑定的目标必须和属性为同一类型或继承关系
    • TemplateBinding 只能在 IStyledElement 上使用,例如是错误的

总结


  • UserControl 与 TemplatedControl 侧重点不同:UserControl 以组合固定视图为主,适合构建页面级或区域级 UI;TemplatedControl 以“无外观”+模板化为主,适合可主题化、可复用的库级控件。
  • 可定制性:UserControl 结构固定、不鼓励外部通过样式改变内部布局;TemplatedControl 通过 Template/Style/Theme 提供高度可替换的视觉表现。
  • 属性与绑定:TemplatedControl 通过 StyledProperty/DirectProperty 暴露可绑定/样式化的状态;UserControl 常用 DataContext / RelativeSource 进行绑定,后端代码引用子元素更直接。
  • 生命周期与性能:UserControl 在加载时直接实例化 XAML;TemplatedControl 的模板延迟实例化,更利于主题和性能优化。
  • 模板与逻辑分离:TemplatedControl 鼓励在 OnApplyTemplate(或 TemplateApplied)中获取 PART_* 元素并绑定行为,保持视觉与逻辑分离;UserControl 更适合把视图与行为写在同一处以提高开发效率。
  • 选择建议:快速组合、结构固定、面向应用层 UI 用 UserControl;需要对外公开属性、可主题化或作为控件库提供复用组件时用 TemplatedControl。
总体原则:按责任选型——页面级组合用 UserControl,控件级可替换外观用 TemplatedControl,兼顾可维护性与可定制性。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

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