Telerik RadGridView with Expander collapsed when scroll

1 Answer 138 Views
Expander GridView
Bikash
Top achievements
Rank 1
Bikash asked on 28 Feb 2023, 05:21 AM

I've used RadGrid and inside that I have one listview with expander control. It is working fine and Expander also expanding and collapsing fine.

But only one issue I have is, when I expanded the Expander and try to scroll down the RadGrid, Expander collapsing every time, it is not retaining it's previous expand state.

I could overcome this by disabling the Virtualization on RadGrid but in that case RadGrid taking long time to load/render.

 

Any help would be appreciated.

Thanks,

1 Answer, 1 is accepted

Sort by
0
Stenly
Telerik team
answered on 02 Mar 2023, 04:38 PM | edited on 02 Mar 2023, 04:39 PM

Hello Bikash,

This behavior is expected because of the UI virtualization feature of the RadGridView control and its container recycling mechanism. What happens is that only the elements that are in the current viewport will be loaded. Once a scroll is performed, in this case, it is scrolled vertically, the elements that were present in the viewport prior to this action will be unloaded and recycled. The newly created DataTemplate instance for the RowDetailsTemplate will create a new RadExpander instance, whose IsExpanded property is false by default when the container is generated. The reason why this behavior is not present when the virtualization is disabled is that there is no container recycling mechanism and all elements are generated at the same time once the control is loaded. 

With this being said, to workaround this behavior, the UnloadingRowDetails and LoadingRowDetails events of the RadGridView control could be utilized. Through the e.Row property of the event arguments, you could retrieve the DataContext of the row (the instance of the underlying data object) and cache its hash code in a dictionary. In it, the value of the IsExpanded property of the RadExpander could be kept as the value for the key, which in this case will be the hash code's value. The RadExpander element could be retrieved via the e.DetailsElement property. This logic could be executed in the added UnloadingRowDetails event handler. In the handler for the LoadingRowDetails, retrieve the hashcode of the DataContext of the e.Row property and search the dictionary for its value, which can be applied to the IsExpanded property of the RadExpander.

The following code snippet shows this suggestion's implementation, in which the RadGridView is populated by an ObservableCollection<Person> where the Person class contains two properties (string Name and int Age, which is a simple class to demonstrate this approach):

private Dictionary<int, bool> dictionary = new Dictionary<int, bool>();

private void RadGridView_UnloadingRowDetails(object sender, GridViewRowDetailsEventArgs e)
{
    Person person = (Person)e.Row.DataContext;
    int hascode = person.GetHashCode();

    if (!this.dictionary.ContainsKey(hascode))
    {
        this.dictionary.Add(hascode, false);
    }

    this.dictionary[hascode] = ((RadExpander)e.DetailsElement).IsExpanded;
}

private void RadGridView_LoadingRowDetails(object sender, GridViewRowDetailsEventArgs e)
{
    Person person = (Person)e.Row.DataContext;
    int hascode = person.GetHashCode();

    if (this.dictionary.ContainsKey(hascode))
    {
        ((RadExpander)e.DetailsElement).IsExpanded = this.dictionary[hascode];
    }
}

With this being said, I have attached a sample project for you to test. The application would need the NoXaml version of the assemblies, as well as the assembly for the Windows 11 theme.

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

Bikash
Top achievements
Rank 1
commented on 09 Mar 2023, 05:02 AM

Thanks for the response. Yes it will work for this scenario but in our case scenario is bit complex. We have a ListView as a child in RadGridView and the Expander is the child of the ListView added as grouped element. So, in this case, model is not having the Expanded flag as Grouping is being maintained internally.
Stenly
Telerik team
commented on 09 Mar 2023, 11:36 AM

Hello Bikash,

Would it be possible to modify the above-shared sample project, to show an example structure of the RowDetailsTemplate property that is present on your end?

Bikash
Top achievements
Rank 1
commented on 12 Mar 2023, 03:02 PM

Hello Stenly,

Here is the XAML content for the UI design:

<telerik:RadGridView
        Name="gridCustomers"
        AlternateRowBackground="{StaticResource LightGrayColor}"
        AlternationCount="2"
        CanUserSelect="False"
        EnableLostFocusSelectedState="False"
        EnableRowVirtualization="True"
        HeaderRowStyle="{DynamicResource TelerikGridSimpleHeaderRowStyle}"
        ItemsSource="{Binding Customers}"
        LoadingRowDetails="GridCustomers_LoadingRowDetails"
        UnloadingRowDetails="GridCustomers_UnLoadingRowDetails"
        RowStyle="{DynamicResource TelerikGridSimpleRowStyle}"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
        Style="{StaticResource TelerikGridSimpleStyle}">
    <telerik:RadGridView.Columns>
        <telerik:RadGridView.Columns>
            <telerik:GridViewColumn
                Width="*"
                CellStyle="{StaticResource TelerikGridSimpleCellStyle}"
                HeaderCellStyle="{StaticResource TelerikGridSimpleHeaderBorderedCellStyle}">
                <telerik:GridViewColumn.Header>
                    <StackPanel>
                        <TextBlock Style="{DynamicResource CustomerGridHeaderTextStyle}" Text="{locale:Translate Key=Company}" />
                    </StackPanel>
                </telerik:GridViewColumn.Header>
                <telerik:GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Style="{DynamicResource CustomerGridCellTextStyle}" Text="{Binding CompanyName}" />
                    </DataTemplate>
                </telerik:GridViewColumn.CellTemplate>
            </telerik:GridViewColumn>
        </telerik:RadGridView.Columns>

        <telerik:RadGridView.RowDetailsTemplate>
            <DataTemplate>
                <Grid Background="White">
                    <Border
                        Margin="12"
                        BorderBrush="{StaticResource NeutralGray400}"
                        BorderThickness="1">
                        <Grid x:Name="rowDetailGrid" Margin="12">
                            <uc:CustomerJobListControl
                                Grid.Row="2"
                                Grid.ColumnSpan="7"
                                Width="{Binding ElementName=rowDetailGrid, Path=ActualWidth}"
                                MaxWidth="{Binding ElementName=rowDetailGrid, Path=ActualWidth}"
                                Margin="0"
                                Padding="0"
                                VerticalAlignment="Top"
                                HorizontalContentAlignment="Stretch"
                                DataContext="{Binding Jobs}"
                                SelectedCustomer="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=telerik:RadGridView}, Path=DataContext.SelectedCustomer}"
                                ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="MouseDoubleClick">
                                        <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=telerik:RadGridView}, Path=DataContext.ViewEditCommentCommand}"/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </uc:CustomerJobListControl>
                        </Grid>
                    </Border>
                </Grid>
            </DataTemplate>
        </telerik:RadGridView.RowDetailsTemplate>
</telerik:RadGridView>
    
    <!-- uc:CustomerJobListControl XAML is below -->

<UserControl
<UserControl.Resources>
    <CollectionViewSource x:Key="view" Source="{Binding}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="CustomerJobId" />
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</UserControl.Resources>

<Grid MaxWidth="{Binding ElementName=customerJobListGrid, Path=ActualWidth}" Margin="0">
    <Border
            Height="1"
            VerticalAlignment="Top"
            Background="{StaticResource TransparentColor}"
            BorderThickness="0,0,0,0" />
    <ListView
            Name="listView"
            Margin="0"
            Padding="0"
            VerticalAlignment="Top"
            HorizontalContentAlignment="Stretch"
            Background="Transparent"
            BorderThickness="0"
            ItemsSource="{Binding Source={StaticResource view}}"
            Unloaded="listView_Unloaded"
            Loaded="listView_Loaded"
            MouseDoubleClick="JobRow_DoubleClick"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <i:Interaction.Behaviors>
            <behaviours:IgnoreMouseWheelBehavior />
        </i:Interaction.Behaviors>
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Margin" Value="0" />
                <Setter Property="Padding" Value="0" />
                <Setter Property="BorderThickness" Value="0" />
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <StackPanel Margin="0,0,0,0" Orientation="Vertical">
                                        <Border
                                                Height="1"
                                                Margin="0,6,0,6"
                                                BorderBrush="{StaticResource NeutralGray400}"
                                                BorderThickness="1" />
                                        <Expander
                                                ext:Extensions.AttachedWidth="{Binding ElementName=customerJobListGrid, Path=ActualWidth, Converter={StaticResource PercentageToSizeConverter}, ConverterParameter=12}"
                                                ext:Extensions.ItemsCount="{Binding Path=Items.Count}"
                                                Header="{Binding Path=Items[0].CustomerJobDescription}"
                                                IsExpanded="{Binding ElementName=customerJobListGrid, Path=IsJobExpanded}"
                                                Expanded="Expander_Expanded"
                                                Style="{DynamicResource ExpanderStyle}">
                                            <ItemsPresenter />
                                        </Expander>
                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </ListView.GroupStyle>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid 
                        Width="{Binding ElementName=customerJobListGrid, Path=ActualWidth, Converter={StaticResource ActualWidthWithMarginConverter}, ConverterParameter=4}"
                        Height="Auto"
                        Margin="0,0,0,2">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="22" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*" />
                        <ColumnDefinition Width="12" />
                        <ColumnDefinition Width="2*" />
                        <ColumnDefinition Width="12" />
                        <ColumnDefinition Width="1*" />
                        <ColumnDefinition Width="12" />
                        <ColumnDefinition Width="1*" />
                    </Grid.ColumnDefinitions>
                    
                    <TextBlock
                            Grid.Column="2"
                            Margin="0"
                            Padding="0"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding NumberAndName}"
                            TextTrimming="CharacterEllipsis" />
                    <TextBlock
                            Grid.Column="4"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding ProductNumber}"
                            TextTrimming="CharacterEllipsis" />
                    <TextBlock
                            Grid.Column="6"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding ColorDateTime, StringFormat='{}{0:MM/dd/yyyy}'}"
                            TextTrimming="CharacterEllipsis" />
                    <TextBlock
                            Grid.Row="2"
                            Grid.Column="2"
                            Grid.ColumnSpan="4"
                            HorizontalAlignment="Left"
                            Foreground="{DynamicResource DarkRedColor}"
                            Text="{Binding Comment}"
                            TextTrimming="CharacterEllipsis">
                        <TextBlock.Style>
                            <Style TargetType="TextBlock">
                                <Style.Triggers>
                                    <Trigger Property="Text" Value="">
                                        <Setter Property="Visibility" Value="Collapsed" />
                                    </Trigger>
                                    <Trigger Property="Text" Value="{x:Null}">
                                        <Setter Property="Visibility" Value="Collapsed" />
                                    </Trigger>
                                </Style.Triggers>
                            </Style>
                        </TextBlock.Style>
                    </TextBlock>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

</UserControl>

Stenly
Telerik team
commented on 15 Mar 2023, 01:53 PM

For this scenario, what comes to my mind would be to retrieve the Expander elements from the custom control using the ChildrenOfType extension method. Then, the dictionary that keeps that hashcode of each Person (referring to the scenario in the sample project from my initial reply) could be edited to keep a collection of bool entries (keep each Expander's IsExpanded property value). On the UnloadingRowDetails event, these Expander elements could be retrieved and their IsExpanded value cached in the mentioned dictionary. On the LoadingRowDetails event, these values could be reapplied on the Expander objects.

With this being said, I have modified the sample project from my initial answer to include a sample implementation of the above suggestion. A bit about the setup is that inside the RowDetailsTemplate, a custom UserControl with a ListView is placed and an Expander element is defined in its GroupStyle style property. The ListView contains a GridView with 2 columns and a grouping being applied to it, in order to show the Expander objects.

Could you give the modified project a try?

Tags
Expander GridView
Asked by
Bikash
Top achievements
Rank 1
Answers by
Stenly
Telerik team
Share this question
or