none
Getting CollectionViewSource groups to refresh

    Question

  • I've got a simple example of a CollectionViewSource that displays a collection of "Animals", grouped by the type of animal. There are two specific problems with it, as far as I'm concerned, and I'd like to understand if there's something that can be done to resolve either of these issues. They might both be the same issue, for all I know.

    The first issue occurs when adding a new element to the collection. Although it properly places it into the correct expander based on the group type, it doesn't cause my Header to update. My Header is bound to the CollectionViewGroup, and I'm using the Name and ItemCount as the header for my expander. So, if I click the button to add a new Dog to the collection, the dog will show up where it belongs, but the header will not update to reflect the new count.

    The second issue is that if I programmatically change the type of an object, it doesn't update the collections. I would expect that if I change an animal from a Dog to a Cat, then the item should be moved into the Cat expander. Instead, the object doesn't move.

    Can anyone give me an idea of this sort of thing is possible, and what I need to do to get it working?

    I wish I could attach a project, but I guess I'll just have to paste the source code below. The code is in a xaml file and its corresponding .cs file. The application runs just fine, so if you like you can create a new WPF app, and paste in these contents for the MainWindow.xaml and MainWindow.xaml.cs (this assumes the project is named "WpfApplication1".



    MainWindow.xaml:
    <Window
      x:Class="WpfApplication1.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:WpfApplication1"
      Title="MainWindow"
      Height="350"
      Width="525">
    
      <Window.Resources>
        <CollectionViewSource
          x:Key="animalsSource"
          Source="{Binding Animals}">
          <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription
              PropertyName="AnimalType" />
          </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    
    
        <local:GroupHeaderConverter
          x:Key="groupHeaderConverter" />
    
        <Style
          x:Key="groupHeader"
          TargetType="{x:Type Expander}">
          <Setter
            Property="Header"
            Value="{Binding Converter={StaticResource groupHeaderConverter}}" />
        </Style>
    
      </Window.Resources>
    
    
      <ScrollViewer>
        <StackPanel>
    
          <Button
            Margin="5"
            Content="Click to Add a new Dog"
            Click="Button_Click"></Button>
    
          <Button
            Margin="5"
            Content="Click to Change a Dog into a Cat"
            Click="Button_Click_1"></Button>
    
    
          <ItemsControl
            x:Name="animalsSource"
            ItemsSource="{Binding Source={StaticResource animalsSource}}">
            
            <ItemsControl.ItemTemplate>
              <DataTemplate>
                <DockPanel>
                  <Label
                    Margin="5"
                    DockPanel.Dock="Left"
                    Content="{Binding Name}"></Label>
                  <Label
                    Margin="5"
                    DockPanel.Dock="Left"
                    Content="{Binding AnimalType}"></Label>
                </DockPanel>
              </DataTemplate>
            </ItemsControl.ItemTemplate>
    
            <ItemsControl.GroupStyle>
              <GroupStyle>
                <GroupStyle.ContainerStyle>
                  <Style
                    TargetType="{x:Type GroupItem}">
                    <Setter
                      Property="Template">
                      <Setter.Value>
                        <ControlTemplate
                          TargetType="{x:Type GroupItem}">
                          <Expander
                            Style="{StaticResource groupHeader}">
                            <ItemsPresenter
                              Margin="20 0 0 0" />
                          </Expander>
                        </ControlTemplate>
                      </Setter.Value>
                    </Setter>
                  </Style>
                </GroupStyle.ContainerStyle>
              </GroupStyle>
            </ItemsControl.GroupStyle>
          </ItemsControl>
    
        </StackPanel>
      </ScrollViewer>
    </Window>
    

    MainWindow.xaml.cs:

    using System;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
      /// <summary>
      /// Interaction logic for MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window
      {
        public MainWindow()
        {
          InitializeComponent();
    
          Animals = new ObservableCollection<Animal>();
    
          Animals.Add( new Animal( "Doggy", AnimalType.Dog ) );
          Animals.Add( new Animal( "Kitty", AnimalType.Cat ) );
          Animals.Add( new Animal( "Birdy", AnimalType.Bird ) );
          Animals.Add( new Animal( "Fishy", AnimalType.Fish ) );
          Animals.Add( new Animal( "Mousey", AnimalType.Mouse ) );
    
          this.DataContext = this;
        }
    
        public ObservableCollection<Animal> Animals
        {
          get;
          set;
        }
    
        private void Button_Click( object sender, RoutedEventArgs e )
        {
          Animals.Add( new Animal( string.Format( "Puppy #{0}", new Random().Next( 1, 1000 ).ToString() ), AnimalType.Dog ) );
        }
    
        private void Button_Click_1( object sender, RoutedEventArgs e )
        {
          foreach ( var animal in this.Animals ) {
            if ( animal.AnimalType == AnimalType.Dog ) {
              animal.AnimalType = AnimalType.Cat;
              break;
            }
          }
        }
      }
    
      public class Animal : DependencyObject
      {
        public Animal( string name, AnimalType animalType )
        {
          Name = name;
          AnimalType = animalType;
        }
    
        public string Name
        {
          get { return (string)this.GetValue( NameProperty ); }
          set { this.SetValue( NameProperty, value ); }
        }
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
          "Name", typeof( string ), typeof( Animal ), new PropertyMetadata( "" ) );
    
        public AnimalType AnimalType
        {
          get { return (AnimalType)this.GetValue( AnimalTypeProperty ); }
          set { this.SetValue( AnimalTypeProperty, value ); }
        }
        public static readonly DependencyProperty AnimalTypeProperty = DependencyProperty.Register(
          "AnimalType", typeof( AnimalType ), typeof( Animal ), new PropertyMetadata() );
    
      }
    
      public enum AnimalType
      {
        Cat, Dog, Fish, Mouse, Bird
      }
    
      public class GroupHeaderConverter : IValueConverter
      {
        public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
          var cvg = value as CollectionViewGroup;
          if ( cvg == null )
            return string.Empty;
          else
            return string.Format( "{0} ({1})", cvg.Name, cvg.ItemCount );
        }
    
        public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
          throw new NotImplementedException();
        }
      }
    
    }
    


    Thanks for any advice.

    -Dan

    Wednesday, February 3, 2010 10:20 PM

Answers

  • Ultimately found this project, which demonstrates the desired functionality:

    http://activecollectionview.codeplex.com/
    • Marked as answer by Dan Goyette Thursday, February 4, 2010 3:43 PM
    Thursday, February 4, 2010 3:43 PM