如何创建用户自定义控件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自动生成两个东西:
- MyCustomControl.cs(逻辑代码)
- 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 = 从头造一个全新控件,高度可定制
浙公网安备 33010602011771号