WPF实现带全选复选框的列表控件

本文将说明如何创建一个带全选复选框的列表控件。其效果如下图:

这个控件是由一个复选框(CheckBox)与一个 ListView 组合而成。它的操作逻辑:

  • 当选中“全选”时,列表中所有的项目都会被选中;反之,取消选中“全选”时,所有项都会被取消勾选。
  • 在列表中选中部分数据项目时,“全选”框会呈现不确定状态(Indetermine)。

由此看出,“全选”复选框与列表项中的复选框达到了双向控制的效果。

其设计思路:首先,创建自定义控件(CheckListView),在其 ControlTemplate 中定义 CheckBox 和 ListView,并为 ListView 设置 ItemTemplate,在其中增加 CheckBox 控件,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<ControlTemplate TargetType= "{x:Type control:CheckListView}" >
           <Grid Background= "{TemplateBinding Background}" >
             <Grid.RowDefinitions>
               <RowDefinition Height= "Auto" />
               <RowDefinition Height= "*" />
             </Grid.RowDefinitions>
             
             <CheckBox Content= "全选" />           
             
             <ListView x:Name= "list"
                  Grid.Row= "1" >
               <ListView.ItemTemplate>
                 <DataTemplate>
                   <CheckBox />                 
                 </DataTemplate>
               </ListView.ItemTemplate>
             </ListView>
           </Grid>
         </ControlTemplate>

其次,为控件添加两个依赖属性,其中一个为 ItemsSource,即该控件所要接收的数据源,也即选择列表;本质上,这个数据源会指定给其内的 ListView。另外也需要一个属性 IsSelectAllChecked 表示是否选中全选复选框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static readonly DependencyProperty IsSelectAllCheckedProperty =
       DependencyProperty.Register( "IsSelectAllChecked" , typeof ( bool ?), typeof (CheckListView), new PropertyMetadata( false ));
 
     public static readonly DependencyProperty ItemsSourceProperty =
       DependencyProperty.Register( "ItemsSource" , typeof ( object ), typeof (CheckListView), new PropertyMetadata( null ));
 
     /// <summary>
     /// 返回或设置全选复选框的选中状态
     /// </summary>
     public bool ? IsSelectAllChecked
     {
       get { return ( bool ?)GetValue(IsSelectAllCheckedProperty); }
       set { SetValue(IsSelectAllCheckedProperty, value); }
     }
 
     /// <summary>
     /// 数据源
     /// </summary>
     public object ItemsSource
     {
       get { return ( object )GetValue(ItemsSourceProperty); }
       set { SetValue(ItemsSourceProperty, value); }
     }

需要注意的一点是,作为一个自定义控件,我们必须考虑它的通用性,所以为了保证能设置各式各样的数据源(如用户列表、物品列表或 XX名称列表),在这里定义一个数据接口,只要数据源中的数据项实现该接口,即可达到通用的效果。该接口定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
public interface ICheckItem
   {
     /// <summary>
     /// 当前项是否选中
     /// </summary>
     bool IsSelected { get ; set ; }
 
     /// <summary>
     /// 名称
     /// </summary>
     string Name { get ; set ; }
   }

最后,我们把刚才定的属性绑定的控件上,如下:

1
2
3
4
5
6
7
8
<CheckBox Content= "全选" IsChecked= "{Binding IsSelectAllChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
  <ListView x:Name= "list" Grid.Row= "1" ItemsSource= "{TemplateBinding ItemsSource}" >
    <ListView.ItemTemplate>
     <DataTemplate>
         <CheckBox Content= "{Binding Name}" IsChecked= "{Binding IsSelected}" />
           </DataTemplate>
         </ListView.ItemTemplate>
</ListView>

接下来,实现具体操作:

首先,通过“全选”复选框来控制所有列表项:这里通过其 Click 事件来执行 CheckAllItems 方法, 在此方法中,会对数据源进行遍历,将其 IsSelected 属性设置为 True 或 False。代码如下:

1
2
3
4
5
6
7
<CheckBox Content= "全选" IsChecked= "{Binding IsSelectAllChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" >
       <i:Interaction.Triggers>
         <i:EventTrigger EventName= "Click" >
            <ei:CallMethodAction MethodName= "CheckAllItems" TargetObject= "{Binding RelativeSource={RelativeSource TemplatedParent}}" />
          </i:EventTrigger>
         </i:Interaction.Triggers>
  </CheckBox>
1
2
3
4
5
6
7
8
9
10
/// <summary>
/// 全选或清空所用选择
/// </summary>
     public void CheckAllItems()
     {
       foreach (ICheckItem item in ItemsSource as IList<ICheckItem>)
       {
         item.IsSelected = IsSelectAllChecked.HasValue ? IsSelectAllChecked.Value : false ;
       }
     }

然后,通过选中或取消选中列表项时,更新“全选”复选框的状态:在 DataTemplate 中,我们也为 CheckBox 的 Click 事件设置了要触发的方法 UpdateSelectAllState,代码如下:

1
2
3
4
5
6
7
8
9
<DataTemplate>
    <CheckBox Content= "{Binding Name}" IsChecked= "{Binding IsSelected}" >
      <i:Interaction.Triggers>
      <i:EventTrigger EventName= "Click" >
       <ei:CallMethodAction MethodName= "UpdateSelectAllState" TargetObject= "{Binding RelativeSource={RelativeSource AncestorType=control:CheckListView}}" />
      </i:EventTrigger>
      </i:Interaction.Triggers>
   </CheckBox>
</DataTemplate>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/// <summary>
/// 根据当前选择的个数来更新全选框的状态
/// </summary>
     public void UpdateSelectAllState()
     {
       var items = ItemsSource as IList<ICheckItem>;
       if (items == null )
       {
         return ;
       }
 
       // 获取列表项中 IsSelected 值为 True 的个数,并通过该值来确定 IsSelectAllChecked 的值
       int count = items.Where(item => item.IsSelected).Count();
       if (count == items.Count)
       {
         IsSelectAllChecked = true ;
       }
       else if (count == 0)
       {
         IsSelectAllChecked = false ;
       }
       else
       {       
         IsSelectAllChecked = null ;
       }
     }

这里也有两点需要提醒:

我一开始定义属性 IsSelectAllChecked 时,它的类型是 bool 类型,那么,由于 CheckBox 控件的 IsChecked 值为 null 时,它将呈现 Indetermine 状态,所以后来把它改为 bool? 类型。

在XAML 代码中可以看出,对事件以及事件的响应使用了行为,所以,需要添加引用 System.Windows.Interactivity.dll 和 Microsoft.Expression.Interactions.dll 两个库,并在XMAL 头部添加如下命名空间的引用:

这样,这个控件就基本完成了,接下来是如何使用它。

首先,定义将要在列表中展示的数据项,并为它实现之前提到的 ICheckItem 接口,这里定义了一个 User 类,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User : BindableBase, ICheckItem
   {
     private bool isSelected;
     private string name;
 
     public bool IsSelected
     {
       get { return isSelected; }
       set { SetProperty( ref isSelected, value); }
     }
 
     public string Name
     {
       get { return name; }
       set { SetProperty( ref name, value); }
     }
   }

接下来在 ViewModel 中定义一个列表 List<ICheckItem>,并添加数据,最后在 UI 上为其绑定 ItemsSource 属性即可,在此不再贴代码了,具体请参考源代码。

源码下载

 

The TargetObject is the object which has the method to be invoked, if you have the method in your window code-behind then the object is the window itself

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
             ...etcetera...
             x:Name="UserControl">

So your CallMethodAction would be:

<ei:CallMethodAction MethodName="MyMethod"
                     TargetObject="{Binding ElementName=UserControl, Mode=OneWay}"/>
 

转载于:https://www.cnblogs.com/sjqq/p/7891383.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值