如何创建用户自定义控件Custom Control

一、先搞懂:UserControl vs CustomControl

1. UserControl(组合控件)

  • 把一堆现成控件(Button、TextBox)包在一起
  • XAML + 后台代码,强耦合
  • 适合:快速做一个可复用模块(登录框、搜索条、卡片)
  • 不能改模板

2. CustomControl(自定义控件)

  • 继承自 Control 或更基础类
  • 没有自己的 XAML,只有 Theme/Generic.xaml 里的默认样式
  • UI 与逻辑完全分离
  • 支持控件模板 重写、替换、复用
  • 适合:做框架级控件、库控件、通用 UI 库(如 Button、TextBox 这种)

二、CustomControl 完整创建步骤(.NET 6 /.NET Framework 通用)

步骤 1:添加 → 自定义控件 (WPF)

右键项目 → 添加 → 新建项 → 自定义控件 (WPF)
 
命名:MyCustomControl.cs
 
自动生成两个东西:
  1. MyCustomControl.cs(逻辑代码)
  2. Themes/Generic.xaml(默认控件模板)

三、完整示例:创建一个带图标的按钮控件

1. 控件类 MyCustomControl.cs

 1 using System.Windows;
 2 using System.Windows.Controls;
 3 using System.Windows.Media;
 4 
 5 namespace WpfCustomControlDemo
 6 {
 7     public class MyCustomControl : Control
 8     {
 9         // 静态构造:必须调用 DefaultStyleKey
10         static MyCustomControl()
11         {
12             DefaultStyleKeyProperty.OverrideMetadata(
13                 typeof(MyCustomControl),
14                 new FrameworkPropertyMetadata(typeof(MyCustomControl))
15             );
16         }
17 
18         // ======================= 依赖属性 ===========================
19 
20         // 图标
21         public ImageSource Icon
22         {
23             get => (ImageSource)GetValue(IconProperty);
24             set => SetValue(IconProperty, value);
25         }
26 
27         public static readonly DependencyProperty IconProperty =
28             DependencyProperty.Register(
29                 nameof(Icon),
30                 typeof(ImageSource),
31                 typeof(MyCustomControl),
32                 new PropertyMetadata(null));
33 
34         // 文本
35         public string Text
36         {
37             get => (string)GetValue(TextProperty);
38             set => SetValue(TextProperty, value);
39         }
40 
41         public static readonly DependencyProperty TextProperty =
42             DependencyProperty.Register(
43                 nameof(Text),
44                 typeof(string),
45                 typeof(MyCustomControl),
46                 new PropertyMetadata(""));
47 
48         // 点击命令(可选,MVVM 用)
49         public ICommand ClickCommand
50         {
51             get => (ICommand)GetValue(ClickCommandProperty);
52             set => SetValue(ClickCommandProperty, value);
53         }
54 
55         public static readonly DependencyProperty ClickCommandProperty =
56             DependencyProperty.Register(
57                 nameof(ClickCommand),
58                 typeof(ICommand),
59                 typeof(MyCustomControl),
60                 new PropertyMetadata(null));
61     }
62 }

2. 控件默认模板 Themes/Generic.xaml

 1 <ResourceDictionary
 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4     xmlns:local="clr-namespace:WpfCustomControlDemo">
 5 
 6     <Style TargetType="local:MyCustomControl">
 7         <Setter Property="Template">
 8             <Setter.Value>
 9                 <ControlTemplate TargetType="local:MyCustomControl">
10                     <!-- 控件外观 -->
11                     <Border Background="LightBlue"
12                             CornerRadius="8"
13                             Margin="10">
14                         <StackPanel Orientation="Horizontal"
15                                     Margin="10">
16                             <Image Source="{TemplateBinding Icon}"
17                                    Width="24" Height="24"/>
18 
19                             <TextBlock Text="{TemplateBinding Text}"
20                                        FontSize="16"
21                                        Margin="10 0"/>
22                         </StackPanel>
23                     </Border>
24                 </ControlTemplate>
25             </Setter.Value>
26         </Setter>
27     </Style>
28 
29 </ResourceDictionary>

四、在窗口中使用自定义控件

MainWindow.xaml

 1 <Window x:Class="WpfCustomControlDemo.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:local="clr-namespace:WpfCustomControlDemo"
 5         Title="MainWindow" Height="450" Width="800">
 6 
 7     <Grid>
 8         <StackPanel>
 9             <!-- 使用自定义控件 -->
10             <local:MyCustomControl
11                 Text="我的自定义控件"
12                 Icon="pack://application:,,,/icon.png"/>
13 
14             <!-- 可以放N个 -->
15             <local:MyCustomControl
16                 Text="第二个控件"
17                 Icon="pack://application:,,,/icon2.png"/>
18         </StackPanel>
19     </Grid>
20 </Window>

五、CustomControl 核心机制(必须懂)

1. 关键:DefaultStyleKey

1 DefaultStyleKeyProperty.OverrideMetadata(
2     typeof(MyCustomControl),
3     new FrameworkPropertyMetadata(typeof(MyCustomControl))
4 );

作用:告诉 WPF

“去 Themes/Generic.xaml 里找我的样式”
 

2. TemplateBinding

模板绑定,用于控件模板内部绑定到控件的属性:
1 {TemplateBinding Icon}
2 {TemplateBinding Text}

3. Generic.xaml

WPF 规定:所有自定义控件的默认样式必须放在这里
  • 生成属性:生成操作 = ResourceDictionary
  • 不能改名,不能改路径

六、高级:给自定义控件加点击事件

 
MyCustomControl.cs 里重写 OnApplyTemplate
 1 public override void OnApplyTemplate()
 2 {
 3     base.OnApplyTemplate();
 4 
 5     // 找到模板中的 Border 并注册点击
 6     if (GetTemplateChild("PART_Border") is Border border)
 7     {
 8         border.MouseLeftButtonDown += (s, e) =>
 9         {
10             ClickCommand?.Execute(null);
11         };
12     }
13 }

模板里命名:

1 <Border x:Name="PART_Border" ...>

这就是 WPF 控件标准的 PART_ 命名约定。

七、什么时候用 CustomControl

  • 你要做一个通用控件库
  • 控件需要支持换肤、改模板
  • 像系统控件 Button/TextBox 那样高度可定制
  • 多人协作、UI 与逻辑分离

八、一句话总结

  • UserControl = 打包现有控件,快速复用
  • CustomControl = 从头造一个全新控件,高度可定制
posted on 2026-03-27 14:42  工业搬砖猿Lee  阅读(10)  评论(0)    收藏  举报