22
技術社區[雲棲]
擴展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和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實現的。
對現有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: }
從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 官方提供的解決方案中,有興趣的朋友可以從
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-30 11:04:19