2
votes

I am using popular code to sort and group listview items. Sorting and grouping work perfect. The problem is when user clicks on column header for sorting then all groups become i.e. expanded or collapsed (that depends from Expander IsExpanded="False") What I would need is sorting of columns leaving expanders in their current state: some expanded, some collapsed. I checked also some sample projects but all of them have same behaviour. Any ideas, is it doable ? Thanks in advance!

<ListView.GroupStyle>
                                <GroupStyle>
                                    <GroupStyle.ContainerStyle>
                                        <Style TargetType="{x:Type GroupItem}">
                                            <Setter Property="Margin" Value="0,0,0,5"/>
                                            <Setter Property="Template">
                                                <Setter.Value>
                                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                                        <Expander IsExpanded="False" BorderBrush="#FFA4B97F" 
                                  BorderThickness="0,0,0,1" >
                                                            <Expander.Header>
                                                                <DockPanel>
                                                                    <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" 
                                 Margin="5,0" Width="Auto"/>
                                                                    <TextBlock FontWeight="Bold" 
                                 Text="{Binding Path=ItemCount}"/>
                                                                    <TextBlock FontWeight="Bold" Text=" Items"/>
                                                                </DockPanel>
                                                            </Expander.Header>
                                                            <Expander.Content>
                                                                <ItemsPresenter />
                                                            </Expander.Content>
                                                        </Expander>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </GroupStyle.ContainerStyle>
                                </GroupStyle>
                            </ListView.GroupStyle>

ICollectionView view = CollectionViewSource.GetDefaultView(dataSet.Tables[0]);
                view.GroupDescriptions.Add(new PropertyGroupDescription("NameOfColumnForGrouping"));
                listViewOpportunitiesHistory.ItemsSource = view;

Sorting:

listViewOpportunitiesHistory.Items.SortDescriptions.Add(new SortDescription(field, newDir));

Here is how I solved my problem:

<ListView  x:Name="listViewOpportunitiesHistory" ItemsSource="{Binding}" AlternationCount="2" 
                              IsTextSearchEnabled="False" IsSynchronizedWithCurrentItem="True"   >
            <ListView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Margin" Value="0,0,0,5"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <Grid x:Name="grid_ControlTemplate_listViewOpportunitiesHistory"  Loaded="grid_ControlTemplate_listViewOpportunitiesHistory_Loaded">
                                            <Expander  BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1"  
                                                                   x:Name="expanderControlTemplate" Expanded="expanderExpandCollapse_Expanded" Collapsed="expanderExpandCollapse_Collapsed" >
                                                <Expander.Header>
                                                    <DockPanel>
                                                        <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" 
                                 Margin="5,0" Width="Auto"/>
                                                        <TextBlock FontWeight="Bold" 
                                 Text="{Binding Path=ItemCount}"/>
                                                        <TextBlock FontWeight="Bold" Text=" Items"/>
                                                    </DockPanel>
                                                </Expander.Header>
                                                <Expander.Content>
                                                    <ItemsPresenter />
                                                </Expander.Content>
                                            </Expander>
                                        </Grid>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </ListView.GroupStyle>
            <ListView.View>
                <GridView>
                    <GridView.Columns>      
                       <GridViewColumn Width="Auto" x:Name="listViewOpportunitiesHistoryColumn_Time"  >
                            <GridViewColumnHeader Click="SortClickHistory" Tag="Time"  Content=" Time "  x:Name="listViewOpportunitiesHistoryColumnHeader_Time"  />
                            <GridViewColumn.CellTemplate >
                                <DataTemplate>
                                    <Border BorderBrush ="Gray" BorderThickness="0,0,1,0" Margin="-6,0,-6,0">
                                        <Grid Margin="6,0,6,0" >
                                            <TextBlock Text="{Binding Path=Time, StringFormat='yyyy-MM-dd HH:mm:ss.fff'}" Grid.Column ="0" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" HorizontalAlignment="Left" ToolTipService.ToolTip="{Binding Path=Time}" />
                                        </Grid>
                                    </Border>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                       <GridViewColumn Width="Auto" x:Name="listViewOpportunitiesHistoryColumn_AOValue"  >
                            <GridViewColumnHeader Click="SortClickHistory" Tag="AOValue"  Content=" AO Value "  x:Name="listViewOpportunitiesHistoryColumnHeader_AOValue"  />
                            <GridViewColumn.CellTemplate >
                                <DataTemplate>
                                    <Border BorderBrush ="Gray" BorderThickness="0,0,1,0" Margin="-6,0,-6,0">
                                        <Grid Margin="6,0,6,0" >
                                            <TextBlock Text="{Binding Path=AOValue}" Grid.Column ="0" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" HorizontalAlignment="Left" ToolTipService.ToolTip="{Binding Path=AOValue}" />
                                        </Grid>
                                    </Border>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>       
                    </GridView.Columns>
                </GridView>
            </ListView.View>  
        </ListView>

Code behind:

private Dictionary<string, bool?> expandStates = new Dictionary<string, bool?>();
        private void grid_ControlTemplate_listViewOpportunitiesHistory_Loaded(object sender, RoutedEventArgs e)
        {
            var grid = (Grid)sender;
            var dc = grid.DataContext as CollectionViewGroup;
            var groupName = (string)dc.Name.ToString();

            //If the dictionary contains the current group, retrieve a saved state of the group
            if (this.expandStates.ContainsKey(groupName))
            {
                var expander = (Expander)grid.FindName("expanderControlTemplate");
                //btn.IsExpanded = this.expandStates[groupName];
                if (this.expandStates[groupName] == true)
                {
                    expander.IsExpanded = true;
                }
                if (this.expandStates[groupName] == false)
                {
                    expander.IsExpanded = false;
                }

            }     
        }     
        private void expanderExpandCollapse_Collapsed(object sender, RoutedEventArgs e)
        {
            var expander = (Expander)sender;
            var dc = (CollectionViewGroup)expander.DataContext;
            var groupName = (string)dc.Name.ToString();

            //Loaded event is fired earlier than the Click event, so I'm sure that the dictionary contains the key
            this.expandStates[groupName] = expander.IsExpanded; //Save the current state
        }
        private void expanderExpandCollapse_Expanded(object sender, RoutedEventArgs e)
        {
            var expander = (Expander)sender;
            var dc = (CollectionViewGroup)expander.DataContext;
            var groupName = (string)dc.Name.ToString();

            //Loaded event is fired earlier than the Click event, so I'm sure that the dictionary contains the key
            this.expandStates[groupName] = expander.IsExpanded; //Save the current state
        }

I get the data from DataSet:

ICollectionView view = CollectionViewSource.GetDefaultView(dataSet.Tables[0]); 
 view.GroupDescriptions.Add(new PropertyGroupDescription(FXH.string_GroupBy_OpportunitiesHistory));
 listViewOpportunitiesHistory.ItemsSource = view;

This solution is based on Sort and Group ListItems in a WPF ListBox- GroupItem collapse and expand

1

1 Answers

0
votes

If you want the expanders for each group to save their IsExpanded property, you have to use data binding, or you have to sort the View (the expanders itself). I think that the first way is easy to implement. You need the mirror collection of bool values for group collection. In xaml, you should bind with converter:

<Expander IsExpanded="{Binding Converter={StaticResource MyOwnConverter}}" 
          BorderBrush="#FFA4B97F" 
          BorderThickness="0,0,0,1" >
    <Expander.Header>
        <DockPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" 
                       Margin="5,0" Width="Auto"/>
            <TextBlock FontWeight="Bold" 
                       Text="{Binding Path=ItemCount}"/>
            <TextBlock FontWeight="Bold" Text=" Items"/>
        </DockPanel>
    </Expander.Header>
    <Expander.Content>
        <ItemsPresenter />
    </Expander.Content>
</Expander>

Inside the converter, you can store the reference to the your ViewModel (or just to the ICollectionView), and store current IsExpanded states for each group and use it to know, which value to return. You also should carefully handle the event CollectionChanged (if your collection will changed while runtime), to add or remove the state for the group.