004003004_WPF ButtonBase 基类官方类定义逐行深度解析
摘要:ButtonBase 是 WPF 所有按钮类控件的抽象基类,统一了「点击触发、命令执行、状态反馈」三大行为模型。本文基于 .NET 8 官方源码逐行解析其类定义、静态构造、核心依赖属性、路由事件与受保护方法,梳理鼠标/键盘点击的完整工作流程,并结合工业上位机场景给出实用建议:触摸屏按钮统一使用 ClickMode="Press",优先采用 Command 命令绑定,自定义模板时保留 TemplateBinding 与 IsPressed/IsEnabled 触发器,并对常见点击无响应、命令不执行、触摸屏延迟、模板样式丢失等问题提供原因分析与排查步骤。
基于 .NET 8 官方开源源码完整解析,包含所有公共/受保护成员、特性、设计意图和工业场景应用。ButtonBase是 WPF 所有按钮类控件的抽象基类,定义了所有按钮共有的点击逻辑、命令系统、键盘支持和状态管理,是 Button、ToggleButton、RepeatButton、CheckBox、RadioButton等控件的共同父类。
一、ButtonBase 在 WPF 类层次结构中的位置
plaintext:
System.Object
↳ System.Windows.Threading.DispatcherObject
↳ System.Windows.DependencyObject
↳ System.Windows.Media.Visual
↳ System.Windows.UIElement
↳ System.Windows.FrameworkElement
↳ System.Windows.Controls.Control
↳ System.Windows.Controls.ContentControl
↳ System.Windows.Controls.Primitives.ButtonBase ← 所有按钮的抽象基类
↳ System.Windows.Controls.Button
↳ System.Windows.Controls.Primitives.ToggleButton
↳ System.Windows.Controls.Primitives.RepeatButton
↳ System.Windows.Controls.Primitives.GridViewColumnHeader
核心设计意义:
- 统一所有按钮类控件的行为模型
- 封装通用的点击逻辑、命令系统和键盘支持
- 提供可扩展的基类,便于开发自定义按钮控件
- 实现 ICommandSource 接口,原生支持 MVVM 命令模式
二、完整官方类定义(.NET 8 源码级)
csharp:
using System.Windows.Automation.Peers;
using System.Windows.Input;
using System.Windows.Markup;
namespace System.Windows.Controls.Primitives
{
/// <summary>
/// 表示所有按钮类控件的基类
/// </summary>
/// <remarks>
/// ButtonBase 是一个抽象类,不能直接实例化。
/// 它定义了所有按钮共有的属性、事件和方法,包括点击逻辑、命令支持和键盘交互。
/// </remarks>
[DefaultEvent("Click")]
[Localizability(LocalizationCategory.Button)]
public abstract class ButtonBase : ContentControl, ICommandSource
{
// ==============================================
// 依赖属性定义(所有按钮共有)
// ==============================================
public static readonly DependencyProperty ClickModeProperty;
public static readonly DependencyProperty IsPressedProperty;
public static readonly DependencyProperty CommandProperty;
public static readonly DependencyProperty CommandParameterProperty;
public static readonly DependencyProperty CommandTargetProperty;
// ==============================================
// 路由事件定义(所有按钮共有)
// ==============================================
public static readonly RoutedEvent ClickEvent;
// ==============================================
// 静态构造函数
// ==============================================
static ButtonBase()
{
// 注册路由事件
ClickEvent = EventManager.RegisterRoutedEvent(
nameof(Click),
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(ButtonBase));
// 注册依赖属性
ClickModeProperty = DependencyProperty.Register(
nameof(ClickMode),
typeof(ClickMode),
typeof(ButtonBase),
new FrameworkPropertyMetadata(ClickMode.Release),
new ValidateValueCallback(IsValidClickMode));
IsPressedProperty = DependencyProperty.RegisterReadOnly(
nameof(IsPressed),
typeof(bool),
typeof(ButtonBase),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnIsPressedChanged)));
CommandProperty = DependencyProperty.Register(
nameof(Command),
typeof(ICommand),
typeof(ButtonBase),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnCommandChanged)));
CommandParameterProperty = DependencyProperty.Register(
nameof(CommandParameter),
typeof(object),
typeof(ButtonBase),
new FrameworkPropertyMetadata(null));
CommandTargetProperty = DependencyProperty.Register(
nameof(CommandTarget),
typeof(IInputElement),
typeof(ButtonBase),
new FrameworkPropertyMetadata(null));
// 重写默认样式键
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ButtonBase),
new FrameworkPropertyMetadata(typeof(ButtonBase)));
// 注册键盘快捷键:空格键和回车键触发点击
KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(
typeof(ButtonBase),
new FrameworkPropertyMetadata(true));
// 禁用文本选择
IsTextSelectionEnabledProperty.OverrideMetadata(
typeof(ButtonBase),
new FrameworkPropertyMetadata(false));
}
// ==============================================
// 受保护构造函数(抽象类不能直接实例化)
// ==============================================
protected ButtonBase();
// ==============================================
// 公共属性
// ==============================================
[Bindable(true)]
[Category("Behavior")]
public ClickMode ClickMode { get; set; }
[Bindable(true)]
[Browsable(false)]
[Category("Appearance")]
public bool IsPressed { get; protected set; }
[Bindable(true)]
[Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public ICommand Command { get; set; }
[Bindable(true)]
[Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public object CommandParameter { get; set; }
[Bindable(true)]
[Category("Action")]
public IInputElement CommandTarget { get; set; }
// ==============================================
// 核心路由事件
// ==============================================
public event RoutedEventHandler Click
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
// ==============================================
// 受保护方法(派生类可重写)
// ==============================================
protected override AutomationPeer OnCreateAutomationPeer();
protected virtual void OnClick();
protected virtual void OnIsPressedChanged(DependencyPropertyChangedEventArgs e);
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e);
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e);
protected override void OnMouseEnter(MouseEventArgs e);
protected override void OnMouseLeave(MouseEventArgs e);
protected override void OnKeyDown(KeyEventArgs e);
protected override void OnKeyUp(KeyEventArgs e);
protected override void OnLostMouseCapture(MouseEventArgs e);
}
}
三、类级特性逐行解析
1. [DefaultEvent("Click")]
csharp:
[DefaultEvent("Click")]
- 作用:指定控件的默认事件。
- 设计意图:在 Visual Studio 设计器中双击任何按钮类控件时,自动生成 Click 事件的处理方法。
- 工业场景意义:符合开发人员直觉,因为所有按钮最常用的操作都是响应点击事件。
2. [Localizability(LocalizationCategory.Button)]
csharp:
[Localizability(LocalizationCategory.Button)]
- 作用:本地化特性,告诉本地化工具该类属于按钮类别。
- 设计意图:本地化工具会自动将所有按钮类控件的 Content 属性作为可本地化内容处理。
- 注意:不会本地化行为相关的属性,只本地化显示内容。
3. abstract class ButtonBase
C#:
public abstract class ButtonBase : ContentControl, ICommandSource
- abstract:标记为抽象类,不能直接实例化,只能作为基类使用。
- 继承 ContentControl:所有按钮都支持任意内容(文本、图标、复杂布局)。
- 实现 ICommandSource:原生支持 MVVM 命令模式,这是 WPF 按钮最重要的特性之一。
四、静态构造函数解析(核心初始化逻辑)
静态构造函数是 ButtonBase 最关键的部分,负责所有路由事件和依赖属性的注册。
1. ClickEvent 路由事件注册(核心)
csharp:
ClickEvent = EventManager.RegisterRoutedEvent(
nameof(Click),
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(ButtonBase));
逐句解析:
- EventManager.RegisterRoutedEvent:WPF 路由事件的标准注册方法。
- nameof(Click):事件名称,必须与公共事件属性名称一致。
- RoutingStrategy.Bubble:路由策略为冒泡路由,事件从触发元素向上冒泡到根元素。
- typeof(RoutedEventHandler):事件处理程序的类型。
- typeof(ButtonBase):事件的所有者类型。
核心设计意义:
- 路由事件允许事件在可视化树中传播,而不仅仅是在触发元素上处理
- 冒泡路由策略符合按钮的交互习惯,父容器可以统一处理子按钮的点击事件
- 这是 WPF 事件系统与传统 WinForms 事件系统最本质的区别之一。
2. ClickModeProperty 注册
csharp:
ClickModeProperty = DependencyProperty.Register(
nameof(ClickMode),
typeof(ClickMode),
typeof(ButtonBase),
new FrameworkPropertyMetadata(ClickMode.Release),
new ValidateValueCallback(IsValidClickMode));
- 类型:ClickMode 枚举
- 默认值:ClickMode.Release(鼠标释放时触发点击)
- 验证回调:IsValidClickMode,确保值是有效的枚举值
- 核心作用:控制点击事件的触发时机
3. IsPressedProperty 注册
csharp:
IsPressedProperty = DependencyProperty.RegisterReadOnly(
nameof(IsPressed),
typeof(bool),
typeof(ButtonBase),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnIsPressedChanged)));
- 类型:bool
- 注册方式:RegisterReadOnly,只读依赖属性
- 默认值:false
- 属性变更回调:OnIsPressedChanged,当按下状态变化时调用
- 核心作用:指示按钮是否处于按下状态,主要用于控件模板的触发器
4. 命令相关属性注册
C#:
CommandProperty = DependencyProperty.Register(
nameof(Command),
typeof(ICommand),
typeof(ButtonBase),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnCommandChanged)));
CommandParameterProperty = DependencyProperty.Register(
nameof(CommandParameter),
typeof(object),
typeof(ButtonBase),
new FrameworkPropertyMetadata(null));
CommandTargetProperty = DependencyProperty.Register(
nameof(CommandTarget),
typeof(IInputElement),
typeof(ButtonBase),
new FrameworkPropertyMetadata(null));
- Command:绑定到 ViewModel 中的命令。
- CommandParameter:传递给命令的参数。
- CommandTarget:命令的目标元素。
- 设计意图:原生支持 MVVM 模式,实现 UI 与业务逻辑的完全分离。
5. 其他初始化
C#:
// 重写默认样式键
DefaultStyleKeyProperty.OverrideMetadata(
typeof(ButtonBase),
new FrameworkPropertyMetadata(typeof(ButtonBase)));
// 注册键盘快捷键:空格键和回车键触发点击
KeyboardNavigation.AcceptsReturnProperty.OverrideMetadata(
typeof(ButtonBase),
new FrameworkPropertyMetadata(true));
// 禁用文本选择
IsTextSelectionEnabledProperty.OverrideMetadata(
typeof(ButtonBase),
new FrameworkPropertyMetadata(false));
- 默认样式键:指定 ButtonBase 的默认样式类型。
- 键盘支持:默认支持空格键和回车键触发点击,符合 Windows 标准。
- 禁用文本选择:按钮上的文本不能被选中,符合按钮的交互习惯。
五、核心依赖属性逐行解析
1. ClickMode 属性(最容易被忽略的重要属性)
csharp:
[Bindable(true)]
[Category("Behavior")]
public ClickMode ClickMode { get; set; }
逐句解析:
-
[Category("Behavior")]:在属性窗口中归类到“行为”组。
-
类型:ClickMode 枚举,有三个可选值:
- ClickMode.Release(默认):鼠标左键释放时触发 Click 事件。
- ClickMode.Press:鼠标左键按下时触发 Click 事件。
- ClickMode.Hover:鼠标悬停在按钮上时触发 Click 事件。
工业场景关键应用:
- 触摸屏设备必须使用ClickMode.Press:触摸屏没有“鼠标释放”事件,使用默认的Release模式会导致点击延迟或不灵敏
- 普通鼠标操作使用Release:符合用户习惯,允许用户在按下后移动鼠标取消点击
- Hover模式极少使用:仅用于特殊的交互场景
示例:触摸屏工业按钮
XAML:
<!– 触摸屏设备专用按钮,按下立即触发 –>
<Button Content="启动设备"
ClickMode="Press"
Command="{Binding StartCommand}"/>
2. IsPressed 属性(只读)
csharp:
[Bindable(true)]
[Browsable(false)]
[Category("Appearance")]
public bool IsPressed { get; protected set; }
逐句解析:
-
[Browsable(false)]:在 Visual Studio 属性窗口中隐藏,因为这是一个只读属性。
-
[Category("Appearance")]:归类到“外观”组。
-
类型:bool(只读)
-
核心作用:
- 指示按钮当前是否处于按下状态。
- 由 WPF 内部自动维护,开发者不能直接修改。
- 主要用于控件模板的触发器,实现按下效果。
示例:在控件模板中使用 IsPressed
XAML:
<ControlTemplate TargetType="Button">
<Border x:Name="Border"
Background="#2196F3"
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
</Border>
<ControlTemplate.Triggers>
<!– 按下时背景变深 –>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="#1976D2"/>
<Setter TargetName="Border" Property="Margin" Value="1,1,0,0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
3. Command 属性(MVVM 核心)
csharp:
[Bindable(true)]
[Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public ICommand Command { get; set; }
逐句解析:
- [Category("Action")]:归类到“操作”组
- [Localizability(LocalizationCategory.NeverLocalize)]:标记为永远不需要本地化
- 类型:ICommand 接口
- 核心作用:绑定到 ViewModel 中的命令,实现 UI 与业务逻辑的分离
- 工业场景意义:工业项目中强烈推荐使用命令绑定,而不是 Click 事件,便于单元测试和维护
示例:MVVM 命令绑定
xaml:
<Button Content="保存参数"
Command="{Binding SaveParametersCommand}"/>
4. CommandParameter 属性
csharp:
[Bindable(true)]
[Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public object CommandParameter { get; set; }
- 作用:传递给命令的参数
- 工业场景应用:同一个命令处理多个按钮的点击,通过参数区分不同的操作
示例:带参数的命令绑定
XAML:
<StackPanel Orientation="Horizontal">
<Button Content="配方1"
Command="{Binding LoadRecipeCommand}"
CommandParameter="1"/>
<Button Content="配方2"
Command="{Binding LoadRecipeCommand}"
CommandParameter="2"/>
<Button Content="配方3"
Command="{Binding LoadRecipeCommand}"
CommandParameter="3"/>
</StackPanel>
5. CommandTarget 属性
csharp:
[Bindable(true)]
[Category("Action")]
public IInputElement CommandTarget { get; set; }
- 作用:指定命令的目标元素。
- 使用场景:当命令需要在特定元素上执行时使用,比如路由命令。
- 工业场景中很少直接使用:通常使用默认的目标元素即可。
六、核心路由事件逐行解析
ClickEvent
C#:
public static readonly RoutedEvent ClickEvent;
public event RoutedEventHandler Click
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
逐句解析:
- public static readonly RoutedEvent ClickEvent:静态只读路由事件字段,是 WPF 路由事件的标准定义方式。
- event RoutedEventHandler Click:公共事件包装器,提供标准的事件订阅/取消订阅语法。
- add => AddHandler(ClickEvent, value):添加事件处理程序到路由事件。
- remove => RemoveHandler(ClickEvent, value):从路由事件中移除事件处理程序。
核心特性:
- 路由策略:冒泡路由(Bubble),事件从触发元素向上传播到根元素
- 触发时机:根据 ClickMode 属性的设置,在按下、释放或悬停时触发
- 与 Command 的关系:Click 事件触发后,会自动执行绑定的 Command 命令
- 可取消性:不能直接取消 Click 事件,但可以通过设置 e.Handled = true 阻止事件继续冒泡
工业最佳实践:
- ✅ MVVM 模式下优先使用 Command 绑定
- ✅ 只有在简单的 UI 交互时才使用 Click 事件
- ❌ 不要在 Click 事件中编写复杂业务逻辑
七、受保护方法逐行解析(自定义按钮必备)
这些方法是自定义 ButtonBase 派生类时必须掌握的,ButtonBase 的所有核心逻辑都在这里实现。
1. OnClick() 方法(核心中的核心)
csharp:
protected virtual void OnClick();
-
官方源码实现:
csharp:
protected virtual void OnClick()
{
// 触发 Click 路由事件
RaiseEvent(new RoutedEventArgs(ClickEvent, this));// 执行绑定的 Command 命令
if (Command != null && Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
} -
核心逻辑:
- 触发 Click 路由事件。
- 执行绑定的 Command 命令。
-
自定义注意事项:重写时必须调用 base.OnClick(),否则 Click 事件和 Command 都不会执行。
2. OnIsPressedChanged() 方法
csharp:
protected virtual void OnIsPressedChanged(DependencyPropertyChangedEventArgs e);
- 触发时机:当 IsPressed 属性的值发生变化时调用
- 默认实现:更新按钮的视觉状态
- 自定义注意事项:重写时必须调用基类方法,否则按钮的按下效果会失效
3. 鼠标事件处理方法
csharp:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e);
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e);
protected override void OnMouseEnter(MouseEventArgs e);
protected override void OnMouseLeave(MouseEventArgs e);
protected override void OnLostMouseCapture(MouseEventArgs e);
-
核心作用:处理鼠标输入,维护 IsPressed 状态,根据 ClickMode 触发 Click 事件
-
默认实现逻辑:
- OnMouseLeftButtonDown:捕获鼠标,设置 IsPressed 为 true,如果 ClickMode="Press" 则触发 Click 事件
- OnMouseLeftButtonUp:释放鼠标捕获,设置 IsPressed 为 false,如果 ClickMode="Release" 则触发 Click 事件
- OnMouseLeave:如果鼠标在按下状态离开按钮,设置 IsPressed 为 false
- OnLostMouseCapture:当鼠标捕获丢失时,重置按钮状态
4. 键盘事件处理方法
csharp:
protected override void OnKeyDown(KeyEventArgs e);
protected override void OnKeyUp(KeyEventArgs e);
-
核心作用:处理键盘输入,支持空格键和回车键触发点击。
-
默认实现逻辑:
- 当按下空格键或回车键时,设置 IsPressed 为 true。
- 当释放空格键或回车键时,触发 Click 事件。
八、ButtonBase 核心工作流程
8.1 鼠标点击完整流程(ClickMode.Release)
当用户用鼠标点击按钮时,WPF 会按照以下顺序执行:
8.2 不同 ClickMode 的触发时机
| Release | 鼠标左键释放时 | 普通鼠标操作 |
| Press | 鼠标左键按下时 | 触摸屏设备、工业按钮 |
| Hover | 鼠标悬停在按钮上时 | 特殊交互场景 |
九、派生类实现原理
所有按钮类控件都继承自 ButtonBase,只需要重写少量方法即可实现自己的特殊行为:
- Button:重写OnClick()方法,增加了默认按钮和取消按钮的逻辑
- ToggleButton:重写OnClick()方法,增加了状态切换逻辑
- RepeatButton:重写鼠标事件处理方法,实现长按重复触发逻辑
- CheckBox:重写OnToggle()方法,实现复选框逻辑
- RadioButton:重写OnToggle()方法,实现单选逻辑
十、工业上位机典型应用实例
实例 1:触摸屏工业按钮
xaml:
<!– 触摸屏专用按钮,按下立即触发,无延迟 –>
<Button Content="启动设备"
Width="120"
Height="40"
ClickMode="Press"
Style="{StaticResource SuccessButtonStyle}"
Command="{Binding StartProductionCommand}"/>
实例 2:带参数的命令绑定
xaml:
<!– 配方选择按钮,同一个命令处理多个配方 –>
<GroupBox Header="配方选择">
<UniformGrid Columns="3">
<Button Content="配方1"
Command="{Binding LoadRecipeCommand}"
CommandParameter="1"/>
<Button Content="配方2"
Command="{Binding LoadRecipeCommand}"
CommandParameter="2"/>
<Button Content="配方3"
Command="{Binding LoadRecipeCommand}"
CommandParameter="3"/>
</UniformGrid>
</GroupBox>
实例 3:自定义按钮模板(工业风格)
xaml:
<Style x:Key="IndustrialButtonStyle" TargetType="Button">
<Setter Property="MinWidth" Value="100"/>
<Setter Property="MinHeight" Value="40"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Background" Value="#F5F5F5"/>
<Setter Property="BorderBrush" Value="#E0E0E0"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<!– 按下效果 –>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="#E0E0E0"/>
<Setter TargetName="Border" Property="Margin" Value="1,1,0,0"/>
</Trigger>
<!– 禁用效果 –>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#BDBDBD"/>
<Setter Property="Foreground" Value="#757575"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
十一、最佳实践与常见问题
11.1 最佳实践
11.2 常见问题与解决方案
问题 1:Click 事件不触发
可能原因:
解决方案:
问题 2:Command 不执行
可能原因:
解决方案:
问题 3:触摸屏点击不灵敏
原因:使用了默认的ClickMode="Release"模式,触摸屏没有 “鼠标释放” 事件
解决方案:设置ClickMode="Press"
问题 4:按钮点击无响应
原因分析:
排查步骤:
问题 5:触摸屏点击延迟
原因分析:
WPF 按钮默认 ClickMode="Release",依赖鼠标释放事件触发 Click;触摸屏没有真正意义上的“悬停”和“释放”状态,系统需要额外判断触摸抬起时机,因此表现为点击延迟。
解决步骤:
<Button Content="启动设备"
ClickMode="Press"/>
问题 6:自定义模板后按钮样式丢失
原因分析:
解决步骤:
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="#E0E0E0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#757575"/>
</Trigger>
</ControlTemplate.Triggers>
11.3 性能与边界条件注意事项
性能方面:
边界条件:
十二、官方设计意图总结
微软设计 ButtonBase 的核心目标是:
总结
ButtonBase 是 WPF 所有按钮类控件的抽象基类,围绕「点击触发、命令执行、状态反馈」三个核心维度统一了按钮类控件的行为模型:
- ClickEvent:冒泡路由事件,按钮被点击时触发。
- ClickMode:控制点击触发时机,触摸屏应使用 Press。
- IsPressed:只读状态属性,用于模板触发器实现按下反馈。
- Command/CommandParameter/CommandTarget:原生支持 MVVM 命令模式,实现 UI 与业务逻辑分离。
工业应用建议:
掌握 ButtonBase 的核心原理,既能正确使用 Button、ToggleButton、CheckBox 等派生控件,也能开发出符合工业标准、稳定可靠的自定义按钮控件。

