Imports Infragistics.Win Imports Infragistics.Win.UltraWinGrid Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call End Sub 'Form overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer 'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. Friend WithEvents UltraGrid1 As Infragistics.Win.UltraWinGrid.UltraGrid Private Sub InitializeComponent() Me.UltraGrid1 = New Infragistics.Win.UltraWinGrid.UltraGrid CType(Me.UltraGrid1, System.ComponentModel.ISupportInitialize).BeginInit() Me.SuspendLayout() ' 'UltraGrid1 ' Me.UltraGrid1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _ Or System.Windows.Forms.AnchorStyles.Left) _ Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) Me.UltraGrid1.DisplayLayout.ViewStyleBand = Infragistics.Win.UltraWinGrid.ViewStyleBand.OutlookGroupBy Me.UltraGrid1.Location = New System.Drawing.Point(8, 8) Me.UltraGrid1.Name = "UltraGrid1" Me.UltraGrid1.Size = New System.Drawing.Size(560, 288) Me.UltraGrid1.TabIndex = 5 Me.UltraGrid1.Text = "UltraGrid1" ' 'Form1 ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(576, 309) Me.Controls.Add(Me.UltraGrid1) Me.Name = "Form1" Me.Text = "UltraWinGrid CheckBox on Header" CType(Me.UltraGrid1, System.ComponentModel.ISupportInitialize).EndInit() Me.ResumeLayout(False) End Sub #End Region #Region " Data Creation " Shared rnd As New Random() Private Function GetData(Optional ByVal Rows As Integer = 10, Optional ByVal Strings As Integer = 2, Optional ByVal Ints As Integer = 2, Optional ByVal Doubles As Integer = 2, Optional ByVal Dates As Integer = 2, Optional ByVal Bools As Integer = 2, Optional ByVal Enums As Integer = 2) As DataTable Dim ReturnValue As New DataTable Dim i, j As Integer With ReturnValue With .Columns() With .Add("Key", GetType(Integer)) .AutoIncrement = True .ReadOnly = True End With For i = 1 To Strings .Add("String " & i, GetType(String)) Next For i = 1 To Ints .Add("Integer " & i, GetType(Integer)) Next For i = 1 To Doubles .Add("Double " & i, GetType(Double)) Next For i = 1 To Dates .Add("Date " & i, GetType(DateTime)) Next For i = 1 To Bools .Add("Boolean " & i, GetType(Boolean)) Next For i = 1 To Enums .Add("Enum " & i, GetType(Elements)) Next End With Dim DR As DataRow For j = 1 To Rows DR = .NewRow For i = 1 To Strings If i = 1 Then DR("String " & i) = New String(Chr(65 + ((j - 1) Mod 26)), 3) Else DR("String " & i) = RandomString() End If Next For i = 1 To Ints If i = 1 Then DR("Integer " & i) = RandomInteger(0, 100) Else DR("Integer " & i) = RandomInteger() End If Next For i = 1 To Doubles DR("Double " & i) = RandomDouble() Next For i = 1 To Dates If i = 1 Then DR("Date " & i) = System.DBNull.Value Else DR("Date " & i) = RandomDate() 'System.DBNull.Value End If Next For i = 1 To Bools DR("Boolean " & i) = RandomBool() Next For i = 1 To Enums DR("Enum " & i) = RandomEnum() Next .Rows.Add(DR) Next End With ReturnValue.AcceptChanges() Return ReturnValue End Function Private Function RandomString(Optional ByVal NumberOfCharacters As Integer = 0) As String If NumberOfCharacters < 1 Then NumberOfCharacters = rnd.Next(1, 51) End If Dim i As Integer Dim SB As New System.Text.StringBuilder For i = 1 To NumberOfCharacters SB.Append(RandomCharacter) Next Return SB.ToString End Function Private Function RandomCharacter() As Char Dim ReturnValue As Char ReturnValue = Chr(rnd.Next(65, 91)) If RandomBool() Then ReturnValue = Char.ToLower(ReturnValue) End If Return ReturnValue End Function Private Function RandomBool() As Boolean Return CBool(rnd.Next(-1, 1)) End Function Private Function RandomInteger(Optional ByVal Min As Integer = -100, Optional ByVal Max As Integer = 100) As Integer Return rnd.Next(Min, Max + 1) End Function Private Function RandomDouble() As Double Return rnd.NextDouble * 100 End Function Private Function RandomDate() As DateTime Dim ReturnValue As DateTime = DateTime.Today ReturnValue = ReturnValue.AddYears(RandomInteger) ReturnValue = ReturnValue.AddMonths(RandomInteger) ReturnValue = ReturnValue.AddDays(RandomInteger) ReturnValue = ReturnValue.AddHours(RandomInteger) ReturnValue = ReturnValue.AddSeconds(RandomInteger) ReturnValue = ReturnValue.AddMinutes(RandomInteger) Return ReturnValue End Function Private Function RandomEnum() As Elements Return CType(rnd.Next(0, System.Enum.GetValues(GetType(Elements)).Length), Elements) End Function #End Region Dim dt, dt2 As DataTable ' Create an instance of the CreationFilter. This needs to be Form-Level so ' we can catch the event that fires when the CheckBox is clicked. Dim WithEvents aCheckBoxOnHeader_CreationFilter As New CheckBoxOnHeader_CreationFilter Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim ds As DataSet = New DataSet dt = GetData(100) dt2 = GetData(100) dt2.TableName = "Child" ds.Tables.Add(dt) ds.Tables.Add(dt2) ds.Relations.Add(dt.Columns(0), dt2.Columns(0)) With Me.UltraGrid1 .DataSource = ds .CreationFilter = aCheckBoxOnHeader_CreationFilter End With End Sub ' This event on the CreationFilter fires when the CheckBox in a Header is clicked. Private Sub aCheckBoxOnHeader_CreationFilter_HeaderCheckBoxClicked(ByVal sender As Object, ByVal e As CheckBoxOnHeader_CreationFilter.HeaderCheckBoxEventArgs) Handles aCheckBoxOnHeader_CreationFilter.HeaderCheckBoxClicked ' Check to see if the column is of type boolean. If it is, set all the cells in that column to ' whatever value the header checkbox is. Dim aRow As UltraGridRow Dim level As Integer = e.Header.Column.Level If e.Header.Column.DataType Is GetType(Boolean) Then For Each aRow In e.Rows aRow.Cells(e.Header.Column.Index).Value = (e.CheckState.Equals(CheckState.Checked)) Next End If End Sub End Class ' This CreationFilter class will create a CheckBoxUIElement in each Header ' in the grid that has a DataType of Boolean. It will fire the ' HeaderCheckBoxClicked event whenever the CheckBox is clicked. ' Note that in order to maintain the CheckState, this CreationFilter uses the ' Tag proprty of the Header. So if the program uses the tag for something else ' this will not work. Public Class CheckBoxOnHeader_CreationFilter ' Implements the CreationFilter interface Implements IUIElementCreationFilter ' This event will fire when the CheckBox is clicked. Public Event HeaderCheckBoxClicked(ByVal sender As Object, ByVal e As HeaderCheckBoxEventArgs) Public Sub AfterCreateChildElements(ByVal parent As Infragistics.Win.UIElement) Implements Infragistics.Win.IUIElementCreationFilter.AfterCreateChildElements ' Check for the HeaderUIElement If TypeOf parent Is HeaderUIElement Then ' Get the actual ColumnHeader that the HeaderUIElement is attached to Dim aColHeader As ColumnHeader aColHeader = CType(parent, HeaderUIElement).Header ' Only put the Checkbox in the header of the Boolean columns If aColHeader.Column.DataType Is GetType(Boolean) Then Dim aTextUIElement As TextUIElement Dim aCheckBoxUIElement As CheckBoxUIElement ' Since the grid sometimes re-uses UIElements, we need to check to make sure ' the header does not already have a CheckBoxUIElement attached to it. ' If it does, we just get a reference to the existing CheckBoxUIElement, ' and reset its properties. aCheckBoxUIElement = parent.GetDescendant(GetType(CheckBoxUIElement)) If aCheckBoxUIElement Is Nothing Then ' Create a New CheckBoxUIElement aCheckBoxUIElement = New CheckBoxUIElement(parent) End If ' Get the TextUIElement - this is where the text for the ' Header is displayed. We need this so we can push it to the right ' in order to make room for the CheckBox aTextUIElement = CType(parent.GetDescendant(GetType(TextUIElement)), TextUIElement) ' Sanity check If aTextUIElement Is Nothing Then Exit Sub ' Get the Header and see if the Tag has been set. I the Tag is ' set, we will assume it's the stored CheckState. This has to be ' done in order to maintain the CheckState when the grid repaints and ' UIElement are destroyed and recreated. Dim aHeader As ColumnHeader = CType(aCheckBoxUIElement.GetAncestor(GetType(HeaderUIElement)).GetContext(GetType(ColumnHeader)), ColumnHeader) If aHeader.Tag Is Nothing Then ' If the tag was nothing, this is probably the first time this ' HeaderRow is being displayed, so default to Indeterminate aHeader.Tag = CheckState.Indeterminate Else aCheckBoxUIElement.CheckState = CType(aHeader.Tag, CheckState) End If ' Hook the ElementClick of the CheckBoxUIElement AddHandler aCheckBoxUIElement.ElementClick, AddressOf aCheckBoxUIElement_ElementClick ' Add the CheckBoxUIElement to the HeaderUIElement parent.ChildElements.Add(aCheckBoxUIElement) ' Position the CheckBoxUIElement. The number 3 here is used for 3 ' pixels of padding between the CheckBox and the side of the header ' The CheckBox is shifted down slightly so it is centered in the header aCheckBoxUIElement.Rect = New Rectangle(parent.Rect.X + 3, parent.Rect.Y + ((parent.Rect.Height - aCheckBoxUIElement.CheckSize.Height) / 2), aCheckBoxUIElement.CheckSize.Width, aCheckBoxUIElement.CheckSize.Height) ' Push the TextUIElement to the right a little to make ' room for the CheckBox. 3 pixels of padding are used again. aTextUIElement.Rect = New Rectangle(aCheckBoxUIElement.Rect.Right + 3, aTextUIElement.Rect.Y, parent.Rect.Width - (aCheckBoxUIElement.Rect.Right - parent.Rect.X), aTextUIElement.Rect.Height) Else ' If the column is not a boolean column, we do not want to have a checkbox in it ' Since UIElements can be reused by the grid, there is a chance that one of the ' HeaderUIElements that we added a checkbox to for a boolean column header ' will be reused in a column that is not boolean. In this case, we must remove ' the checkbox so that it will not appear in an inappropriate column header. Dim aCheckBoxUIElement As CheckBoxUIElement aCheckBoxUIElement = parent.GetDescendant(GetType(CheckBoxUIElement)) If Not aCheckBoxUIElement Is Nothing Then parent.ChildElements.Remove(aCheckBoxUIElement) aCheckBoxUIElement.Dispose() End If End If End If End Sub Public Function BeforeCreateChildElements(ByVal parent As Infragistics.Win.UIElement) As Boolean Implements Infragistics.Win.IUIElementCreationFilter.BeforeCreateChildElements ' Don't need to do anything here. Return False End Function Private Sub aCheckBoxUIElement_ElementClick(ByVal sender As Object, ByVal e As Infragistics.Win.UIElementEventArgs) ' Get the CheckBoxUIElement that was clicked Dim aCheckBoxUIElement As CheckBoxUIElement = CType(e.Element, CheckBoxUIElement) ' Get the Header associated with this particular element Dim aHeaderUIElement As HeaderUIElement = CType(aCheckBoxUIElement.GetAncestor(GetType(HeaderUIElement)), HeaderUIElement) Dim aHeader As ColumnHeader = CType(aHeaderUIElement.GetContext(GetType(ColumnHeader)), ColumnHeader) ' Set the Tag on the Header to the new CheckState aHeader.Tag = aCheckBoxUIElement.CheckState ' So that we can apply various changes only to the relevant Rows collection that the header belongs to Dim hRows As RowsCollection = CType(aHeaderUIElement.GetContext(GetType(RowsCollection)), RowsCollection) ' Raise an event so the programmer can do something when the CheckState changes RaiseEvent HeaderCheckBoxClicked(Me, New HeaderCheckBoxEventArgs(aHeader, aCheckBoxUIElement.CheckState, hRows)) End Sub ' EventArgs used for the HeaderCheckBoxClicked event. This event has to pass in the CheckState and the Header Public Class HeaderCheckBoxEventArgs Inherits EventArgs Public Sub New(ByVal Header As ColumnHeader, ByVal CheckState As CheckState, ByRef Rows As RowsCollection) mvarHeader = Header mvarCheckState = CheckState mvarRowsCollection = Rows End Sub Private mvarRowsCollection As RowsCollection Private mvarHeader As ColumnHeader Private mvarCheckState As CheckState ' Expose the rows collection for the specific row island that the header belongs to Public ReadOnly Property Rows() As RowsCollection Get Return mvarRowsCollection End Get End Property Public ReadOnly Property Header() As ColumnHeader Get Return mvarHeader End Get End Property Public Property CheckState() As CheckState Get Return mvarCheckState End Get Set(ByVal Value As CheckState) mvarCheckState = Value End Set End Property End Class End Class Enum Elements Air Earth Fire Water Energy End Enum