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

这个控件是由一个复选框(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}"/>
 

WPF实现带全选复选框的列表控件的更多相关文章

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

    本文将说明如何创建一个带全选复选框的列表控件.其效果如下图:     这个控件是由一个复选框(CheckBox)与一个 ListView 组合而成.它的操作逻辑: 当选中“全选”时,列表中所有的项目都 ...

  2. QTableWidget自定义表头QHeaderView加全选复选框

    1         QTableWidget自定义表头QHeaderView加全选复选框 在使用QTableWidget时需要在表头添加全选复选框,但是默认的表头无法添加复选框,只能用图片画上去一个复 ...

  3. ExtJS中,将Grid表头中的全选复选框取消复选

    今天发现公司产品用的EXTJS中使用Grid时,Grid表头中的全选复选框的选中状态不是很准确,就写了这个小扩展 在js中加入下面方法,在需要取消全选的地方调用即可,例:Ext.getCmp('gri ...

  4. [原创]纯JS实现网页中多选复选框checkbox和单选radio的美化效果

    图片素材: 最终效果图: <html><title> 纯JS实现网页中多选复选框checkbox和单选radio的美化效果</title><head>& ...

  5. java freemarker导出word时添加或勾选复选框

    最近项目导出word碰到一个需求,要求根据数据动态的决定word里的复选框是否勾选, 公司导出word用的是freemarker,相比较其他技术,freemarker可以很容易的控制输出样式, 在wo ...

  6. JS-001-单选复选按钮操作

    此文主要针对 web 页面中常见元素(例如:单选按钮.复选按钮)的 JavaScript 操作,进行简单的源码示例演示,敬请小主们参阅.若有不足之处,敬请大神指正,不胜感激! 话不多言了,直接上码: ...

  7. iOS开发-UITableView单选多选/复选实现1

    TableView怎样实现单选或者多选呢? 我们的直接思路是改动某一个Cell的样式就可以, 那么改动样式须要通过改动相应的数据, 从这里能够判断我们须要给Cell相应的数据设置一个标志位, 当选中的 ...

  8. Asp.net绑定带层次下拉框(select控件)

    1.效果图 2.数据库中表数据结构 3.前台页面 <select id="pid" runat="server" style="width:16 ...

  9. WPF进阶技巧和实战03-控件(3-文本控件及列表控件)

    系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...

随机推荐

  1. nodejs编写实例基础操作

    学习视频地址 https://cnodejs.org/topic/5a72c66ace45d440451465c3   初始化项目 首先查看项目中是否有package.json 文件,如果有可执行np ...

  2. Codeforces Gym101502 E.The Architect Omar-find()函数

    E. The Architect Omar   time limit per test 1.0 s memory limit per test 256 MB input standard input ...

  3. Codeforces 777E Hanoi Factory(线段树维护DP)

    题目链接 Hanoi Factory 很容易想到这是一个DAG模型,那么状态转移方程就出来了. 但是排序的时候有个小细节:b相同时看a的值. 因为按照惯例,堆塔的时候肯定是内半径大的在下面. 因为N有 ...

  4. BZOJ2870—最长道路tree

    最长道路tree Description H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样.每个路口都有很多车辆来往,所以每个路口i都 ...

  5. 2016集训测试赛(二十四)Problem B: Prz

    Solution 这道题有两个关键点: 如何找到以原串某一个位置为结尾的某个子序列的最晚出现位置 如何找到原串中某个位置之前的所有数字的最晚出现位置中的最大值 第一个关键点: 我们注意到每个数字在\( ...

  6. 2016北京集训测试赛(六)Problem A: 冒泡排序

    Solution 观察冒泡排序的过程. 我们注意到, 每一轮的排序都会使得每个数后面比它小的数的个数减\(1\). 我们用\(f(n, m)\)表示对\(1\)到\(n\)的一个排列进行冒泡排序, 满 ...

  7. 【转】.net 经典面试题

    [转].net 经典面试题  1. 简述 private. protected. public. internal 修饰符的访问权限. 答 . private : 私有成员, 在类的内部才可以访问.  ...

  8. 漫谈程序员系列:3D打印能打印出程序员吗

    首先声明,本文是一本正经的胡扯,绝不是随随便便的胡扯,请您不要随便攻击我胡说八道.我要反复星爷在<喜剧之王>里的台词:事实上.我是一本正经的喷子. 3D打印的定义 关于3D打印,以下是来自 ...

  9. Java中对象、对象引用、堆、栈、值传递以及引用传递的详解

    Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...

  10. RR调度(Round-robin scheduling)简单介绍

    在RR调度策略下,一个线程会一直运行.直到: 自愿放弃控制权 被更高优先级的线程抢占 时间片用完 例如以下图所看到的,A在用完自己的时间片后,将CPU运行权让给线程B.于是A离开Read队列,而B进入 ...