Monday, May 1, 2023

Another Infragistics XamDataGrid bug

I came across yet another Infragistics XamDataGrid bug the other day. I sent it to Infragistcs support, which is normally very good, but the guy working on this is having trouble understanding why this is a problem.

The bug concerns a parent layout that has two child layouts. It displays expanders and labels when it should not. Here's a sample XAML and code.

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp9"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <igDP:XamDataGrid DataSource="{Binding Parents}">
            <igDP:XamDataGrid.FieldSettings>
                <igDP:FieldSettings AllowEdit="False"/>
            </igDP:XamDataGrid.FieldSettings>
            <igDP:XamDataGrid.FieldLayoutSettings>
                <igDP:FieldLayoutSettings AutoGenerateFields="False" ExpansionIndicatorDisplayMode="CheckOnDisplay"/>
            </igDP:XamDataGrid.FieldLayoutSettings>
            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout Key="Parents">
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Parent Name" Name="ParentName"/>
                        <igDP:Field Name="Children1"/>
                        <igDP:Field Name="Children2"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
                <igDP:FieldLayout ParentFieldLayoutKey="Parents" ParentFieldName="Children1" Key="Children1">
                    <igDP:FieldLayout.Settings>
                        <igDP:FieldLayoutSettings LabelLocation="Hidden"/>
                    </igDP:FieldLayout.Settings>
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Name" Name="ChildName"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
                <igDP:FieldLayout ParentFieldLayoutKey="Parents" ParentFieldName="Children2" Key="Children2">
                    <igDP:FieldLayout.Settings>
                        <igDP:FieldLayoutSettings LabelLocation="Hidden"/>
                    </igDP:FieldLayout.Settings>
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Name" Name="ChildName"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>
 
    </Grid>
</Window
>
 
----------------------------------------------

Class MainWindow
 
    Public Class cParent
        Public Property ParentName As String
        Public Property Children1 As List(Of cChild)
        Public Property Children2 As List(Of cChild)
    End Class
 
    Public Class cChild
        Public Property ChildName As String
    End Class
 
    Public Property Parents As New List(Of cParent) From
        {
            New cParent() With {.ParentName = "Parent1",
                                .Children1 = New List(Of cChild) From {New cChild() With {.ChildName = "Parent1.Child1"}},
                                .Children2 = New List(Of cChild) From {New cChild() With {.ChildName = "Parent1.Child2"}}},
            New cParent() With {.ParentName = "Parent2",
                                .Children1 = Nothing,
                                .Children2 = Nothing}
    }
End
Class
 
As you can see, I have hidden the labels on the child rows and expansion indicators are visible only on parents that have children. Parent1 has children and Parent2 does not.

The result looks like this.


I am not expecting Parent2 to have an expander button and I am not expecting the "Children1" and "Childrent2" labels because their location is set to hidden. I'm expecting it to look a bit like this when parent1 is expanded.

If I remove either of the child bands the grid behaves the way I expect.

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp9"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <igDP:XamDataGrid DataSource="{Binding Parents}">
            <igDP:XamDataGrid.FieldSettings>
                <igDP:FieldSettings AllowEdit="False"/>
            </igDP:XamDataGrid.FieldSettings>
            <igDP:XamDataGrid.FieldLayoutSettings>
                <igDP:FieldLayoutSettings AutoGenerateFields="False" ExpansionIndicatorDisplayMode="CheckOnDisplay"/>
            </igDP:XamDataGrid.FieldLayoutSettings>
            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout Key="Parents">
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Parent Name" Name="ParentName"/>
                        <igDP:Field Name="Children1"/>
                        <!--<igDP:Field Name="Children2"/>-->
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
                <igDP:FieldLayout ParentFieldLayoutKey="Parents" ParentFieldName="Children1" Key="Children1">
                    <igDP:FieldLayout.Settings>
                        <igDP:FieldLayoutSettings LabelLocation="Hidden"/>
                    </igDP:FieldLayout.Settings>
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Name" Name="ChildName"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
                <!--<igDP:FieldLayout ParentFieldLayoutKey="Parents" ParentFieldName="Children2" Key="Children2">
                    <igDP:FieldLayout.Settings>
                        <igDP:FieldLayoutSettings LabelLocation="Hidden"/>
                    </igDP:FieldLayout.Settings>
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Name" Name="ChildName"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>-->
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>
 
    </Grid>
</Window
>
 


Now Parent2 does not have an expander and the labels are not displayed.

Update: So Infragistics explained that the blue tags are not labels but expanders. When you have a single child band you don't see them and when you expand the parent you immediately see just the children as in the image above. When there are multiple child bands you see the expanders first and you use them to decide which child band to expand.

OK, this makes sense. Except that there's no way to override this behavior. Also, I would expect the XamDataGrid to realize that a collection of empty collections contains no data, so Parent2 doesn't need an expander.

Here's the response from Infragistics support..

Unfortunately  there is no way you can remove expandable field records( expander labels) with XamDatagrid and that is why we build another control called XamTreeGrid.

Seriously? You built another control just because you couldn't fix this? I could have saved you a lot of time. You have to collapse the expander record in the RecordInitialized event then take responsibility for expanding and collapsing its children yourself.

Using the project above, add some event handlers to the XamDataGrid and then write them.

        <igDP:XamDataGrid DataSource="{Binding Parents}"
                          InitializeRecord="XamDataGrid_InitializeRecord"
                          RecordExpanded="XamDataGrid_RecordExpanded"
                          RecordCollapsed="XamDataGrid_RecordCollapsed">

----------------------------------------------------

    Private Sub XamDataGrid_InitializeRecord(sender As Object, e As Events.InitializeRecordEventArgs)
 
        Dim er As ExpandableFieldRecord = TryCast(e.Record, ExpandableFieldRecord)
        Dim dr As DataRecord
        Dim dataItem As cParent
        Dim diType As Type
        Dim PI As PropertyInfo
        Dim collection As IList
 
        If er IsNot Nothing Then
            er.ExpansionIndicatorVisibility = Visibility.Collapsed
            dr = DirectCast(er.ParentRecord, DataRecord)
            dataItem = TryCast(dr.DataItem, cParent)
            diType = dataItem.GetType()
            PI = diType.GetProperty(er.Field.Name)
            collection = TryCast(PI.GetValue(dataItem), IList)
            If collection Is Nothing OrElse collection.Count = 0 Then
                er.Visibility = Visibility.Collapsed
            End If
        End If
    End Sub
 
    Private Sub XamDataGrid_RecordExpanded(sender As Object, e As Events.RecordExpandedEventArgs)
        SyncExpandableRecords(TryCast(e.Record, DataRecord))
    End Sub
 
    Private Sub XamDataGrid_RecordCollapsed(sender As Object, e As Events.RecordCollapsedEventArgs)
        SyncExpandableRecords(TryCast(e.Record, DataRecord))
    End Sub
 
    Private Sub SyncExpandableRecords(dr As DataRecord)
        dr?.ChildRecords.OfType(Of ExpandableFieldRecord).ToList().ForEach(Sub(er) er.IsExpanded = dr.IsExpanded)
    End Sub

The XamDataGrid_InitializeRecord event handler collapses the expansion indicator for all Expandable records. If the IList it is expanding is empty the entire expandable record is collapsed. Note cParent could be replaced with Object to be more generic.

When we collapse the expander record, it will no longer be able to expand/collapse its children so we have to take responsibility for that ourselves when the parent record is expanded or collapsed.

I have not tested this with more complex hierarchies or when binding to datasets. But clearly, it can be done.

Ta Da!


No comments:

Post a Comment