This is a migrated thread and some comments may be shown as answers.

Index out of range exception in hierarchical grid with custum multi select behavior

7 Answers 203 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Wesley
Top achievements
Rank 1
Wesley asked on 15 Nov 2012, 02:29 PM
Hi,

I have a hierarchical grid with custom multi select behavior.
I recently upgraded to Q3 2012. Now I get an exception when I try to select multiple rows that have childrows with the mouse.
This is the exception:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
   at System.ThrowHelper.ThrowArgumentOutOfRangeException()
   at System.Collections.Generic.List`1.get_Item(Int32 index)
   at System.Collections.ObjectModel.Collection`1.get_Item(Int32 index)
   at Telerik.WinControls.UI.GridRowBehavior.DoMultiFullRowSelect(Boolean shift, Boolean control)
   at Telerik.WinControls.UI.GridRowBehavior.DoMouseSelection(GridCellElement currentCell, Point currentLocation)
   at Telerik.WinControls.UI.GridRowBehavior.ProcessMouseSelection(Point mousePosition, GridCellElement currentCell)
   at Telerik.WinControls.UI.GridRowBehavior.OnMouseMove(MouseEventArgs e)
   at Telerik.WinControls.UI.BaseGridBehavior.OnMouseMove(MouseEventArgs e)
   at Telerik.WinControls.UI.RadGridView.OnMouseMove(MouseEventArgs e)
   at System.Windows.Forms.Control.WmMouseMove(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at Telerik.WinControls.RadControl.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
----------------------------------------

I made a test application that reproduces the problem.
The application contains 2 files:
  • Form1.cs
  • CustomRowInfos.cs

Form1.cs:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Telerik.WinControls.UI;
 
namespace GridWithIndexedHierarchySpike
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            dummyGridView.Dock = DockStyle.Fill;
            dummyGridView.Parent = this;
            dummyGridView.BringToFront();
            dummyGridView.MultiSelect = true;
            dummyGridView.UseScrollbarsInHierarchy = true;
        }
 
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
            GridViewTemplate childTemplate = CreateChildTemplate();
            dummyGridView.Templates.Add(childTemplate);
            childTemplate.HierarchyDataProvider = new GridViewEventDataProvider(childTemplate);
 
            //grid events
            dummyGridView.RowSourceNeeded += gridView_RowSourceNeeded;
            dummyGridView.CreateRowInfo += CreateGridViewHierarchyRowInfoWithCustomSelectBehavior;
            dummyGridView.Templates[0].CreateRowInfo += CreateGridViewDataRowInfoWhitCustomSelectBehavior;
 
            //databind
            dummyGridView.DataSource = CreateDummyData(5);
        }
  
        void gridView_RowSourceNeeded(object sender, GridViewRowSourceNeededEventArgs e)
        {
            List<SomeData> data = CreateDummyData(2);
            foreach (SomeData someData in data)
            {
                GridViewRowInfo row = e.Template.Rows.NewRow();
                row.Cells[0].Value = someData.Name;
                row.Cells[1].Value = someData.SomeDataId;
                row.Cells[2].Value = someData.ParentDataId;
  
                e.SourceCollection.Add(row);
            }
        }
 
        private static void CreateGridViewHierarchyRowInfoWithCustomSelectBehavior(object sender, GridViewCreateRowInfoEventArgs e)
        {
            if (e.RowInfo is GridViewHierarchyRowInfo)
            {
                e.RowInfo = new MultiSelectGridViewHierarchyRowInfo(e.ViewInfo);
            }
        }
 
        private static void CreateGridViewDataRowInfoWhitCustomSelectBehavior(object sender, GridViewCreateRowInfoEventArgs e)
        {
            if (e.RowInfo is GridViewDataRowInfo)
            {
                e.RowInfo = new MultiSelectGridViewDataRowInfo(e.ViewInfo);
            }
        }
 
        private GridViewTemplate CreateChildTemplate()
        {
            var template = new GridViewTemplate {AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill};
            var namecolumn = new GridViewTextBoxColumn("Name");
            var idColumn = new GridViewTextBoxColumn("ID");
            var parentColumn = new GridViewTextBoxColumn("Parent");
            template.Columns.AddRange(namecolumn, idColumn, parentColumn);
            return template;
        }
  
        private static List<SomeData> CreateDummyData(int nbrRows)
        {
            var data = new List<SomeData>();
            var someData = new SomeData { Name = Guid.NewGuid().ToString(), ParentDataId = null, SomeDataId = 0 };
  
            data.Add(someData);
            for (int i = 1; i <= nbrRows - 1; i++)
            {
                var someChildData = new SomeData()
                {
                    Name = Guid.NewGuid().ToString(),
                    ParentDataId = someData.SomeDataId,
                    SomeDataId = i
                };
                data.Add(someChildData);
            }
  
            return data;
        }
 
        public class SomeData
        {
            public int SomeDataId { get; set; }
            public int? ParentDataId { get; set; }
            public string Name { get; set; }
        }
    }
}

CustomRowInfos.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Telerik.WinControls.UI;
 
namespace GridWithIndexedHierarchySpike
{
    /// <summary>
    /// Hierarchy row with custom select behavior.
    /// When the row is (de)selected, all child rows are (de)selected
    /// </summary>
    public class MultiSelectGridViewHierarchyRowInfo : GridViewHierarchyRowInfo
    {
        public static bool ParentSilentUpdate;
        public static bool OverrideChildSelection;
 
        public MultiSelectGridViewHierarchyRowInfo(GridViewInfo owner)
            : base(owner)
        {
            ParentSilentUpdate = false;
        }
 
        protected override bool SetBooleanProperty(string propertyName, int propertyKey, bool value)
        {
            bool result = base.SetBooleanProperty(propertyName, propertyKey, value);
 
            if (result && propertyName == "IsSelected")
            {
                if (!MultiSelectGridViewDataRowInfo.ChildSilentUpdate)
                {
                    //parent row is selected/deselected;
                    SelectChildRows(value);
                }
            }
 
            return result;
        }
 
 
        private void SelectChildRows(bool isSelected)
        {
            if (OverrideChildSelection) return;
 
            ParentSilentUpdate = true;
            foreach (MultiSelectGridViewDataRowInfo row in this.ChildRows)
            {
                row.IsSelected = isSelected;
            }
            ParentSilentUpdate = false;
        }
    }
 
    /// <summary>
    /// Data row with custom select behavior.
    /// If the row is a child row of a parent row then
    /// the parent row is selected when the child rows (or one of its siblings) is selected,
    /// otherwise the parent row is deselected.
    /// </summary>
    public class MultiSelectGridViewDataRowInfo : GridViewDataRowInfo
    {
        public static bool ChildSilentUpdate;
        public MultiSelectGridViewDataRowInfo(GridViewInfo viewInfo)
            : base(viewInfo)
        {
            ChildSilentUpdate = false;
        }
 
        public object OriginalBoundItem { get; set; }
 
        protected override bool SetBooleanProperty(string propertyName, int propertyKey, bool value)
        {
           
            bool result = base.SetBooleanProperty(propertyName, propertyKey, value);
 
            if (result && propertyName == "IsSelected")
            {
                if (!MultiSelectGridViewHierarchyRowInfo.ParentSilentUpdate)
                {
                    //child row is selected
                    SelectParentRow(value);
                }
            }
 
            return result;        
        }
 
        private void SelectParentRow(bool isSelected)
        {
            var parent = Parent as MultiSelectGridViewHierarchyRowInfo;
            if (parent == null) return;
 
            ChildSilentUpdate = true;
            if (isSelected)
            {
                parent.IsSelected = true;
            }
            else
            {
                //check if there are other rows
                bool selected = GetParentRowSelection(parent);
                parent.IsSelected = selected;
            }
            ChildSilentUpdate = false;
        }
 
        private bool GetParentRowSelection(MultiSelectGridViewHierarchyRowInfo parent)
        {
            //if there is one selected child row, return true
            return parent.ChildRows.Cast<MultiSelectGridViewDataRowInfo>().Any(row => row != this && row.IsSelected);
        }
    }
}

7 Answers, 1 is accepted

Sort by
0
Emanuel Varga
Top achievements
Rank 1
answered on 15 Nov 2012, 03:02 PM
Hello Wesley,

Just fix this method:
private void SelectChildRows(bool isSelected)
{
    if (OverrideChildSelection) return;
 
    ParentSilentUpdate = true;
    foreach (MultiSelectGridViewDataRowInfo row in this.ChildRows.Where(row => !row.IsSelected))
    {
        row.IsSelected = isSelected;
    }
    ParentSilentUpdate = false;
}

If you have any other questions, please let me know.


Best Regards,
Emanuel Varga
WinForms MVP
0
Wesley
Top achievements
Rank 1
answered on 16 Nov 2012, 08:04 AM
Hi Emanuel,

I tried to change "ChildRows" in to "ChildRows.Where(row => row.IsSelected != isSelected)".
I still get the same error.
Did you mean something else with "fix the method"?

Just to be clear:
The same code worked in the past, but after I upgraded to Q3 2012 I got this problem.
The code only craches when you use the mouse to multiselect. If you use shift+arrow multiple rows get selected without problems.

How the multi select should work:
  • when a parent row is selected, all the child rows get selected automatically
  • when a child row is deselected and all siblings are deselected, the parent is deselected automatically
0
Jack
Telerik team
answered on 20 Nov 2012, 12:28 PM
Hello Wesley,

I found what causes the described issue: You are changing the selected rows collection at the same time when RadGridView iterates it. However, I am not sure how to address the issue because your selection logic is quite complex. 

One possible option would be to call SuspendPropertyNotifications/ResumePropertyNotifications methods when setting the IsSelected property inside the SelectChildRows and SelectParentRow methods:
private void SelectChildRows(bool isSelected)
{
    if (OverrideChildSelection) return;
 
    ParentSilentUpdate = true;
    foreach (MultiSelectGridViewDataRowInfo row in this.ChildRows)
    {
        row.SuspendPropertyNotifications();
        row.IsSelected = isSelected;
        row.ResumePropertyNotifications();
    }
    ParentSilentUpdate = false;
}

If this does not help, please describe in detail the behavior of you selection logic and I will be glad to help further.

I am looking forward to your reply.
 
Regards,
Jack
the Telerik team
Q3’12 of RadControls for WinForms is available for download (see what's new). Get it today.
0
Wesley
Top achievements
Rank 1
answered on 21 Nov 2012, 08:30 AM
Hi Jack,

Suspending the property notifications causes the multiselect not to work as it should.

This is how the multiselect should work:
  • when a parent row is selected, all the child rows get selected automatically
  • when a parent row is deselected, all the child rows get deselected automatically
  • As long as at least one child row of a parent row is selected, the parent row stays selected
  • When all child rows of a parent row are deselected, the parent row is deselected automatically

 

If you take the 2 files I posted in the first post and put them in a project you can easily reproduce the problem.
Any help would be appreciated.

PS: The code in the first post worked in the past. The problem occured when I upgraded to Q3 2012.

0
Jack
Telerik team
answered on 22 Nov 2012, 03:43 PM
Hello Wesley,

Thank you for this clarification. 

Now I understand the behavior that you want to achieve and the issue. It will take some time in order to find a proper solution. I will write back when I have further information.

If you have other questions, do not hesitate to contact us.
 
Kind regards,
Jack
the Telerik team
Q3’12 of RadControls for WinForms is available for download (see what's new). Get it today.
0
Jack
Telerik team
answered on 28 Dec 2012, 03:12 PM
Hello Wesley,

I apologize for the late reply. 

We identified the code which causes the selection issue and we will address it an internal build which is scheduled for the beginning of next month. The fix will be included also in our next official release - Q1 2012. The issue is now logged in our issue tracking system and you can use the following link to track its status. I updated also your Telerik points accordingly. 

You will receive notification from our Visual Studio extension service that a new version is available. I will write also when the new release is ready.

If you have any questions, do not hesitate to contact us.

Regards,
Jack
the Telerik team
Q3'12 SP1 of RadControls for WinForms is out now. See what's new.
0
Accepted
Jack
Telerik team
answered on 10 Jan 2013, 04:30 PM
Hello Wesley,

I am glad to inform you that we just released our latest internal build that addresses the selection issue observed in your application. You can download it through our Visual Studio Extensions.

If you still experience issues, do not hesitate to contact us.

Kind regards,
Jack
the Telerik team
Q3'12 SP1 of RadControls for WinForms is out now. See what's new.
Tags
GridView
Asked by
Wesley
Top achievements
Rank 1
Answers by
Emanuel Varga
Top achievements
Rank 1
Wesley
Top achievements
Rank 1
Jack
Telerik team
Share this question
or