I'm not sure if this is a bug or not, but here is the issue I am experiencing: I am binding a custom collection to a grid but for bands more than two levels deep, no columns will display.
I am using a System.Collections.ObjectModel.Collection(Of T) class. I am using it to store a simple class that looks like:
As you can see, the only interesting thing this class has is a reference to another collection that also contains Issues.
So say I have a form with a grid on it and then populate a collection with some Issue objects. The grid will correctly display the first and second bands, but for the third band, it will show no columns and no rows - although it looks like something should be there (you can see the indicators and spacing where the columns should be).
Here is the code that populates and binds the collection. There are three Issues in the collection: the first one has no children, the second one has one child, and the third one has one child that itself has a child.
This will display the grid but there are no rows or column headers (the columns collection is empty in the band) for the Root3/Child/Child row - although it looks like something is there since it shows the indicator and has the spacing for it.
The thing is that adding the Root3 issue as the first issue in the collection (with its child and the child's child) will work. It seems that the grid is just looking at the first item in the collection to determine 'how far down to go' for band displaying (but the databinding logic sort of works since it at least seems that it 'knows' something is really there).
Does anyone have any ideas on whether this is a bug or if there is a setting I could use to force the grid to determine how many bands to display? I've tried every setting I could think of. Binding to a DataSource or DataSet with a self-referencing table object bound to the grid at design-time and then populating it at run time works, but I would really like to associate the object in the collection with the grid and not have that intermediary (and I'm not quite sure how deep the collection will get - although realistically no more than five levels deep).
Thanks in advance for any pointers.
-David
Aach, sorry, I pasted the text from Word and when I hit the post button it changed the formatting on me, here is a better post:
Imports System.Collections.ObjectModel
Private _description As String
Public Property Description() As String
Get
End Get
Me._description = value
End Set
End Property
End Class
Dim issues As New System.Collections.ObjectModel.Collection(Of Issue)
Dim rootIssue As Issue
Dim childIssue As Issue
Dim child2Issue As Issue
'Root 1rootIssue = New Issue()
rootIssue.Description = "Root Issue #1"
issues.Add(rootIssue)
'Root 2
rootIssue = New Issue()
rootIssue.Description = "Root Issue #2"
'Root 2 Child 1
childIssue = New Issue()
childIssue.Description = "Root Issue #2/Child Issue #1"
rootIssue.ChildIssues.Add(childIssue)
'Root 3
rootIssue.Description = "Root Issue #3"
'Root 3 Child 1
childIssue.Description = "Root Issue #3/Child Issue #1"
'Root 3 Child 1 Child 1
child2Issue = New Issue()
child2Issue.Description = "Root Issue #3/Child Issue #1/Child Issue #1"
childIssue.ChildIssues.Add(child2Issue)
Me.UltraGrid1.DataSource = issues
Hi David,
dpalau said:The thing is that adding the Root3 issue as the first issue in the collection (with its child and the child's child) will work. It seems that the grid is just looking at the first item in the collection to determine 'how far down to go' for band displaying (but the databinding logic sort of works since it at least seems that it 'knows' something is really there).
That is possible. The grid cannot handle a non-homogenous data source. That means that every row of data needs to have the same depth of child data. So, for example, if the first row in the grid returns null for a property that returns the list of child rows, then no other row in the grid will have child data either. If the property returns an empty collection, then it's possible that the BindingManager will be unable to determine the structure of the data beyond that level, and the grid will base all levels of data on the first row and it's descendants.
You may be able to get around this by implementing ITypedList on your data lists and IEditableObject on your dta object. The former gives the BindingManager a way to determine the structure independent of the data. The latter allows it to create a dummy row and then cancel it in order to get the structure.
Another option to consider is using UltraWinTree instead of UltraWinGrid. The tree can handle non-homogenous data. BUt it lacks certain other features of the grid like filtering and summaries, so it may not work for the needs ofyour application.
dpalau said:I turned off the property to auto-generate the column sets and have defined my own (set in the Override.ColumnSet property) but when I attach my collection to the treeview datasource, I now get no child nodes what so ever.. I can get around that I guess by manually adding nodes with some column information and then attaching the actual issue object to the node.Tag property, but I sure wish there was some way to set the datasource and just have done with it.
When defining your own column sets, you have to remember that a child band is actually a column. My guess is that you are not getting any child nodes because you didn't add the child band column to the ColumnSet. You need to add a column to the ColumnSet with the same key as the child band and set IsChaptered on the column to true to indicate that it should display as a child band instead of as a cell.
I'm having the same issue with the UltraWinGrid. I have a collection that represents a multilevel Bill of Materials. Certain child bands appear with empty rows and the row's Cell and Column collections are empty. This causes problems because I'm trying to set row background colors during the InitializeLayout, based on the values in certain columns. This exibits a similar behavior in the UltraTree as well, only it does not show the empty space where the row should be, it just shows up like the nodes have no children. I just updated to 2008 v3 and the same behavior exists. I also checked, but there are no hotfixes under v3 yet.
I did notice however, if you clicked on the parent row and started to edit a cell, the missing child rows suddenly appear. My workaround was to programmatically enter edit mode for the parent rows of each row that was missing its columns.
Below is the code for the my form.
In the SetRowApperance function when I come across a row with missing columns I call the EnterEdit function for the parent row. This seems to have solved my problem.
Private PartNo As String Private Sub frmBomTree_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = PartNoDim Bom As M2MInterface.M2M.BOM.BOMItems Bom = M2MInterface.M2M.BOM.PopulateBOMItems(PartNo) grdBom.DataSource = BomgrdBom.BeginInvoke(New MethodInvoker(AddressOf GridInitLayout)) End SubPrivate Sub GridInitLayout() grdBom.Rows.ExpandAll(True) SetRowApperance(grdBom.Rows) EnterEdit(grdBom.Rows(0)) End SubSub EnterEdit(ByVal row As Infragistics.Win.UltraWinGrid.UltraGridRow) If row Is Nothing Then Return grdBom.Focus() grdBom.ActiveCell = row.Cells(0)grdBom.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.ToggleCellSel, False, False) grdBom.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.EnterEditMode, False, False) End SubPublic Sub New(ByVal PartNumber As String) ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. PartNo = PartNumberEnd Sub Private Sub SetRowApperance(ByVal rows As Infragistics.Win.UltraWinGrid.RowsCollection) For Each row As Infragistics.Win.UltraWinGrid.UltraGridRow In rows If row.Cells.IndexOf("Source") = -1 Then EnterEdit(row.ParentRow) End If If row.Cells.IndexOf("Source") <> -1 Then Dim source As String = row.Cells("Source").Value If source = "M" Then row.Appearance.BackColor = Color.Pink row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True ElseIf source = "S" Then row.Appearance.BackColor = Color.Beige ElseIf source = "B" Then row.Appearance.BackColor = Color.LightGreen ElseIf source = "P" Then row.Appearance.BackColor = Color.LightBlue row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True End If End If If Not row.ChildBands Is Nothing Then For Each band As Infragistics.Win.UltraWinGrid.UltraGridChildBand In row.ChildBands SetRowApperance(band.Rows) NextEnd If NextEnd Sub
Private PartNo As String
Me.Text = PartNoDim Bom As M2MInterface.M2M.BOM.BOMItems Bom = M2MInterface.M2M.BOM.PopulateBOMItems(PartNo) grdBom.DataSource = BomgrdBom.BeginInvoke(New MethodInvoker(AddressOf GridInitLayout))
Me.Text = PartNo
Bom = M2MInterface.M2M.BOM.PopulateBOMItems(PartNo)
grdBom.DataSource = Bom
End Sub
grdBom.Rows.ExpandAll(True) SetRowApperance(grdBom.Rows) EnterEdit(grdBom.Rows(0))
SetRowApperance(grdBom.Rows)
EnterEdit(grdBom.Rows(0))
If row Is Nothing Then Return grdBom.Focus() grdBom.ActiveCell = row.Cells(0)grdBom.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.ToggleCellSel, False, False) grdBom.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.EnterEditMode, False, False)
If row Is Nothing Then Return
grdBom.Focus()
grdBom.ActiveCell = row.Cells(0)
grdBom.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.EnterEditMode, False, False)
' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. PartNo = PartNumber
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
PartNo = PartNumber
For Each row As Infragistics.Win.UltraWinGrid.UltraGridRow In rows If row.Cells.IndexOf("Source") = -1 Then EnterEdit(row.ParentRow) End If
If row.Cells.IndexOf("Source") = -1 Then EnterEdit(row.ParentRow)
If row.Cells.IndexOf("Source") = -1 Then
EnterEdit(row.ParentRow)
End If
If row.Cells.IndexOf("Source") <> -1 Then Dim source As String = row.Cells("Source").Value If source = "M" Then row.Appearance.BackColor = Color.Pink row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True ElseIf source = "S" Then row.Appearance.BackColor = Color.Beige ElseIf source = "B" Then row.Appearance.BackColor = Color.LightGreen ElseIf source = "P" Then row.Appearance.BackColor = Color.LightBlue row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True End If End If If Not row.ChildBands Is Nothing Then For Each band As Infragistics.Win.UltraWinGrid.UltraGridChildBand In row.ChildBands SetRowApperance(band.Rows) NextEnd If Next
If row.Cells.IndexOf("Source") <> -1 Then Dim source As String = row.Cells("Source").Value If source = "M" Then row.Appearance.BackColor = Color.Pink row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True ElseIf source = "S" Then row.Appearance.BackColor = Color.Beige ElseIf source = "B" Then row.Appearance.BackColor = Color.LightGreen ElseIf source = "P" Then row.Appearance.BackColor = Color.LightBlue row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True End If End If If Not row.ChildBands Is Nothing Then For Each band As Infragistics.Win.UltraWinGrid.UltraGridChildBand In row.ChildBands SetRowApperance(band.Rows) NextEnd If
If row.Cells.IndexOf("Source") <> -1 Then
Dim source As String = row.Cells("Source").Value
If source = "M" Then row.Appearance.BackColor = Color.Pink row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True ElseIf source = "S" Then row.Appearance.BackColor = Color.Beige ElseIf source = "B" Then row.Appearance.BackColor = Color.LightGreen ElseIf source = "P" Then row.Appearance.BackColor = Color.LightBlue row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True End If
If source = "M" Then
row.Appearance.BackColor = Color.Pink row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True
row.Appearance.BackColor = Color.Pink
row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True
ElseIf source = "S" Then
row.Appearance.BackColor = Color.Beige
ElseIf source = "B" Then
row.Appearance.BackColor = Color.LightGreen
ElseIf source = "P" Then
row.Appearance.BackColor = Color.LightBlue row.Appearance.FontData.Bold = Infragistics.Win.DefaultableBoolean.True
row.Appearance.BackColor = Color.LightBlue
If Not row.ChildBands Is Nothing Then
For Each band As Infragistics.Win.UltraWinGrid.UltraGridChildBand In row.ChildBands SetRowApperance(band.Rows) Next
SetRowApperance(band.Rows)
Next
Hm, that doesn't sounds like the same problem at all. Sounds like a completely different issue to me. The grid doesn't have ColumnSets, it gets the structure from the data, so I don't see how this could even be related.
The code you have posted here doesn't really help because it doesn't give any information about the grid's data source or the structure of those objects.
My best guess is that your data source is not correctly implementing IBindingList or that it contain non-homogenous data, which is not supported by the grid. But that's really just a wild guess with so little to go on.
For the record, I solved my problem by creating an UltraDataSource that had the structure I needed, filled it from my custom collection, and then bound that to the grid. I am able to get away with this because I know that I'm never going to have four bands deep of data and then I can just walk my collection and fill in the datasource appropriately recursively. This isn't too much of a hassle as I know the band depth and each band really only has two columns of data per band.
As I think Mike was saying, the problem is with the non-homogenous data that exists as a result of the dynamic nature of my custom collection. I tried some of the other options mentioned were either too much of a hassle (as compared to the UltraDataSource solution) or didn't work (or I didn't implement correctly).
It would be nice though if it was possible for an UltraWinGrid to check out the whole datasource to determine the band depth to go with.
I was having a similar issue. I was using a simple collection and binding it to the WinGrid. I wasn't using the UltraDataSource, or setting anything at desing time on the Grid. Even when the data was homogenous (accidentally loaded the same child data for each row), the datagrid would not show child rows. The space for the rows would apper along with the dashed line on the left side, but just white space for the rows.
I explored the ITypeList that Mike had suggested and added the following code to my collection definition. The grid suddenly showed the child rows. I don't actually understand what the code below does, but it worked for me.
*Update: My child rows show up now, but the first column in my grid is suddenly some huge width and it cannot be made smaller. ** Column Issue solved here: http://news.infragistics.com/forums/p/17405/63268.aspx#63268
Dim pdc As PropertyDescriptorCollection = Nothing
If listAccessors Is Nothing Then
Else
End Function
This has nothing to do with the grid, it's an issue with the DotNet BindingManager, and I don't think it's a bug.
The BindingManager determines the data structure based on the current row of data - which is typically the first row. If your first item in the collection has no child rows, then the BindingManager cannot determine the data structure of the child rows. Implementing ITypedList allows it to determine the type of objects inthe child list.
I have tested the issue with a simple recursive class:
class Person{ public string Name { get; set; } public Collection<Person> Children { get; } }
I have filled a Collection<Person> myList with data (6 hierarchies), and bound it to the grid:
grid.DataSource = myList;It shows only 2 Bands.
grid.DataSource = new BindingList<Person>(myList);It shows only 2 Bands.
grid.DataSource = new BindingSource(myList, null);It shows all the 6 Bands.
The problem occurs only if the first row in the Band has no child.UltraWinTree has the same behaviour. It seems the DataSource property of the Infragistics controls works only with ITypedList objects.