# WPF CheckBox 控件详解与使用—WPF 中的控件(6)--复选框控件(CheckBox)

WPF CheckBox 控件详解与使用

---WPF 中的控件(6)--复选框控件(CheckBox)

文章目录

官方文档 CheckBox 类

CheckBox 是 WPF 中最常用的 选择类控件 ,继承自 ToggleButton,核心特性是支持 多选 、可选的 三态模式(勾选/未勾选/不确定),常用于协议同意、功能开关、批量选项筛选等场景。


一、控件基础概述

1. 命名空间与继承链

  • 命名空间:System.Windows.Controls
  • 继承链:CheckBox → ToggleButton → ButtonBase → ContentControl → Control → ...
  • 核心特性:
    • 默认支持 两种状态 :勾选(IsChecked=true)、未勾选(IsChecked=false)
    • 开启 IsThreeState 后支持 三种状态 :新增「不确定」(IsChecked=null)
    • 属于 ContentControl,内容可承载任意 WPF 元素(文字、图片、布局面板等)

二、核心属性详解

属性名 类型 默认值 说明
IsChecked bool?(可空布尔) false 控件的勾选状态:true = 勾选,false = 未勾选,null = 不确定
IsThreeState bool false 是否启用三态模式;启用后用户点击可循环切换三种状态
Content object 空 显示的内容,支持文本、图片、布局等任意元素
IsEnabled bool true 控件是否可用,禁用后呈灰色且无法交互
Command ICommand 空 点击时执行的命令,MVVM 场景常用
CommandParameter object 空 传递给 Command 的参数
Foreground/Background Brush 系统默认 文字颜色/背景色
FontSize/FontFamily 数值/字体 系统默认 文字大小与字体

三、核心事件详解

事件名 触发时机
Checked 当 IsChecked 变为 true(勾选)时触发
Unchecked 当 IsChecked 变为 false(未勾选)时触发
Indeterminate 当 IsChecked 变为 null(不确定)时触发(仅三态模式下用户可触发)
Click 控件被点击时触发(无论状态是否变化)

四、基础使用示例(带详细注释)

UI 部分
xaml 复制代码
        <!--CheckBox 基础示例-->
        <StackPanel Margin="20" >
            <!-- 1. 最基础的CheckBox:绑定勾选/取消勾选事件 -->
            <CheckBox x:Name="chkAgreement" 
                  Content="我已阅读并同意用户服务协议"
                  FontSize="14"
                  Checked="ChkAgreement_Checked"
                  Unchecked="ChkAgreement_Unchecked"/>

            <!-- 2. 初始状态为勾选的CheckBox -->
            <CheckBox x:Name="chkAutoLogin" 
                  Content="自动登录账号"
                  IsChecked="True"
                  FontSize="14"/>

            <!-- 3. 禁用状态的CheckBox -->
            <CheckBox x:Name="chkDisabled" 
                  Content="该选项暂不可用"
                  IsEnabled="False"
                  FontSize="14"/>

            <!-- 4. 图文混排CheckBox(Content承载复杂内容) -->
            <CheckBox x:Name="chkImage"
                  FontSize="14">
                <!-- Content可以是任意布局,实现图文组合 -->
                <StackPanel Orientation="Horizontal" >
                    <Image Source="/Tank.png" Width="18" Height="18"/>
                    <TextBlock Text="开启消息提醒" Margin="8,2"/>
                </StackPanel>
            </CheckBox>

            <!-- 按钮:获取所有CheckBox状态 -->
            <Button Content="获取选中状态" 
                Width="120" Height="30"
                Click="BtnGetState_Click"
                Margin="0 10 0 0" HorizontalAlignment="Left"/>            
        </StackPanel>
后台 C# 代码
复制代码
#region   CheckBox 基础示例
/// <summary>
/// CheckBox勾选时触发
/// </summary>
private void ChkAgreement_Checked(object sender, RoutedEventArgs e)
{
    // sender 是触发事件的控件对象,强转为CheckBox即可访问属性
    if (sender is CheckBox checkBox)
    {
        MessageBox.Show($"已勾选:{checkBox.Content}");
    }
}

/// <summary>
/// CheckBox取消勾选时触发
/// </summary>
private void ChkAgreement_Unchecked(object sender, RoutedEventArgs e)
{
    if (sender is CheckBox checkBox)
    {
        MessageBox.Show($"已取消勾选:{checkBox.Content}");
    }
}

/// <summary>
/// 按钮点击:获取CheckBox状态
/// </summary>
private void BtnGetState_Click(object sender, RoutedEventArgs e)
{
    // 注意:IsChecked是bool?可空类型,必须用 == true 判断
    string result = $"协议勾选:{chkAgreement.IsChecked == true}\n" +
                    $"自动登录:{chkAutoLogin.IsChecked == true}\n" +
                    $"消息提醒:{chkImage.IsChecked == true}";

    MessageBox.Show(result, "当前状态");
}
#endregion  CheckBox 基础示例

⚠️ 注意:IsChecked 是 bool?(可空布尔),不能直接写 if (chk.IsChecked) ,必须显式判断 == true,否则 null 和 false 都会被判定为 false。


五、三态模式使用(IsThreeState)

当需要表示「部分选中」「不确定」状态时(例如树形结构的父节点、批量选择的全选框),启用三态模式。

示例代码

xaml 复制代码
<!-- 全选框:启用三态模式 -->
<CheckBox x:Name="chkAll" Margin="10 10 0 0"
      Content="全选框:启用三态模式"
      FontSize="14"
      IsThreeState="True"
      Checked="ChkAll_Checked"
      Unchecked="ChkAll_Unchecked"
      Indeterminate="ChkAll_Indeterminate"/>
c# 复制代码
#region  全选框:启用三态模式
/// <summary>
/// 全选框:启用三态模式-已选中(已选中)
/// </summary>
private void ChkAll_Checked(object sender, RoutedEventArgs e)
{
    if (sender is CheckBox checkBox)
    {
        MessageBox.Show($"启用三态模式-已选中(已选中)");
    }
}
/// <summary>
/// 全选框:启用三态模式-未选中(已清除)
/// </summary>
private void ChkAll_Unchecked(object sender, RoutedEventArgs e)
{
    if (sender is CheckBox checkBox)
    {
        MessageBox.Show($"启用三态模式-未选中(已清除)");
    }
}
/// <summary>
/// 全选框:启用三态模式-不确定
/// </summary>
private void ChkAll_Indeterminate(object sender, RoutedEventArgs e)
{
    if (sender is CheckBox checkBox)
    {
        MessageBox.Show($"启用三态模式-不确定");
    }
}
#endregion  全选框:启用三态模式

提示:即使 IsThreeState=false,代码也可以手动给 IsChecked 赋值 null,只是用户无法通过点击切换到该状态。


六、MVVM 数据绑定示例

WPF 推荐使用 MVVM 模式,通过绑定实现 UI 与逻辑解耦。

1.ViewModelBase 代码

c# 复制代码
using System.ComponentModel;
using System.Runtime.CompilerServices;

   /// <summary>
   /// ViewModel 基类,实现 INotifyPropertyChanged 接口
   /// 负责属性变更通知,当属性值改变时通知 UI 刷新
   /// </summary>
   public class ViewModelBase : INotifyPropertyChanged
   {
       /// <summary>
       /// 属性变更事件,WPF 绑定系统会自动监听此事件
       /// 当属性值变化时触发该事件,UI 随之更新
       /// </summary>
       public event PropertyChangedEventHandler? PropertyChanged;

       /// <summary>
       /// 触发属性变更通知
       /// </summary>
       /// <param name="propertyName"> 变更的属性名,使用 CallerMemberName 自动获取 </param>
       protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
       {
           // 如果有订阅者,则触发事件
           PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
       }

       /// <summary>
       /// 通用属性设置方法,自动判断值是否变化并触发通知
       /// 简化属性定义,避免在每个属性中重复编写判断逻辑
       /// </summary>
       /// <typeparam name="T"> 属性类型 </typeparam>
       /// <param name="field"> backing field 引用 </param>
       /// <param name="newValue"> 新值 </param>
       /// <param name="propertyName"> 属性名(自动获取)</param>
       /// <returns> 值是否发生了变化 </returns>        
       protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
       {
           // 等值预判断:使用默认相等比较器比较当前字段值与新值
           if (EqualityComparer<T>.Default.Equals(field, newValue))
           {
               // 值未变化,直接返回 false,不触发任何事件
               return false;
           }

           //触发「属性即将变更」事件(INotifyPropertyChanging)件发生在赋值之前,订阅者可以在此获取旧值
           //OnPropertyChanging(propertyName);  

           //实际赋值
           field = newValue;

           //触发「属性已变更」事件(INotifyPropertyChanged)。事件发生在赋值之后,通知 UI 绑定重新读取属性值
           OnPropertyChanged(propertyName);

           // 返回 true 表示属性值已成功变更并发出通知
           return true;
       }

        /// <summary>
       /// 通过回调委托为外部模型对象设置属性值,并在值变更时触发变更通知事件
       /// 用于为不实现变更通知的外部模型类(如 DTO、数据库实体、第三方模型)包装可观察的代理属性。
       /// </summary>
       /// <typeparam name="TModel"> 目标模型的类型,必须为引用类型 </typeparam>
       /// <typeparam name="T"> 属性值的类型 </typeparam>
       /// <param name="oldValue"> 模型属性的当前值(无法直接读取模型字段,需调用方手动传入)</param>
       /// <param name="newValue"> 属性的新值 </param>
       /// <param name="model"> 目标模型对象实例 </param>
       /// <param name="callback"> 赋值回调:接收模型实例和新值,执行具体的属性赋值操作 </param>
       /// <param name="propertyName"> 属性名称,默认由编译器自动填充 </param>
       /// <returns> 若值发生了实际变更则返回 true;若新旧值相等则返回 false </returns>
       protected bool SetProperty<TModel, T>(T oldValue,T newValue,TModel model,Action<TModel, T> callback,	                                   [CallerMemberName] string? propertyName = null)    where TModel : class
       {
           // 参数非空校验
           ArgumentNullException.ThrowIfNull(model);
           ArgumentNullException.ThrowIfNull(callback);

           // 等值判断
           if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
           {
               // 值未变化,直接返回
               return false;
           }

           // 触发「属性即将变更」事件
           //OnPropertyChanging(propertyName);

           // 执行赋值回调:将新值写入模型对象的对应属性
           //    具体赋值逻辑由调用方通过 callback 委托传入,本方法不关心模型的内部结构
           callback(model, newValue);

           // 触发「属性已变更」事件,通知 UI 更新
           OnPropertyChanged(propertyName);

           //返回 true 表示属性值已成功变更
           return true;
       }
   }

2. ViewModel 代码(实现属性通知)

c# 复制代码
using MVVM_manual.Core;

    public class SettingViewModel : ViewModelBase
    {
        //  backing field:后台存储字段
        private bool? _isAutoUpdate=false;
        private bool _isOpenNotification;
        private string chkResult  = "--";

        public bool? IsAutoUpdate
        {
            get => _isAutoUpdate;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty <T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)                
                if (SetProperty(ref _isAutoUpdate, value))
                {
                    if (_isAutoUpdate==null) ChkResult = $"IsAutoUpdate=null,IsOpenNotification={_isOpenNotification.ToString()}";
                    else ChkResult = $"IsAutoUpdate={_isAutoUpdate.ToString()},IsOpenNotification={_isOpenNotification.ToString()}";
                }
            }
        }

        public string ChkResult
        {
            get => chkResult;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty <T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)                
                SetProperty(ref chkResult, value);                
            }
        }

        public bool IsOpenNotification
        {
            get => _isOpenNotification;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty <T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)                
                if (SetProperty(ref _isOpenNotification, value))
                {
                    if (_isAutoUpdate == null) ChkResult = $"IsAutoUpdate=null,IsOpenNotification={_isOpenNotification.ToString()}";
                    else ChkResult = $"IsAutoUpdate={_isAutoUpdate.ToString()},IsOpenNotification={_isOpenNotification.ToString()}";
                }
            }
        }
    }

3. XAML 绑定

xaml 复制代码
 <!-- 设置DataContext,关联ViewModel -->
<Window.DataContext>
    <local:SettingViewModel/>
</Window.DataContext> 
 
 
 <TextBlock Text="MVVM 数据绑定示例 " />
 <StackPanel Margin="20" >
     <!-- 双向绑定IsChecked,Mode=TwoWay可省略(CheckBox默认双向) -->
     <CheckBox Content="自动更新软件(三态)"
           IsThreeState="True"
           IsChecked="{Binding IsAutoUpdate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
           FontSize="14"/>

     <CheckBox Content="开启桌面通知(二态)"
           IsChecked="{Binding IsOpenNotification, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
           FontSize="14"/>
     <StackPanel Orientation="Horizontal">
         <TextBlock Text="两个CheckBox的选择结果:" Margin="10,2" />
         <TextBox Text="{Binding ChkResult, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,2"/>
     </StackPanel>
 </StackPanel>

七、自定义样式与外观

WPF 原生 CheckBox 的勾选框由系统渲染,若需自定义颜色、形状,需重写 ControlTemplate。

简易自定义样式示例

xaml 复制代码
<Window.Resources>
    <!-- 自定义CheckBox样式,修改勾选框颜色 -->
    <Style x:Key="CustomCheckBoxStyle" TargetType="CheckBox">
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Margin" Value="0 5"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="CheckBox">
                    <!-- 外层布局:勾选框 + 内容 -->
                    <BulletDecorator Background="Transparent">
                        <BulletDecorator.Bullet>
                            <!-- 自定义勾选框边框 -->
                            <Border x:Name="Border"
                                    Width="16" Height="16"
                                    BorderBrush="#333"
                                    BorderThickness="1"
                                    Background="White"
                                    CornerRadius="2">
                                <!-- 勾选标记:默认隐藏 -->
                                <Path x:Name="CheckMark"
                                      Width="10" Height="10"
                                      Data="M 2 5 L 5 8 L 9 2"
                                      Stroke="#2196F3"
                                      StrokeThickness="2"
                                      Visibility="Collapsed"/>
                            </Border>
                        </BulletDecorator.Bullet>
                        
                        <!-- 内容显示区 -->
                        <ContentPresenter Margin="8 0 0 0"
                                          VerticalAlignment="Center"/>
                    </BulletDecorator>

                    <!-- 触发器:根据IsChecked状态切换显示 -->
                    <ControlTemplate.Triggers>
                        <!-- 勾选状态:显示对勾 -->
                        <Trigger Property="IsChecked" Value="True">
                            <Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
                        </Trigger>
                        <!-- 不确定状态:显示方块 -->
                        <Trigger Property="IsChecked" Value="{x:Null}">
                            <Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="CheckMark" Property="Data" Value="M 3 3 L 13 3 L 13 13 L 3 13 Z"/>
                            <Setter TargetName="CheckMark" Property="Fill" Value="#2196F3"/>
                        </Trigger>
                        <!-- 禁用状态 -->
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Border" Property="Background" Value="#EEE"/>
                            <Setter Property="Foreground" Value="#999"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<!-- 使用自定义样式 -->
<CheckBox Content="自定义样式勾选框" Style="{StaticResource CustomCheckBoxStyle}"/>

八、常见场景与注意事项

  1. 批量获取选中项 多个 CheckBox 放在容器中时,可通过遍历容器子元素、或绑定集合的方式获取选中状态;推荐使用数据绑定(ObservableCollection + 项模板)实现批量管理。
  2. 与 RadioButton 的区别
    • CheckBox:支持多选,彼此无互斥关系
    • RadioButton:同组内单选,必须通过 GroupName 分组实现互斥
  3. 性能优化 大量 CheckBox 列表场景,使用 VirtualizingStackPanel 开启虚拟化,避免一次性渲染所有控件。
  4. 三态使用限制 Indeterminate 状态仅用于表示「部分选中」的中间态,不建议作为独立的业务值存储,最终业务数据应映射为明确的 true/false。

九、源码

1.UI

xaml 复制代码
<Window x:Class="CheckBoxCtrl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CheckBoxCtrl"
        mc:Ignorable="d"
        Title="CheckBox 示例" Height="600" Width="800">
    <!-- 设置DataContext,关联ViewModel -->
    <Window.DataContext>
        <local:SettingViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <!-- 自定义CheckBox样式,修改勾选框颜色 -->
        <Style x:Key="CustomCheckBoxStyle" TargetType="CheckBox">
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="Margin" Value="0 5"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="CheckBox">
                        <!-- 外层布局:勾选框 + 内容 -->
                        <BulletDecorator Background="Transparent">
                            <BulletDecorator.Bullet>
                                <!-- 自定义勾选框边框 -->
                                <Border x:Name="Border"
                                    Width="16" Height="16"
                                    BorderBrush="#333"
                                    BorderThickness="1"
                                    Background="White"
                                    CornerRadius="2">
                                    <!-- 勾选标记:默认隐藏 -->
                                    <Path x:Name="CheckMark"
                                      Width="10" Height="10"
                                      Data="M 2 5 L 5 8 L 9 2"
                                      Stroke="#2196F3"
                                      StrokeThickness="2"
                                      Visibility="Collapsed"/>
                                </Border>
                            </BulletDecorator.Bullet>

                            <!-- 内容显示区 -->
                            <ContentPresenter Margin="8 0 0 0"
                                          VerticalAlignment="Center"/>
                        </BulletDecorator>

                        <!-- 触发器:根据IsChecked状态切换显示 -->
                        <ControlTemplate.Triggers>
                            <!-- 勾选状态:显示对勾 -->
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
                            </Trigger>
                            <!-- 不确定状态:显示方块 -->
                            <Trigger Property="IsChecked" Value="{x:Null}">
                                <Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
                                <Setter TargetName="CheckMark" Property="Data" Value="M 3 3 L 13 3 L 13 13 L 3 13 Z"/>
                                <Setter TargetName="CheckMark" Property="Fill" Value="#2196F3"/>
                            </Trigger>
                            <!-- 禁用状态 -->
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter TargetName="Border" Property="Background" Value="#EEE"/>
                                <Setter Property="Foreground" Value="#999"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel Margin="10">
        <TextBlock Text="CheckBox 基础示例 " />
        <StackPanel Margin="20" >
            <!-- 1. 最基础的CheckBox:绑定勾选/取消勾选事件 -->
            <CheckBox x:Name="chkAgreement" 
                  Content="我已阅读并同意用户服务协议"
                  FontSize="14"
                  Checked="ChkAgreement_Checked"
                  Unchecked="ChkAgreement_Unchecked"/>

            <!-- 2. 初始状态为勾选的CheckBox -->
            <CheckBox x:Name="chkAutoLogin" 
                  Content="自动登录账号"
                  IsChecked="True"
                  FontSize="14"/>

            <!-- 3. 禁用状态的CheckBox -->
            <CheckBox x:Name="chkDisabled" 
                  Content="该选项暂不可用"
                  IsEnabled="False"
                  FontSize="14"/>

            <!-- 4. 图文混排CheckBox(Content承载复杂内容) -->
            <CheckBox x:Name="chkImage"
                  FontSize="14">
                <!-- Content可以是任意布局,实现图文组合 -->
                <StackPanel Orientation="Horizontal" >
                    <Image Source="/Tank.png" Width="18" Height="18"/>
                    <TextBlock Text="开启消息提醒" Margin="8,2"/>
                </StackPanel>
            </CheckBox>

            <!-- 按钮:获取所有CheckBox状态 -->
            <Button Content="获取选中状态" 
                Width="120" Height="30"
                Click="BtnGetState_Click"
                Margin="0 10 0 0" HorizontalAlignment="Left"/>            
        </StackPanel>
        <Separator Background="Red" Height="10" Margin="20,2" />
        
        <TextBlock Text="全选框:启用三态模式 " />
        <CheckBox x:Name="chkAll" Margin="10 10 0 0"
              Content="全选框:启用三态模式"
              FontSize="14"
              IsThreeState="True"
              Checked="ChkAll_Checked"
              Unchecked="ChkAll_Unchecked"
              Indeterminate="ChkAll_Indeterminate"/>
        <Separator Background="Red" Height="10" Margin="20,2" />

        <TextBlock Text="MVVM 数据绑定示例 " />
        <StackPanel Margin="20" >
            <!-- 双向绑定IsChecked,Mode=TwoWay可省略(CheckBox默认双向) -->
            <CheckBox Content="自动更新软件(三态)"
                  IsThreeState="True"
                  IsChecked="{Binding IsAutoUpdate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  FontSize="14"/>

            <CheckBox Content="开启桌面通知(二态)"
                  IsChecked="{Binding IsOpenNotification, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  FontSize="14"/>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="两个CheckBox的选择结果:" Margin="10,2" />
                <TextBox Text="{Binding ChkResult, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,2"/>
            </StackPanel>
        </StackPanel>
        <Separator Background="Red" Height="10" Margin="20,2" />

        <TextBlock Text="使用自定义样式" />
        <CheckBox Content="自定义样式勾选框" Style="{StaticResource CustomCheckBoxStyle}" Margin="10,2" />

    </StackPanel>
</Window>

2.代码

c# 复制代码
//1
using System.Windows;
using System.Windows.Controls;

namespace CheckBoxCtrl
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        
        #region   CheckBox 基础示例
        /// <summary>
        /// CheckBox勾选时触发
        /// </summary>
        private void ChkAgreement_Checked(object sender, RoutedEventArgs e)
        {
            // sender 是触发事件的控件对象,强转为CheckBox即可访问属性
            if (sender is CheckBox checkBox)
            {
                MessageBox.Show($"已勾选:{checkBox.Content}");
            }
        }

        /// <summary>
        /// CheckBox取消勾选时触发
        /// </summary>
        private void ChkAgreement_Unchecked(object sender, RoutedEventArgs e)
        {
            if (sender is CheckBox checkBox)
            {
                MessageBox.Show($"已取消勾选:{checkBox.Content}");
            }
        }

        /// <summary>
        /// 按钮点击:获取CheckBox状态
        /// </summary>
        private void BtnGetState_Click(object sender, RoutedEventArgs e)
        {
            // 注意:IsChecked是bool?可空类型,必须用 == true 判断
            string result = $"协议勾选:{chkAgreement.IsChecked == true}\n" +
                            $"自动登录:{chkAutoLogin.IsChecked == true}\n" +
                            $"消息提醒:{chkImage.IsChecked == true}";

            MessageBox.Show(result, "当前状态");
        }
        #endregion  CheckBox 基础示例

        #region  全选框:启用三态模式
        /// <summary>
        /// 全选框:启用三态模式-已选中(已选中)
        /// </summary>
        private void ChkAll_Checked(object sender, RoutedEventArgs e)
        {
            if (sender is CheckBox checkBox)
            {
                MessageBox.Show($"启用三态模式-已选中(已选中)");
            }
        }
        /// <summary>
        /// 全选框:启用三态模式-未选中(已清除)
        /// </summary>
        private void ChkAll_Unchecked(object sender, RoutedEventArgs e)
        {
            if (sender is CheckBox checkBox)
            {
                MessageBox.Show($"启用三态模式-未选中(已清除)");
            }
        }
        /// <summary>
        /// 全选框:启用三态模式-不确定
        /// </summary>
        private void ChkAll_Indeterminate(object sender, RoutedEventArgs e)
        {
            if (sender is CheckBox checkBox)
            {
                MessageBox.Show($"启用三态模式-不确定");
            }
        }
        #endregion  全选框:启用三态模式
    }
}

//2
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MVVM_manual.Core
{
    /// <summary>
    /// ViewModel 基类,实现 INotifyPropertyChanged 接口
    /// 负责属性变更通知,当属性值改变时通知 UI 刷新
    /// </summary>
    public class ViewModelBase : INotifyPropertyChanged
    {
        /// <summary>
        /// 属性变更事件,WPF 绑定系统会自动监听此事件
        /// 当属性值变化时触发该事件,UI 随之更新
        /// </summary>
        public event PropertyChangedEventHandler? PropertyChanged;

        /// <summary>
        /// 触发属性变更通知
        /// </summary>
        /// <param name="propertyName">变更的属性名,使用 CallerMemberName 自动获取</param>
        protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            // 如果有订阅者,则触发事件
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 通用属性设置方法,自动判断值是否变化并触发通知
        /// 简化属性定义,避免在每个属性中重复编写判断逻辑
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="field"> backing field 引用</param>
        /// <param name="newValue">新值</param>
        /// <param name="propertyName">属性名(自动获取)</param>
        /// <returns>值是否发生了变化</returns>        
        protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
        {
            // 等值预判断:使用默认相等比较器比较当前字段值与新值
            if (EqualityComparer<T>.Default.Equals(field, newValue))
            {
                // 值未变化,直接返回 false,不触发任何事件
                return false;
            }

            //触发「属性即将变更」事件(INotifyPropertyChanging)件发生在赋值之前,订阅者可以在此获取旧值
            //OnPropertyChanging(propertyName);  

            //实际赋值
            field = newValue;

            //触发「属性已变更」事件(INotifyPropertyChanged)。事件发生在赋值之后,通知 UI 绑定重新读取属性值
            OnPropertyChanged(propertyName);

            // 返回 true 表示属性值已成功变更并发出通知
            return true;
        }

        //protected bool SetProperty<T>([NotNullIfNotNull(nameof(newValue))] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
        //{
        //    // 等值预判断:使用默认相等比较器比较当前字段值与新值
        //    if (EqualityComparer<T>.Default.Equals(field, newValue))
        //    {
        //        // 值未变化,直接返回 false,不触发任何事件
        //        return false;
        //    }
        //    //触发「属性即将变更」事件(INotifyPropertyChanging)件发生在赋值之前,订阅者可以在此获取旧值
        //    //OnPropertyChanging(propertyName);  

        //    //实际赋值
        //    field = newValue;

        //    //触发「属性已变更」事件(INotifyPropertyChanged)。事件发生在赋值之后,通知 UI 绑定重新读取属性值
        //    OnPropertyChanged(propertyName);

        //    // 返回 true 表示属性值已成功变更并发出通知
        //    return true;
        //}

        /// <summary>
        /// 通过回调委托为外部模型对象设置属性值,并在值变更时触发变更通知事件
        /// 用于为不实现变更通知的外部模型类(如 DTO、数据库实体、第三方模型)包装可观察的代理属性。
        /// </summary>
        /// <typeparam name="TModel">目标模型的类型,必须为引用类型</typeparam>
        /// <typeparam name="T">属性值的类型</typeparam>
        /// <param name="oldValue">模型属性的当前值(无法直接读取模型字段,需调用方手动传入)</param>
        /// <param name="newValue">属性的新值</param>
        /// <param name="model">目标模型对象实例</param>
        /// <param name="callback">赋值回调:接收模型实例和新值,执行具体的属性赋值操作</param>
        /// <param name="propertyName">属性名称,默认由编译器自动填充</param>
        /// <returns>若值发生了实际变更则返回 true;若新旧值相等则返回 false</returns>
        protected bool SetProperty<TModel, T>(T oldValue,T newValue,TModel model,Action<TModel, T> callback,[CallerMemberName] string? propertyName = null)    where TModel : class
        {
            // 参数非空校验
            ArgumentNullException.ThrowIfNull(model);
            ArgumentNullException.ThrowIfNull(callback);

            // 等值判断
            if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
            {
                // 值未变化,直接返回
                return false;
            }

            // 触发「属性即将变更」事件
            //OnPropertyChanging(propertyName);

            // 执行赋值回调:将新值写入模型对象的对应属性
            //    具体赋值逻辑由调用方通过 callback 委托传入,本方法不关心模型的内部结构
            callback(model, newValue);

            // 触发「属性已变更」事件,通知 UI 更新
            OnPropertyChanged(propertyName);

            //返回 true 表示属性值已成功变更
            return true;
        }
    }
}

//3
using MVVM_manual.Core;

namespace CheckBoxCtrl
{
    public class SettingViewModel : ViewModelBase
    {
        //  backing field:后台存储字段
        private bool? _isAutoUpdate=false;
        private bool _isOpenNotification;
        private string chkResult  = "--";

        public bool? IsAutoUpdate
        {
            get => _isAutoUpdate;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty<T>(ref T field, T newValue,[CallerMemberName] string? propertyName = null)                
                if (SetProperty(ref _isAutoUpdate, value))
                {
                    if (_isAutoUpdate==null) ChkResult = $"IsAutoUpdate=null,IsOpenNotification={_isOpenNotification.ToString()}";
                    else ChkResult = $"IsAutoUpdate={_isAutoUpdate.ToString()},IsOpenNotification={_isOpenNotification.ToString()}";
                }
            }
        }

        public string ChkResult
        {
            get => chkResult;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty<T>(ref T field, T newValue,[CallerMemberName] string? propertyName = null)                
                SetProperty(ref chkResult, value);                
            }
        }

        public bool IsOpenNotification
        {
            get => _isOpenNotification;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty<T>(ref T field, T newValue,[CallerMemberName] string? propertyName = null)                
                if (SetProperty(ref _isOpenNotification, value))
                {
                    if (_isAutoUpdate == null) ChkResult = $"IsAutoUpdate=null,IsOpenNotification={_isOpenNotification.ToString()}";
                    else ChkResult = $"IsAutoUpdate={_isAutoUpdate.ToString()},IsOpenNotification={_isOpenNotification.ToString()}";
                }
            }
        }
    }
}
using MVVM_manual.Core;

namespace CheckBoxCtrl
{
    public class SettingViewModel : ViewModelBase
    {
        //  backing field:后台存储字段
        private bool? _isAutoUpdate=false;
        private bool _isOpenNotification;
        private string chkResult  = "--";

        public bool? IsAutoUpdate
        {
            get => _isAutoUpdate;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty<T>(ref T field, T newValue,[CallerMemberName] string? propertyName = null)                
                if (SetProperty(ref _isAutoUpdate, value))
                {
                    if (_isAutoUpdate==null) ChkResult = $"IsAutoUpdate=null,IsOpenNotification={_isOpenNotification.ToString()}";
                    else ChkResult = $"IsAutoUpdate={_isAutoUpdate.ToString()},IsOpenNotification={_isOpenNotification.ToString()}";
                }
            }
        }

        public string ChkResult
        {
            get => chkResult;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty<T>(ref T field, T newValue,[CallerMemberName] string? propertyName = null)                
                SetProperty(ref chkResult, value);                
            }
        }

        public bool IsOpenNotification
        {
            get => _isOpenNotification;
            set
            {
                // 使用基类 SetProperty 自动判断并触发通知
                //bool SetProperty<T>(ref T field, T newValue,[CallerMemberName] string? propertyName = null)                
                if (SetProperty(ref _isOpenNotification, value))
                {
                    if (_isAutoUpdate == null) ChkResult = $"IsAutoUpdate=null,IsOpenNotification={_isOpenNotification.ToString()}";
                    else ChkResult = $"IsAutoUpdate={_isAutoUpdate.ToString()},IsOpenNotification={_isOpenNotification.ToString()}";
                }
            }
        }
    }
}

十、显示效果

相关推荐
光泽雨2 天前
WPF 音视频四大核心方案对比
wpf·音视频
浪扼飞舟3 天前
WPF(数据绑定)
wpf
samble4 天前
从零搭建半导体设备上位机(四):单实例、启动流程与配置文件,App 里那 526 行到底在干什么
wpf·配置文件·单实例
浪扼飞舟4 天前
WPF(控件的继承树、内容控件、布局控件)
wpf
avi91117 天前
电影开源Jevdo - 源码介绍(vue版)没有android 和 没有wpf
android·wpf·框架·数据爬虫·meego·电影管理
吾与谁归in8 天前
C#脚本工具,支持智能提示自动补全(WPF + Roslyn + AvalonEdit)
开发语言·c#·wpf
昂好的day8 天前
WPF 从零入门(上):WPF 是什么、XAML 语法、布局与样式全解
开发语言·c#·wpf
影寂ldy9 天前
WPF导航窗体+Page页面跳转 知识点整理
wpf
He BianGu9 天前
【WPF-Control教程】IOC 依赖注入
c#·wpf·wpf-control