RadGrid Hierarchy & CheckBoxColumn 3 states

10 posts, 1 answers
  1. Filleau
    Filleau avatar
    114 posts
    Member since:
    Jun 2006

    Posted 07 Nov 2010 Link to this post

    Hi,

    I'm working with 2010Q2 RadForm in VB.NET 2010

    I had trouble achieving what I want with RadGridView (as something like a treeview)

    I have one table with
    > DadName (as string)
    > ChildName (as string)
    > ChildAge (as integer)

    I would like to have this table in a RadGridView with hierarchy (Dad/Clildren) with a 3 state checkbox Column in order to let users select all/none/some chidren.

    Master Template must have 3 columns : CheckBox, DadName, Clidren Count
    Child Remplare must have 3 columns : CheckBox, ChildName, ChildAge

    - When radgrid is shown for the first time, none off Child Template are displayed
    - If DadName have only one Child, I don't whant to display the children template (plus sign) and the checkbox must be checked or unchecked
    - if DadName have more than one chlid, user can :
        - Check from Dad Row in order to select or unselect all children
        - Display the Child template hierarchy in order to select some children (In this case the row dad parent checkbox must be on the third state if some clidren are selected, unchecked il none are selected, and checked if all are selected)

    Ultimately, I need to retreive all chidren selected for all fathers in a loop.

    Thanks for your help

    Anthony
  2. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 07 Nov 2010 Link to this post

    Hello Anthony,

    Will an example with business objects as data source be ok in your case? Please let me know so that i can prepare something for this.

    Best Regards,
    Emanuel Varga
  3. UI for WinForms is Visual Studio 2017 Ready
  4. Filleau
    Filleau avatar
    114 posts
    Member since:
    Jun 2006

    Posted 07 Nov 2010 Link to this post

    Hello Emanuel

    I'm sorry but I not sure to understand what do you mean with "Buisiness Objects"

    If my english is good, I think you want to do an example with business data rather than daddy and child.
    For me it is not a problem (I wrote my question with daddy and children in order to be simple, but my project is not like this of course)

    We can imagine the same thing in others cases. Like "send a gift" to some vendors's clients, or like a folder explorer.

    Thanks

    Anthony

  5. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 07 Nov 2010 Link to this post

    Hello again,

    I have prepared something, but it is in C#, can you test it like this? and if everything is ok, i will help you convert it to VB, please let me know.

    Best Regards,
    Emanuel Varga
  6. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 07 Nov 2010 Link to this post

    C# version:
    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    using Telerik.WinControls.UI;
     
    public partial class Form1 : Form
    {
        private RadGridView radGridView1;
     
        public Form1()
        {
            InitializeComponent();
            this.Controls.Add(radGridView1 = new RadGridView());
     
            radGridView1.Dock = DockStyle.Fill;
            radGridView1.DataBindingComplete += new GridViewBindingCompleteEventHandler(radGridView1_DataBindingComplete);
            radGridView1.CellValueChanged += new GridViewCellEventHandler(radGridView1_CellValueChanged);
            radGridView1.ValueChanged += new EventHandler(radGridView1_ValueChanged);
            radGridView1.ViewCellFormatting += new CellFormattingEventHandler(radGridView1_ViewCellFormatting);
            radGridView1.ChildViewExpanding += new ChildViewExpandingEventHandler(radGridView1_ChildViewExpanding);
        }
     
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
     
            this.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
            this.radGridView1.DataSource = new ParentsCollection(3);
     
            GridViewTemplate childTemplate = new GridViewTemplate();
            childTemplate.DataSource = new ChildrenCollection();
     
            GridViewRelation childrenRelation = new GridViewRelation(this.radGridView1.MasterTemplate);
            childrenRelation.ChildTemplate = childTemplate;
            childrenRelation.RelationName = "ParentChild";
            childrenRelation.ParentColumnNames.Add("DadName");
            childrenRelation.ChildColumnNames.Add("DadName");
            this.radGridView1.Relations.Add(childrenRelation);
            childTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
            childTemplate.BestFitColumns();
            childTemplate.Caption = "Children";
     
            this.radGridView1.MasterTemplate.Templates.Add(childTemplate);
        }
     
        void radGridView1_ValueChanged(object sender, EventArgs e)
        {
            if (radGridView1.CurrentColumn is GridViewCheckBoxColumn)
            {
                radGridView1.EndEdit();
            }
        }
     
        void radGridView1_CellValueChanged(object sender, GridViewCellEventArgs e)
        {
            if (e.Column is GridViewCheckBoxColumn)
            {
                if (e.Value == null)
                {
                    return;
                }
     
                if (e.Row.Parent == null)
                {
                    foreach (var row in e.Row.ChildRows)
                    {
                        // use the row.Tag to perform a programatic change, if the tag is false the parent row will not be updated on child changes
                        row.Tag = false;
                        row.Cells["IsSelected"].Value = radGridView1.CurrentCell.Value;
                        row.Tag = true;
                    }
                }
                else if (e.Row.Tag == null || (bool)e.Row.Tag)
                {
                    var parentRow = e.Row.Parent as GridViewRowInfo;
                    if (parentRow != null)
                    {
                        parentRow.Cells["ChildrenSelected"].Value = GetCheckedValueForChildren(parentRow.ChildRows);
                    }
                }
            }
        }
     
        private object GetCheckedValueForChildren(GridViewChildRowCollection gridViewChildRowCollection)
        {
            bool? state = null;
            foreach (var row in gridViewChildRowCollection)
            {
                var selectedValue = (bool)row.Cells["IsSelected"].Value;
                if (state.HasValue && state.Value != selectedValue)
                {
                    state = null;
                    break;
                }
                else
                {
                    state = (bool)row.Cells["IsSelected"].Value;
                }
            }
     
            return state;
        }
     
        void radGridView1_DataBindingComplete(object sender, GridViewBindingCompleteEventArgs e)
        {
            var checkBoxColumn = radGridView1.Columns["ChildrenSelected"] as GridViewCheckBoxColumn;
            if (checkBoxColumn != null)
            {
                checkBoxColumn.ThreeState = true;
            }
        }
     
        private void radGridView1_ViewCellFormatting(object sender, CellFormattingEventArgs e)
        {
            GridGroupExpanderCellElement cell = e.CellElement as GridGroupExpanderCellElement;
            if (cell != null && e.CellElement.RowElement is GridDataRowElement)
            {
                if (!IsExpandable(cell.RowInfo))
                {
                    cell.Expander.Visibility = Telerik.WinControls.ElementVisibility.Hidden;
                }
                else
                {
                    cell.Expander.Visibility = Telerik.WinControls.ElementVisibility.Visible;
                }
            }
        }
     
        private void radGridView1_ChildViewExpanding(object sender, ChildViewExpandingEventArgs e)
        {
            e.Cancel = !IsExpandable(e.ParentRow);
        }
     
        private bool IsExpandable(GridViewRowInfo rowInfo)
        {
            if (rowInfo.ChildRows != null && rowInfo.ChildRows.Count > 0)
            {
                return true;
            }
     
            return false;
        }
    }
     
    #region Helpers
     
    public class Parent
    {
        public bool? ChildrenSelected { get; set; }
     
        public string DadName { get; set; }
     
        public Parent(string dadName)
        {
            this.DadName = dadName;
        }
    }
     
    public class Children
    {
        public bool IsSelected { get; set; }
     
        public string DadName { get; set; }
     
        public string ChildName { get; set; }
     
        public Children(string dadName, string childName)
        {
            this.ChildName = childName;
            this.DadName = dadName;
        }
    }
     
    public class ParentsCollection : BindingList<Parent>
    {
        public ParentsCollection(int noItems)
        {
            for (int i = 0; i < noItems; i++)
            {
                this.Add(new Parent("Dad" + i));
            }
        }
    }
     
    public class ChildrenCollection : BindingList<Children>
    {
        public ChildrenCollection()
        {
            this.Add(new Children("Dad1", "Child1"));
            this.Add(new Children("Dad2", "Child1"));
            this.Add(new Children("Dad2", "Child2"));
            this.Add(new Children("Dad2", "Child3"));
        }
    }
     
    #endregion Helpers
    the VB one is coming up...

    Best Regards,
    Emanuel Varga
  7. Filleau
    Filleau avatar
    114 posts
    Member since:
    Jun 2006

    Posted 07 Nov 2010 Link to this post

    If it is in c# I can use your online converter, but I can understand the mechanism even if it's written in c #.
    Your example will help me, but where is the link ?
  8. Filleau
    Filleau avatar
    114 posts
    Member since:
    Jun 2006

    Posted 07 Nov 2010 Link to this post

    Bigs Thanks Emanuel.
    I will study your sample.
    Best regards
  9. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 07 Nov 2010 Link to this post

    Hello again,

    Please try this, I've made some minor changes:
    Imports System
    Imports System.ComponentModel
    Imports System.Windows.Forms
    Imports Telerik.WinControls.UI
     
    Public Class Form1
        Private WithEvents RadGridView1 As RadGridView
     
        Public Sub New()
            InitializeComponent()
            Me.Controls.Add(InlineAssignHelper(RadGridView1, New RadGridView()))
            RadGridView1.Dock = DockStyle.Fill
     
        End Sub
     
        Protected Overrides Sub OnLoad(ByVal e As EventArgs)
            MyBase.OnLoad(e)
     
            Me.RadGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill
            Me.RadGridView1.DataSource = New ParentsCollection(3)
     
            Dim childTemplate As New GridViewTemplate()
            childTemplate.DataSource = New ChildrenCollection()
     
            Dim childrenRelation As New GridViewRelation(Me.RadGridView1.MasterTemplate)
            childrenRelation.ChildTemplate = childTemplate
            childrenRelation.RelationName = "ParentChild"
            childrenRelation.ParentColumnNames.Add("DadName")
            childrenRelation.ChildColumnNames.Add("DadName")
            Me.RadGridView1.Relations.Add(childrenRelation)
            childTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill
            childTemplate.BestFitColumns()
            childTemplate.Caption = "Children"
     
            Me.RadGridView1.MasterTemplate.Templates.Add(childTemplate)
        End Sub
     
        Private Sub radGridView1_ValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles RadGridView1.ValueChanged
            If TypeOf RadGridView1.CurrentColumn Is GridViewCheckBoxColumn Then
                RadGridView1.EndEdit()
            End If
        End Sub
     
        Private Sub radGridView1_CellValueChanged(ByVal sender As Object, ByVal e As GridViewCellEventArgs) Handles RadGridView1.CellValueChanged
            If TypeOf e.Column Is GridViewCheckBoxColumn Then
                If e.Value Is Nothing Then
                    Return
                End If
     
                If e.Row.Parent Is Nothing Then
                    For Each row As GridViewRowInfo In e.Row.ChildRows
                        ' use the row.Tag to perform a programatic change, if the tag is false the parent row will not be updated on child changes
                        row.Tag = False
                        row.Cells("IsSelected").Value = RadGridView1.CurrentCell.Value
                        row.Tag = True
                    Next
                ElseIf e.Row.Tag Is Nothing OrElse CBool(e.Row.Tag) Then
                    Dim parentRow = TryCast(e.Row.Parent, GridViewRowInfo)
                    If parentRow IsNot Nothing Then
                        parentRow.Cells("ChildrenSelected").Value = GetCheckedValueForChildren(parentRow.ChildRows)
                    End If
                End If
            End If
        End Sub
     
        Private Function GetCheckedValueForChildren(ByVal gridViewChildRowCollection As GridViewChildRowCollection) As Object
            Dim state As System.Nullable(Of Boolean) = Nothing
            For Each row As GridViewRowInfo In gridViewChildRowCollection
                If (state Is Nothing And row.Cells("IsSelected").Value Is Nothing) Then
                    Continue For
                End If
     
                Dim selectedValue = CBool(row.Cells("IsSelected").Value)
                If state.HasValue AndAlso state.Value <> selectedValue Then
                    Return Nothing
                    Exit For
                Else
                    state = CBool(row.Cells("IsSelected").Value)
                End If
            Next
     
            Return IIf(state is Nothing, False, state)
        End Function
     
        Private Sub radGridView1_DataBindingComplete(ByVal sender As Object, ByVal e As GridViewBindingCompleteEventArgs) Handles RadGridView1.DataBindingComplete
            Dim checkBoxColumn = TryCast(RadGridView1.Columns("ChildrenSelected"), GridViewCheckBoxColumn)
            If checkBoxColumn IsNot Nothing Then
                checkBoxColumn.ThreeState = True
            End If
     
     
            For Each row As GridViewRowInfo In RadGridView1.Rows
                row.Cells("ChildrenSelected").Value = GetCheckedValueForChildren(row.ChildRows)
            Next
     
        End Sub
     
        Private Sub radGridView1_ViewCellFormatting(ByVal sender As Object, ByVal e As CellFormattingEventArgs) Handles RadGridView1.ViewCellFormatting
            Dim cell As GridGroupExpanderCellElement = TryCast(e.CellElement, GridGroupExpanderCellElement)
            If cell IsNot Nothing AndAlso TypeOf e.CellElement.RowElement Is GridDataRowElement Then
                If Not IsExpandable(cell.RowInfo) Then
                    cell.Expander.Visibility = Telerik.WinControls.ElementVisibility.Hidden
                Else
                    cell.Expander.Visibility = Telerik.WinControls.ElementVisibility.Visible
                End If
            End If
        End Sub
     
        Private Sub radGridView1_ChildViewExpanding(ByVal sender As Object, ByVal e As ChildViewExpandingEventArgs) Handles RadGridView1.ChildViewExpanding
            e.Cancel = Not IsExpandable(e.ParentRow)
        End Sub
     
        Private Function IsExpandable(ByVal rowInfo As GridViewRowInfo) As Boolean
            If rowInfo.ChildRows IsNot Nothing AndAlso rowInfo.ChildRows.Count > 0 Then
                Return True
            End If
     
            Return False
        End Function
        Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
            target = value
            Return value
        End Function
     
    End Class
     
    #Region "Helpers"
     
    Public Class Parent
        Public Property ChildrenSelected() As System.Nullable(Of Boolean)
            Get
                Return m_ChildrenSelected
            End Get
            Set(ByVal value As System.Nullable(Of Boolean))
                m_ChildrenSelected = Value
            End Set
        End Property
        Private m_ChildrenSelected As System.Nullable(Of Boolean)
     
        Public Property DadName() As String
            Get
                Return m_DadName
            End Get
            Set(ByVal value As String)
                m_DadName = Value
            End Set
        End Property
        Private m_DadName As String
     
        Public Sub New(ByVal dadName As String)
            Me.DadName = dadName
        End Sub
    End Class
     
    Public Class Children
        Public Property IsSelected() As Boolean
            Get
                Return m_IsSelected
            End Get
            Set(ByVal value As Boolean)
                m_IsSelected = Value
            End Set
        End Property
        Private m_IsSelected As Boolean
     
        Public Property DadName() As String
            Get
                Return m_DadName
            End Get
            Set(ByVal value As String)
                m_DadName = Value
            End Set
        End Property
        Private m_DadName As String
     
        Public Property ChildName() As String
            Get
                Return m_ChildName
            End Get
            Set(ByVal value As String)
                m_ChildName = Value
            End Set
        End Property
        Private m_ChildName As String
     
        Public Sub New(ByVal dadName As String, ByVal childName As String)
            Me.ChildName = childName
            Me.DadName = dadName
        End Sub
    End Class
     
    Public Class ParentsCollection
        Inherits BindingList(Of Parent)
        Public Sub New(ByVal noItems As Integer)
            For i As Integer = 0 To noItems - 1
                Me.Add(New Parent("Dad" + i.ToString))
            Next
        End Sub
    End Class
     
    Public Class ChildrenCollection
        Inherits BindingList(Of Children)
        Public Sub New()
            Me.Add(New Children("Dad1", "Child1"))
            Me.Add(New Children("Dad2", "Child1"))
            Me.Add(New Children("Dad2", "Child2"))
            Me.Add(New Children("Dad2", "Child3"))
        End Sub
    End Class
     
    #End Region

    This should solve your problem, if you have any other questions or comments, please let me know,

    Best Regards,
    Emanuel Varga
  10. Filleau
    Filleau avatar
    114 posts
    Member since:
    Jun 2006

    Posted 07 Nov 2010 Link to this post

    Your sample is simply perfect !

    The last thing, is  I must finaly retreive all children selected.

    May I need to do a loop on each RadGridView1.Rows with checkbox column selected (all or partial) and for each of them another loop on the children rows ?
  11. Answer
    Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 07 Nov 2010 Link to this post

    Hello again,

    Yes, sadly you have to do that, or you can keep an internal list, and modify it on each operation, but i would suggest the one you mentioned, on button click, go trough all the parent rows, something like this,
    If:
    - none, continue;
    - partial - go trough all the children and just add the ones selected,
    - checked, add all child rows.

    If you need any help in doing this please let me know, or if the question has been solved, please mark the question as answered, so that others can find the answers to their questions faster.

    Best Regards,
    Emanuel Varga

Back to Top
UI for WinForms is Visual Studio 2017 Ready