扩展UltraGrid控件实现对所有数据行的全选功能[Source Code下载]

前面一篇文章中,我通过对三种Infragistics

控件(UltraToolBarManager、UltraGird和UltraListView)进行扩展,以实现对ToolTip样式的定义,今天我来介绍如何采用相同的方式实现另外一个更为常用的功能:在UltraGrid的Header中动态添加CheckBox,从而实现对所有数据行进行全选的功能。[Source
Code从这里下载]

一、我们的目标:在UltraGird的选择列的Header添加CheckBox实现对所有数据行的全选 

我们现有的绝大部分UltraGird都具有如下图(点击查看大图)所示的结构:第一行为UnBound列,单元格中的CheckBox用于对当前行的选择,即通过勾选相应的CheckBox代表选中某一行。现在的新的要求是:在CheckBox列的列头添加一个总的CheckBox,用于选中所有数据行,即当勾选CheckBox时,下面所有数据行对应的均自动被勾选,反之,解除现有数据行对应的CheckBox的勾选状态。

熟悉Infragistics控件的朋友应该知道,UltraGird具有一个很有用的动态分局的特性:你可以将可被用于分组的列通过鼠标拖到最上方的区域(Drag
a column header here to group by the
column),那么UltraGird会自动为你将所有的数据行按照该列的值进行动态分组。这个分组功能为我们要扩展的UltraGird又增加了一个新的特性:如果在分组状态,需要在每一个分组行中添加CheckBox,该CheckBox用于对当前组范围内所有数据行的全选。最后的效果如右图(点击查看大图)所示。

二、关于UIElement和UIElementCreationFilter

在具体介绍实现的之前,我们先来谈谈相关的一些背景知识:关于UIElement和UIElementFilter。Infragistics
基于Windows Forms应用的控件具有非常高的扩展型。通过合理使用UIElement,开发者可以很容易地添加一些现有控件没有提供的功能。

基本上所有的Infragistics
控件(这里我们仅仅指基于Window Forms应用控件)都有一个具有层级关系的UIElement组成。比如,一个UltraTree
由一个UltraTreeUIElement构成,而一个UltraTreeUIElement又包含一组TreeNodeUIElement
对象和NodeConnectorUIElement对象。而TreeNodeUIElements又由一组PreNodeAreaUIElement
和NodeSelectableAreaUIElement组成。对于NodeSelectableAreaUIElements,其组成元素又包含两种:ExpansionIndicatorUIElements
和NodeTexUIElements。

所有的UIElement相关的操作,比如改变其显示风格和位置,可以通过两个Filter对象控制,即CreationFilter和DrawFilter。而CreationFilter还能用于为现有控件动态的添加或者移除子控件,我们将要实现的对于CheckBox的动态添加就是通过自定义CreationFilter实现的。

三、自定义UIElementCreationFilter实现对CheckBox的动态添加

对现有UltraGrid的扩展的核心在于自定义UIElementCreationFilter实现对CheckBox的动态添加,在具体介绍如何自定义UIElementCreationFilter之前,我们先看看我们扩展出来的UltraGrid的定义。从下面的代码片段可以看出,扩展控件ExtendedUltraGrid的定义其实很简单。其中,SelectAllColumnName表示CheckBox列的名称;而IsGroupMode属性表示当前是否处于分组模式;CheckState表示在未分组情况下Select-All
CheckBox应有的状态;在构造函数中,我们指定了UltraGrid的CreationFilter属性。

   1: using System.Windows.Forms;
   2: using Infragistics.Win.UltraWinGrid;
   3:  
   4: namespace Artech.ExtendedUltraGrid4SelectAll
   5: {
   6:     public class ExtendedUltraGrid : UltraGrid
   7:     {
   8:         public string SelectAllColumnName{get;set;}
   9:  
  10:         internal bool IsGroupMode
  11:         {
  12:             get
  13:             {
  14:                 foreach (var row in this.Rows)
  15:                 {
  16:                     if (row.IsGroupByRow)
  17:                     {
  18:                         return true;
  19:                     }
  20:                 }
  21:                 return false;
  22:             }
  23:         }
  24:  
  25:         internal CheckState CheckState{ get; set; }
  26:  
  27:         public ExtendedUltraGrid()
  28:         {
  29:             this.CreationFilter = new CheckableHeaderCreationFilter(this);
  30:         }       
  31:     }
  32: }

在构造函数中指定的Creation属性就是我们上面介绍的自定义的UIElementCreationFilter:CheckableHeaderCreationFilter。所有的UIElementCreationFilter均实现接口IUIElementCreationFilter,该接口具有两个方法BeforeCreateChildElements和AfterCreateChildElements。

   1: using Infragistics.Win;
   2: public class MyUltraExplorerBarCreationFilter : IUIElementCreationFilter
   3: {
   4:     public void AfterCreateChildElements(UIElement parent)
   5:     {
   6:         // called for a UIElement (parent) after it creates its child elements.
   7:     }
   8:     public bool BeforeCreateChildElements(UIElement parent)
   9:     {
  10:         // called for a UIElement (parent) before it creates its child elements.
  11:         return false;
  12:     }
  13: }

在CheckableHeaderCreationFilter中,我们将在Select列的列头添加CheckBox的操作实现在AfterCreateChildElements方法中。其主要的逻辑是:通过parent的类型(必须是HeaderUIElement)、Column的类型(比如是Boolean)和Column

Name(必须是在ExtendedUltraGrid的SelectAllColumnName属性指定)确定parent正是我们需要位置添加Check子控件的UIElement。然后创建CheckBoxUIElement,并将其添加到parent中,并通过Rect属性确定其显示的位置。然后我们会根据分组行(UltraGridGroupByRow)的Tag(这个会在自定义CheckBoxUIElement中设置)设置新创建的CheckBoxUIElement的CheckState状态,如果没有在分组模式下,我们根据ExtendedUltraGrid的CheckState属性指定该CheckBoxUIElement的状态。

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Drawing;
   4: using System.Windows.Forms;
   5: using Infragistics.Win;
   6: using Infragistics.Win.UltraWinGrid;
   7:  
   8: namespace Artech.ExtendedUltraGrid4SelectAll
   9: {
  10:     public class CheckableHeaderCreationFilter : IUIElementCreationFilter
  11:     {
  12:         public ExtendedUltraGrid Control
  13:         { get; private set; }
  14:  
  15:         public CheckableHeaderCreationFilter( ExtendedUltraGrid control)
  16:         {          
  17:             if (null == control)
  18:             {
  19:                 throw new ArgumentNullException("control");
  20:             }
  21:             this.Control = control;
  22:         }
  23:  
  24:         public void AfterCreateChildElements(UIElement parent)
  25:         {
  26:             //Filter the HeaderUIElement element.
  27:             HeaderUIElement headerUIElement = parent as HeaderUIElement;
  28:             if (null == headerUIElement)
  29:             {
  30:                 return;
  31:             }
  32:  
  33:             //Filter by DataType and SelectAll column name.
  34:             if (headerUIElement.Header.Column.DataType != typeof(bool) || headerUIElement.Header.Column.Key != this.Control.SelectAllColumnName)
  35:             {
  36:                 return;
  37:             }
  38:  
  39:             //Check the exitence of CheckBoxUIElement.
  40:             //If not, create a new one and wrap it with a WeakReference.
  41:             CheckBoxUIElement checkBoxUIElement = parent.GetDescendant(typeof(CheckBoxUIElement)) as CheckBoxUIElement;
  42:             if (null == checkBoxUIElement)
  43:             {
  44:                 checkBoxUIElement = new ExtendedCheckBoxUIElement(parent);              
  45:             }
  46:             //Add the CheckBoxUIElement and set its position.
  47:             parent.ChildElements.Add(checkBoxUIElement);
  48:             checkBoxUIElement.Rect = new Rectangle(
  49:                         parent.Rect.X + (parent.Rect.Width - checkBoxUIElement.CheckSize.Width) / 2 + 1,
  50:                         parent.Rect.Y + ((parent.Rect.Height - checkBoxUIElement.CheckSize.Height) / 2),
  51:                         checkBoxUIElement.CheckSize.Width,
  52:                         checkBoxUIElement.CheckSize.Height
  53:                         );           
  54:             //For GroupRow, set the Tag as the current CheckState.
  55:             UltraGridRow ultraGridRow = headerUIElement.GetContext(typeof(UltraGridRow)) as UltraGridRow;
  56:             UltraGridGroupByRow parentRow = ultraGridRow.ParentRow as UltraGridGroupByRow;
  57:             if (null != parentRow && null != parentRow.Tag)
  58:             {
  59:                 checkBoxUIElement.CheckState = (CheckState)parentRow.Tag;
  60:             }
  61:  
  62:             if (!this.Control.IsGroupMode)
  63:             {
  64:                 checkBoxUIElement.CheckState = this.Control.CheckState;
  65:             }
  66:         }
  67:  
  68:         public bool BeforeCreateChildElements(UIElement parent)
  69:         {
  70:             return false;
  71:         }
  72:     }
  73: }

四、自定义CheckBoxUIElement

从CheckableHeaderCreationFilter的定义我们可以看到:动态添加的CheckBoxUIElement的类型为ExtendedCheckBoxUIElement,这是我们自定义的类型。我们通过该类型来设置分组行或者整个UltraGrid(没有在分组模式下)应有的状态,并最终对相应的数据行(在分组模式下为当前分组的所有行,而没有分组情况下为整个UltraGrid的所有行)的Check状态。所有的实现具有体现在重写的OnCheckStateChange上面。

   1: using System.Windows.Forms;
   2: using Infragistics.Win;
   3: using Infragistics.Win.UltraWinGrid;
   4:  
   5: namespace Artech.ExtendedUltraGrid4SelectAll
   6: {
   7:     public class ExtendedCheckBoxUIElement : CheckBoxUIElement
   8:     { 
   9:         public ExtendedCheckBoxUIElement(UIElement parent)
  10:             : base(parent)
  11:         {
  12:             this.ThreeState = false;
  13:         }
  14:  
  15:         public override CheckState CheckState
  16:         {
  17:             get
  18:             {
  19:                 return base.CheckState;
  20:             }
  21:             set
  22:             {
  23:                 CheckState checkState = this.CheckState;
  24:                 base.CheckState = value;
  25:                 if (checkState != value)
  26:                 {
  27:                     this.OnCheckStateChange();
  28:                 }
  29:             }
  30:         }
  31:  
  32:         protected override void OnCheckStateChange()
  33:         {
  34:             base.OnCheckStateChange();
  35:             ExtendedUltraGrid ultraGrid = this.Control as ExtendedUltraGrid;
  36:             HeaderUIElement headerUIElement = this.GetAncestor(typeof(HeaderUIElement))as HeaderUIElement;
  37:             UltraGridRow ultraGridRow = headerUIElement.GetContext(typeof(UltraGridRow))as UltraGridRow;
  38:             UltraGridGroupByRow parentRow = ultraGridRow.ParentRow as UltraGridGroupByRow;
  39:             if (null != parentRow)
  40:             {
  41:                 parentRow.Tag = this.CheckState;
  42:                 this.SetAllGridRowSelected(parentRow.Rows, this.CheckState);
  43:             }
  44:             else
  45:             {
  46:                 this.SetAllGridRowSelected((this.Control as UltraGrid).Rows, this.CheckState);
  47:             }
  48:  
  49:             if (!ultraGrid.IsGroupMode)
  50:             {
  51:                 ultraGrid.CheckState = this.CheckState;
  52:             }
  53:         }
  54:  
  55:         private void SetAllGridRowSelected(RowsCollection rows, CheckState state)
  56:         {
  57:             foreach (var row in rows)
  58:             {
  59:                 if (row.IsGroupByRow)
  60:                 {
  61:                     row.Tag = this.CheckState;
  62:                     SetAllGridRowSelected(((UltraGridGroupByRow)row).Rows, state);
  63:                 }
  64:                 else
  65:                 {
  66:                     string selectAllColumnName = (this.Control as ExtendedUltraGrid).SelectAllColumnName;
  67:                     if (row.Activation == Activation.Disabled) continue;
  68:                     if (!row.Band.Columns.Exists(selectAllColumnName)) continue;
  69:                     if (row.Band.Columns[selectAllColumnName].CellActivation == Activation.Disabled) continue;
  70:                     if (null == row.Cells) continue;
  71:                     row.Cells[selectAllColumnName].Value = state;
  72:                     row.Update();
  73:                 }
  74:             }
  75:         }
  76:     }
  77: }

P.S.
上面的例子只是提供了一个解决问题的思路,还有一些细节问题。比如,当我应用了一些风格文件之后,发现每当鼠标移至UltraGrid的Select列的列头时,CheckableHeaderCreationFilter的AfterCreateChildElements方法会被调用,并且总是会导致新的CheckBoxUIElement被创建。但是一旦我将不采用风格文件,就不是出现这样的问题。这个问题也同样出现在Infragistics
官方提供的解决方案中,有兴趣的朋友可以从这里下载,并通过StyleManager.Load方法加载某个风格文件,通过Debug你会发现CheckBoxUIElement被频繁创建。Infragistics

提供的例子和我对UltraGrid的扩展方式,本质上是一致的,虽有被创建出来的CheckBoxUIElement会成为垃圾对象,可以被垃圾回收,但是频繁的创建这样的对象总归会对内存造成一定的压力。

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-06 07:37:00

扩展UltraGrid控件实现对所有数据行的全选功能[Source Code下载]的相关文章

如何于DataGridView控件中以跨数据行方式显示数据

datagrid|控件|数据|显示 一般来说,每一个字段的内容会单独显示于DataGridView控件的一个数据行中.问题是,某些字段拥有大量文字数据,我是不是能够让该字段的内容以跨数据行的方式来显示,以便在有限的画面空间中的呈现出更完整的内容呢?答案当然是肯定的. 以图表1所示的执行画面而言,「自传」字段的内容并未单独显示于一个数据行中,而是以横跨数据行的方式,显示在同笔数据列之各字段内容的下方.相关程序代码列示如下: using System;using System.Collections

WPF中TextBox控件对于鼠标单击获取焦点后的全选

程序代码  代码如下 复制代码 void OnLostFocus(object sender, RoutedEventArgs e)          {              TextBox tb = e.Source as TextBox;              tb.PreviewMouseDown += new MouseButtonEventHandler(OnPreviewMouseDown);          }            void OnPreviewMous

转贴自MS:扩展 TreeView 控件 (1)

Windows 窗体控件开发示例 Duncan MackenzieMicrosoft Developer Network 2002 年 5 月 摘要:讲述了如何向 TreeView 控件添加数据绑定功能,它是一系列 Microsoft Windows 控件开发示例之一.您可以将本文与相关的概述文章结合起来阅读.您可以从 MSDN Code Center 下载 WinFormControls.exe(英文)源代码.(请注意,在示例文件中,程序员的注释使用的是英文,本文中将其译为中文是为了便于读者理

怎么给控件添加二次扩展的控件事件

问题描述 怎么给控件添加二次扩展的控件事件 在VB6.0语言中,控件缺少一些事件,比如菜单条缺少mousemove事件,而toolbar也缺少mouseenter控件,怎么扩展这些事件呢? 解决方案 可以用setwindowlong来实现子类化,拦截对应的 windows 消息来处理. 解决方案二: 给用户控件添加事件Android为自定义控件添加事件用代码给控件添加事件

轮循法-C++MSComm控件的定时接收数据怎么实现

问题描述 C++MSComm控件的定时接收数据怎么实现 查了好多资料都是VB的,我想定时的从输入缓冲区中读数,C++如何实现啊,新手求教! 解决方案 我用settimer这个来实现定时,可能是由于ONCOMM事件在缓冲区有数时候就会触发,两者发生冲突阻塞了,有人说还可以多线程实现定时,但是我是新手,有没有哪位前辈遇到这种问题,传授一二?

datepicker-silverlight的DataGrid中添加DatePicker控件使用滚动条滚动数据有误

问题描述 silverlight的DataGrid中添加DatePicker控件使用滚动条滚动数据有误 silverlight的DataGrid中添加DatePicker控件,初始设定DatePicker中的时间,连续使用滚动条滚动,初始设定DatePicker的值会随机改变. <sdk:DataGrid Grid.Row="1" HorizontalAlignment="Stretch" Name="dataGrid1" Vertical

c#-C#使用UltraGrid控件,如何实现用鼠标拖拽来选中多个单元格

问题描述 C#使用UltraGrid控件,如何实现用鼠标拖拽来选中多个单元格 C# 使用UltraGrid这个控件,属性中SelectTypeCell也设置了为ExtendedAutoDrag,但是没办法实现鼠标托拽选中多个单元格,只能选中某个单元格后,按住Shift键,再选中需要选中的多个单元格的最后一个,才能选中多个单元格,请问怎么实现直接用鼠标拖拽就可选中多个单元格![图片说明](http://img.ask.csdn.net/upload/201505/22/1432263419_684

有没有什么好的JavaScript地图控件可以实现根据数据量变色、链接等,另外需要修改脚本一则

问题描述 有没有什么好的JavaScript地图控件可以实现根据数据量变色.链接等,另外需要修改脚本一则 <script type="text/javascript"> $(function(){ var data = {"jiangsu":{"value":"<{$count5}>","stateInitColor":"4"},"heilongjian

access数据库-C#treeview控件读取ACCESS数据库数据并显示

问题描述 C#treeview控件读取ACCESS数据库数据并显示 C#中treeview控件读取ACCESS数据库数据并显示,急求急求急求急求急求急求 解决方案 支持任意数据库http://blog.csdn.net/xianfajushi/article/details/7756584 解决方案二: http://wenku.baidu.com/link?url=wmhiqcblgkcgVwQw9-mvCuAD0WG3AA8k87fHuHb0j3wPUTz41huk-TGUbmCagBZhd