GridView with self referencing hierarchy problem, child rows not shown after adding programmaticaly

2 Answers 18 Views
GridView
Marian
Top achievements
Rank 2
Iron
Iron
Iron
Marian asked on 17 Apr 2024, 10:55 PM

Hello,

I have two problems with selfrefencing hierarchy in GridView. First, my test application is related to this thread, just for info and context, but I am asking question in new thread, as it was recommended to me, and I think this is separate problem not related to original design question.

https://www.telerik.com/forums/gridview---design-of-parameter-editor-with-subitems

I have created more realistic test application (TelerikTestReal in attached project) with new data structures for self referencing hierarchy binding. I can see desired output after program startup. When I add new main item programatically, GridView shows it properly. But, when I change test type of main item and subitems are changed, deleted child rows disappear, but newly added are not shown, as you can see on animation below. After some debugging, I see child rows are added to BindingList bound to DataSource of GridView, I also see ListChanged event of BindingList is properly fired, but GridView doesn't show new child rows. I can show them by calling ResetBindings() method of BindingList. What can be the problem?

The second problem is that I cannot close program after call to ResetBindings() method, I can click close button manytimes, but nothing happens.

Marian
Top achievements
Rank 2
Iron
Iron
Iron
commented on 21 Apr 2024, 11:05 PM

Hello, one more comment about the second problem, not only I cannot close program after call to ResetBindings(), but also I cannot edit any cell, clicking the cell does nothing.

2 Answers, 1 is accepted

Sort by
0
Nadya | Tech Support Engineer
Telerik team
answered on 22 Apr 2024, 01:52 PM

Hello, Marian,

Thank you for providing a test application and submitting a new thread that describes a different problem than the previous one. 

When investigating the provided project, I noticed that you have BindingList<TestParEditorItem> for the data source but you use Linq queries for the GridViewComboBoxColumn that stores the data, and indeed you call.ToList method in the end of statement. The following code is extracted from your MainForm file:

col.DataSource = ((TestType[])Enum.GetValues(typeof(TestType))).Select(v => new { Value = v, Display = v.ToString() }).ToList();

ToList method makes the collection to non IBinding collection. Although List is a generic collection and it is convenient for storing a number of business objects, it does not support the two-way binding mechanism needed for the purposes of the notifications. This is why RadGridView is not synchronized, simply because it is not notified about the change in the collection. 

I highly recommend you to refer to the following article where different cases about reflecting changes in RadGridView are described: Reflecting Custom Object Changes in RGV - RadGridView - Telerik UI for WinForms

In your case, it is necessary to call the ResetBindings() method as you have already found out, in order to notify RadGridView that the TestType in the combo column has changes and update it accordingly by adding desired sub items. The right place of this to happen in directly into the TestParEditorCollection.cs in Items_ListChanged event which you handle. I have updated it in the following way and the grid now shows correct:

private void Items_ListChanged(object sender, ListChangedEventArgs e)
{
	if ((e.ListChangedType == ListChangedType.ItemChanged) && (e.PropertyDescriptor.Name == "TestType"))
	{
		HandleTestTypeChanged(Items[e.NewIndex]);
                Items.ResetBindings();
    }
}

Also, I am able to edit cells further, and close the form. See the attached gif file.

I hope this helps. If you have any other questions do not hesitate to contact me.

Regards,
Nadya | Tech Support Engineer
Progress Telerik

A brand new ThemeBuilder course was just added to the Virtual Classroom. The training course was designed to help you get started with ThemeBuilder for styling Telerik and Kendo UI components for your applications. You can check it out at https://learn.telerik.com
Marian
Top achievements
Rank 2
Iron
Iron
Iron
commented on 22 Apr 2024, 04:56 PM

Hello,

thanks for answer. First, about that LINQ for DataSource of combo box column. Ok, so what's the right way to have enum values in combo box? I have looked to documentation, all samples are using DataTable, I think it's obsolete now. That LINQ with anonymous classes was copied from WinForms DataGridView object.

But, I don't agree the problem is in this. When I debug this code, I put breakpoint to Items_ListChanged of my collection, I can see changed TestType, HandleTestTypeChanged method is properly called, it adds two new subitems to BindingList with correct GridID / GridParentID. Next, I see two other calls to Items_ListChanged with ListChangedType == ListChangedType.ItemAdded, and I can see BindingList notification of two new subitems.

private void Items_ListChanged(object sender, ListChangedEventArgs e)
{
		if ((e.ListChangedType == ListChangedType.ItemChanged) && (e.PropertyDescriptor.Name == "TestType"))
		{
			HandleTestTypeChanged(Items[e.NewIndex]);
		}

		if(e.ListChangedType == ListChangedType.ItemAdded)
		{
			string subName = Items[e.NewIndex].SubItemName;
			// OK, fired	
		}
}

Also, when I change som other item with subitems and change type to other without subitems, I can see subitems are deleted:

So I think GridView was notified about changes, but it has some problem with newly added subitems. Btw. I can add subitems also programmatically without changing main item, so I don't think problem will be in this. Or am I missing something?

Marian
Top achievements
Rank 2
Iron
Iron
Iron
commented on 23 Apr 2024, 09:45 AM

Hello,

I forgot to comment the second problem with freezing yesterday. Today I have figured out the problem is not in changing test type or in adding child items, but generally when I call ResetBindings while GridView is edit state. See this animation. Can you please try also this?

0
Nadya | Tech Support Engineer
Telerik team
answered on 25 Apr 2024, 01:40 PM

Hello, Marian,

The way you bind the GridViewComboBox is correct, the data you want is displayed correctly in grid. However, in order to update the grid about the changes it should be notified. This is why the ResetBindings should be used. More information is availabale here: Reflect Data Source Updates in Control with BindingSource - Windows Forms .NET Framework | Microsoft Learn

From the attached gif file I can see that you modified "TestZatky" to become "RelVyska". "RelVyska" item does not have sub items. Hence, it is expected that no sub items with appear if you choose an item without subitems. I am a little confused. Can you please clarify what exactly you mean with "when I change some other item with subitems and change type to other without subitems, I can see subitems are deleted"? Am I missing something here? 

As to the other problem with freezing, after calling the ResetBindings and the grid is updated, the form closes when I am in edit mode. See the following gif file:

Let me know if I can assist you further.

Regards,
Nadya | Tech Support Engineer
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Marian
Top achievements
Rank 2
Iron
Iron
Iron
commented on 25 Apr 2024, 11:23 PM | edited

Hello,
maybe I should explain it more. Subitems for test items are dependent on selected test type. Most test types has no subitems, but there are three special tests (TestNitu, TestZatky and TestKolika) which has more results (subitems), TestNitu has 4 subitems, TestZatky and TestKolika have 2 subitems. So when test type for item is changed and new test type has different requested subitem count, subitems are internally modified in TestParItem.TestType property. More precisely, GridView is bound to collection of TestParEditorItem, which passes TestType property to underlying TestParItem and then calls NotifyPropertyChanged(). So when NotifyPropertyChanged() is called here, there are already new subitems generated in underlying TestParItem. You can see it here:

		public TestType? TestType
		{
			get => IsItem ? (TestType?)Item.TestType : null;
			set
			{
				if (IsItem)
				{
					if (Item.TestType != value.Value)
					{
						Item.TestType = value.Value;
						NotifyPropertyChanged();
					}
				}
			}
		}

Next, TestParEditorCollection is notified about changed test type in Items_ListChanged, this is handled here (I have added prints to console, so I can see events in Output window):

		private void Items_ListChanged(object sender, ListChangedEventArgs e)
		{
			Console.WriteLine($"ListChanged, Type={e.ListChangedType}, NewIndex={e.NewIndex}, PropertyDescr={(e.PropertyDescriptor?.Name ?? "--")}");

			if ((e.ListChangedType == ListChangedType.ItemChanged) && (e.PropertyDescriptor != null) && (e.PropertyDescriptor.Name == "TestType"))
			{
				Console.WriteLine($"TestType changed, GridID={EditorItems[e.NewIndex].GridID}, value={EditorItems[e.NewIndex].TestType}");
				HandleTestTypeChanged(EditorItems[e.NewIndex]);
				//EditorItems.ResetBindings();
			}

			if (e.ListChangedType == ListChangedType.ItemAdded)
			{
				Console.WriteLine($"ItemAdded, GridID={EditorItems[e.NewIndex].GridID}, ParentID={EditorItems[e.NewIndex].GridParentID}");
				string subName = EditorItems[e.NewIndex].SubItemName;
				// OK, fired	
			}
		}

Items_ListChanged event handler is notified about TestType property is changed, e.ListChangedType is ItemChanged, e.PropertyDescriptor.Name is TestType, so handler knows that subitems were changed, so it calls HandleTestTypeChanged() method, which removes old subitems first and then generates new subitems and adds them to collection. They are added and BindingList notifies GridView it contains new items, Items_ListChanged event handler is again called for each new subitems with e.ListChangedType = ItemAdded. I handled also this case and report it to console. You can see it in animation below. You can see 3 event calls there, first about changed test type, you can see there correct GridID and new TestType value, and then you can see 2 events about added subitems, also, you can see correct GridParentID and new GridIDs for subitems. So I think GridView should be correctly notified about all this, but it doesn't show new subitems.

I am also confused, what's going on there, it's strange. I think all notifications should be ok, I don't know why GridView doesn't show new subitems. If I do opposite change of TestType, when I change TestType from TestZatky to something else without subitems, you can see old subitems are removed, you can see it on my animation from previous comment, where P8 is changed from TestZatky to RelVyska and you see 2 subitems (Hlbka + Naklon) are removed. But generally problem is that GridView doesn't show newly added subitems, you can see notification in animation in Output window, I don't understand why should it be dependent to notification about change in TestType column as you wrote.

I have done one more test and I am confused little more. I added new subitem manually bypassing the internal handling in mycollection, and it's correctly shown. Why adding new subitem by EditorItems.Add() works from here, and not from Items_ListChanged event handler? In both cases, subitems are added to BindingList.

		private void tsBtnTestAdd_Click(object sender, EventArgs e)
		{
			var ed = items.EditorItems.Where(o => o.IsItem && (o.TestType == TestType.TestZatky)).Last();
			items.EditorItems.Add(new TestParEditorItem(new TestParSubItem(), 501, ed.GridID, "Test added"));
		}

But, when I first change test type, for example that P3 from AbsVyska to TestZatky, which will add two new subitems, which are not shown due to this problem we are trying to find, the same call to items.EditorItems.Add with end with strange exception originating from GridView ListChanged handler - ArgumentOutOfRangeException, Index must be within the bounds of the List. I have attached exception details with stack trace, also modified project, TelerikTestReal.

So, I don't know. Yes, I can call ResetBindings() in ListChanged event for TestType, as you proposed, but I have any clue why it doesn't work without it. Your explanation about notifications doesn't have sense to me, because I think I can see all necessary ListChanged notifications.

Tags
GridView
Asked by
Marian
Top achievements
Rank 2
Iron
Iron
Iron
Answers by
Nadya | Tech Support Engineer
Telerik team
Share this question
or