Manual Object-Relational Hierarchy Not Quite Working

3 Answers 147 Views
GridView
Michael
Top achievements
Rank 1
Iron
Iron
Veteran
Michael asked on 10 Feb 2022, 05:26 PM

I have a grid whose datasource is a list of DocumentGroup objects, each of which has a property "Others" of type IEnumerable<Document>.

I wrote the following:

        GridViewTemplate childTemplate = new GridViewTemplate();
        grdDocuments.Templates.Add(childTemplate);
        childTemplate.Columns.Add(new GridViewTextBoxColumn(nameof(Document.Docname)));
        childTemplate.Columns.Add(new GridViewTextBoxColumn(nameof(Document.Filepath)));
        GridViewRelation relation = new GridViewRelation(grdDocuments.MasterTemplate, childTemplate);
        relation.ChildColumnNames.Add(nameof(CompanyTb.DocumentGroup.Others));
        childTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
        grdDocuments.Relations.Add(relation);
        
        grdDocuments.Fill(Data.DocumentGroups);

(I'm not showing the whole setup of grdDocuments.)

This basically works, in that it shows a row for each DocumentGroup with the data in their columns, plus an icon to the left of each allowing the user to open the associated sub-grid of Documents.  Clicking on that icon gives me the correct number of rows under each DocumentGroup and each row has two columns (one for the Docname property and one for the Filepath property).

The only problem is that all the cells in all the Document rows are empty, even though there's data in the objects behind them.

What am I missing?

3 Answers, 1 is accepted

Sort by
0
Dinko | Tech Support Engineer
Telerik team
answered on 11 Feb 2022, 03:05 PM

Hello Michael,

Thank you for the provided code snippet.

At first sight, it seems that the set-up is correct. However, I could be missing something from your implementation. Could it be possible to share a sample project which demonstrates this behavior? This way I can take a closer look and debug it on my side.

I am looking forward to your reply.

Regards,
Dinko
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.

Samuel
Top achievements
Rank 1
commented on 07 Mar 2022, 02:27 PM

As I wrote you some time ago -- when last I had time for this -- it's not practical for me to construct a minimal project that demonstrates the problem.  I'm fine with doing my share of the debugging under your guidance, though.

So one thing that occurs to me is that in the examples I've seen in your forum the relation has its ChildColumnNames and ParentColumnNames set to cconnect the child grid to the parent grid.  I don't have that.

If that indeed is the problem then I have two comments:

  1. This seems like an arbitrary and unhelpful restriction.  The child table can be populated from a collection contained in the parent row, where the objects in the collection don't have back-pointers to their containing object.  My data, for example, doesn't come straight from a table; these are POCOs.
  2. I did indicate the property of the parent object that is the collection of child objects and that worked to the extent that the correct number of child rows is generated for each parent row.  How is that happening if the relation is unaware of how they're connected?

I look forward to your response.

0
Dinko | Tech Support Engineer
Telerik team
answered on 10 Mar 2022, 09:30 AM

Hello Michael,

I will start with that I am assuming that you are setting the hierarchy in RadGridView bound mode. When we have RadGridView in Hierarchical mode, in order for the child table to be able to match the parent row child collection, the ChildColumnNames and ParentColumnNames properties need to be set. Otherwise, the relation won't know which columns to use to match the child template source. My guess here is that the experienced behavior is related to the fact that you are building a relationship with a column that is not available in your child template. If that column is not necessary you can hide it but it needs to be part of the template. 

What I can suggest here first is to check the pass strings as parameters to the columns. Check nameof(Document.Docname) and nameof(Document.Filepath)). One of the child column field names needs to match a column field name from its parent grid. Also, the matching columns need to have equal types otherwise the relation won't work. At last but not least, you need to set the ChildColumnNames and ParentColumnNames properties of the relation to point out which child column to use to match the parent one. 

For your convenience, I have prepared a sample project to demonstrate the above settings. When you run the project, expand the row, you can observe how the second column cell is empty. This is because the fieldname property of the second column is not set. You can debug this in your project.

As a side note, if you get an exception when you try to run the example, saying that the Nwind.mdb cannot be found, navigate to the project folder. Copy the Nwind.mdb file and paste it inside the bin\debug folder.

Regards,
Dinko
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 27 Feb 2023, 08:17 PM

Hi, Dinko.  I had put this aside to work on other projects and am finally circling back to it.

I have just spent several days trying to get this to work and have returned to the point where I need your help.  Let me explain my situation:

I have a hierarchical grid in bound mode.  This one is based on different classes than in my previous example, but the idea is the same.  There's a class called InvoiceFeesDetail and it has a member, Credits, that's an ISet<InvoiceCredit>.

I had hoped to be able to describe the situation to RadGridView in those terms, but that seems to not be possible.  Instead, I wrote the following:

After creating the grid bound to a list of InvoiceFeesDetails, I add

AutoGenerateHierarchy = true;

Then I fill the parent grid, which generates a Relation.  I notice that -- contrary to all the documentation I've seen from Telerik -- the Relation has "Credits" in the child columns and nothing in the parent columns.  This actually makes sense to me, as it maps to the relationship between the classes.

In fact, this works, whereas creating an "ID" column in the parent grid and a corresponding "ParentID" column in the child grid did not work for me.  Now the proper InvoiceCredit objects show up in the proper child grids.  So far so good.

But I have not been able to figure out how I can add an InvoiceCredit to an InvoiceFeesDetail.Credits set.  I would expect the default behavior to be a) creating an InvoiceCredit with its default constructor and binding it to the new row.  That way any values I enter into the row will automatically be propagated to the InvoiceCredit, just as it would if I were editing it.  Then, when I hit Enter or otherwise move out of the editor, the InvoiceCredit would be added to the Credits set of the parent.

Instead, it seems to wait until I finish editing and then try to create the InvoiceCredit using a constructor with unknown arguments.  At least that's what I'm guessing.  Here's the exception:

System.ArgumentNullException: Value cannot be null.
Parameter name: type
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at Telerik.WinControls.UI.GridViewObjectRelationalDataProvider.AddNewRow(GridViewNewRowInfo newRow)
   at Telerik.WinControls.UI.GridViewNewRowInfo.EndAddNewRow()
   at Telerik.WinControls.UI.GridViewNewRowInfo.OnEndEdit()
   at Telerik.WinControls.UI.GridViewRowInfo.CallOnEndEdit()
   at Telerik.WinControls.UI.GridViewEditManager.FinishEditingOperation()
   at Telerik.WinControls.UI.GridViewEditManager.EndEdit()
   at Telerik.WinControls.UI.RadGridViewElement.EndEdit()
   at Telerik.WinControls.UI.GridRowBehavior.ValidateOnUserInput(MouseEventArgs e)
   at Telerik.WinControls.UI.GridRowBehavior.OnMouseDown(MouseEventArgs e)
   at Telerik.WinControls.UI.BaseGridBehavior.OnMouseDown(MouseEventArgs e)
   at Telerik.WinControls.UI.RadGridView.OnMouseDown(MouseEventArgs e)
   at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at Telerik.WinControls.RadControl.WndProc(Message& m)
   at Telerik.WinControls.UI.RadGridView.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

As an aside,

childTemplate.MasterTemplate.AddNewBoundRowBeforeEdit = true

seems to make no difference, making me wonder what it's for.

I'd appreciate it if you could explain to me how adding a new InvoiceCredit corresponding to the new row is supposed to work.

 

0
Dess | Tech Support Engineer, Principal
Telerik team
answered on 28 Feb 2023, 03:16 PM

Hello, Michael,

The following KB article is quite useful about the scenario you have:

https://docs.telerik.com/devtools/winforms/knowledge-base/gridview-build-hierarchy-from-a-nested-list 

It is important to note you need a parameterless constructor in order to create an instance in the new row. I have attached a sample project for your reference. Please give it a try and see how it works on your end.

I hope this information helps. If you need any further assistance please don't hesitate to contact me. 

Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 28 Feb 2023, 07:32 PM

Thank you, that helped.  I noticed that in your example project "Parts" is a BindingList<Part>.  If I change that to ISet<Part> I get the same exception that I'm getting.  I have reason for "Credits" to be an ISet, though, so how can I work around this?
Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 01 Mar 2023, 08:16 AM

I've managed to change Credits to an IList, but now when I do 

AutoGenerateHierarchy = true;

it generates not only the Relation but also autogenerates the columns for the InvoiceCredit subgrid from all its properties.  I don't want them all.  How do I turn that functionality off (and please don't tell me to simply make the unwanted columns invisible)?

Dinko | Tech Support Engineer
Telerik team
commented on 01 Mar 2023, 09:48 AM

In this case, you can remove all columns in the child grid and manually add the desired ones. For example:

this.radGridView1.MasterTemplate.Templates[0].Columns.Clear();

GridViewTextBoxColumn partTitleColumn = new GridViewTextBoxColumn();
partTitleColumn.Name = "Part Title";
partTitleColumn.HeaderText = "Part Title";
partTitleColumn.FieldName = "PartTitle";
partTitleColumn.MaxLength = 50;
partTitleColumn.Width = 250;
this.radGridView1.MasterTemplate.Templates[0].Columns.Add(partTitleColumn);

Give this approach a try and let me know how it goes.

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 01 Mar 2023, 09:56 AM

Thank you.  I tried that.  I used Relations.First().ChildTemplate rather than MasterTemplate.Templates[0], but the result was the same:  The columns I want were inserted (I checked in the debugger), but at some later point the "missing" columns were added back in.

 

 

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 01 Mar 2023, 10:02 AM

If there's an event for adding a column I can handle it and see what's on the stack when they get re-added...

Which reminds me:  Is there any document that shows a complete list of events?

Dinko | Tech Support Engineer
Telerik team
commented on 01 Mar 2023, 11:21 AM

You can share when exactly the Clear() method is called. You need to call it after setting the DataSource of the RadGridView. You can also set the AutoGenerateColumns property of the RadGridView.

this.radGridView1.AutoGenerateColumns = false;
this.radGridView1.AutoGenerateHierarchy = true;

this.radGridView1.DataSource = cars;
this.radGridView1.MasterTemplate.Templates[0].Columns.Clear();

I have modified the project attached to my colleague Desislava's reply to show how to modify the child template columns.

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 01 Mar 2023, 02:28 PM

I have those lines in the code and they're in the proper order, but I'll take a look at this download and get back to you.
Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 01 Mar 2023, 05:09 PM

I'm doing the same thing as in the download (obviously not exactly the same thing, but as far as the Columns) but I'm seeing the behavior I described to you earlier.  I made the following event handlers:

childTemplate.Columns.CollectionChanged += (sender, args) =>
{
};
childTemplate.CreateRowInfo += (sender, args) =>
{
};

I set a breakpoint on each.  CollectionChanged is entered when I Clear the Columns and then when I add the four columns that I want.  It correctly shows first the 8 autogenerated columns and then zero when I clear them and then 4 when I add the ones I want.  That breakpoint is never hit again.

However, CreateRowInfo is hit when I try to create a child row, and at that point there are 8 columns again.  I have no idea why they're there or how they got there without raising the CollectionChanged event.

As an aside, when I try to add a child row with your new project I get this:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Telerik.WinControls.UI.GridViewObjectRelationalDataProvider.AddNewRow(GridViewNewRowInfo newRow)
   at Telerik.WinControls.UI.GridViewNewRowInfo.EndAddNewRow()
   at Telerik.WinControls.UI.GridViewNewRowInfo.OnEndEdit()
   at Telerik.WinControls.UI.GridViewRowInfo.CallOnEndEdit()
   at Telerik.WinControls.UI.GridViewEditManager.FinishEditingOperation()
   at Telerik.WinControls.UI.GridViewEditManager.EndEdit()
   at Telerik.WinControls.UI.RadGridViewElement.EndEdit()
   at Telerik.WinControls.UI.GridNewRowBehavior.ProcessEnterKey(KeyEventArgs keys)
   at Telerik.WinControls.UI.GridRowBehavior.ProcessKey(KeyEventArgs keys)
   at Telerik.WinControls.UI.BaseGridBehavior.ProcessKey(KeyEventArgs keys)
   at Telerik.WinControls.UI.BaseGridBehavior.ProcessKeyDown(KeyEventArgs keys)
   at Telerik.WinControls.UI.BaseGridEditor.OnKeyDown(KeyEventArgs keyEventArgs)
   at Telerik.WinControls.UI.RadTextBoxEditor.OnKeyDown(KeyEventArgs e)
   at Telerik.WinControls.UI.RadTextBoxEditor.TextBoxItem_KeyDown(Object sender, KeyEventArgs e)
   at System.Windows.Forms.KeyEventHandler.Invoke(Object sender, KeyEventArgs e)
   at Telerik.WinControls.RadItem.OnKeyDown(KeyEventArgs e)
   at Telerik.WinControls.UI.RadTextBoxItem.TextBoxControl_KeyDown(Object sender, KeyEventArgs e)
   at System.Windows.Forms.Control.OnKeyDown(KeyEventArgs e)
   at System.Windows.Forms.Control.ProcessKeyEventArgs(Message& m)
   at System.Windows.Forms.Control.ProcessKeyMessage(Message& m)
   at System.Windows.Forms.Control.WmKeyChar(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.TextBoxBase.WndProc(Message& m)
   at System.Windows.Forms.TextBox.WndProc(Message& m)
   at Telerik.WinControls.UI.HostedTextBoxBase.WndProc(Message& message)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at _1553376GridHierarchy.Program.Main() in C:\3rdParty\Telerik\1553376GridHierarchy\Program.cs:line 17

 

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 01 Mar 2023, 05:23 PM

Let me show you my code:

AutoGenerateColumns = false;
AutoGenerateHierarchy = true;
Fill(details);
var creditGrid = new ObjectGrid<InvoiceCredit>();
var childTemplate = Relations.First().ChildTemplate;

childTemplate.Columns.CollectionChanged += (sender, args) =>
{
};
childTemplate.CreateRowInfo += (sender, args) =>
{
};
childTemplate.Columns.Clear(); // don't know why, but they were autogenerated from the properties
childTemplate.Columns.AddRange(new[]
{
creditGrid.CreateColumn(50, "Fee", c => c.Fee),
creditGrid.CreateColumn(60, "Expenses", c => c.Expenses),
creditGrid.CreateColumn(40, "Code", c => c.CodeString),
creditGrid.CreateColumn(100, "Comment", c => c.Comment),
});
childTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
childTemplate.AllowAddNewRow = true;
childTemplate.AllowDeleteRow = true;
childTemplate.AllowEditRow = true;

I need to point out that ObjectGrid<> is a class of mine that inherits from your GridView.  I use it so that I can do things like add columns using LINQ for the underlying FieldName rather than strings.  There's a lot of other code in there and I'm perfectly willing to believe that it's some of my code that's causing the problem, but I don't see how.  I'm certainly not adding those columns back in.

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 02 Mar 2023, 07:40 AM

Okay, I solved the column problem by adding

childTemplate.AutoGenerateColumns = false;

after

AutoGenerateColumns = false;
AutoGenerateHierarchy = true;
Fill(details);
var creditGrid = new ObjectGrid<InvoiceCredit>();
var childTemplate = Relations.First().ChildTemplate;

My next challenge is getting the InvoiceCredits created and added to the InvoiceFeesDetail.Credits correctly.  I'll write with more details about later.

Michael
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 03 Mar 2023, 02:58 PM

I didn't have time to work on it today, but perhaps you can give me some ideas over the weekend on how to debug the next problem:

When I add a row, the InvoiceCredit gets created and added to the Credits of the proper InvoiceFeesDetail.  But, the values that I enter into the row are not copied into the properties.  The setters are not called.

Thanks in advance.  Have a good weekend.

Tags
GridView
Asked by
Michael
Top achievements
Rank 1
Iron
Iron
Veteran
Answers by
Dinko | Tech Support Engineer
Telerik team
Dess | Tech Support Engineer, Principal
Telerik team
Share this question
or