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
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.
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?
Hello Stenly,
Here is the XAML content for the UI design:
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>
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?