Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing CollectionViewSource Source in a MVVM world

Edited: I created a new VS2010 WPF appilication with just 3 files MainWindow.xaml, MainWindow.xaml.cs, and MainWindowViewModel.cs (Listed Below). If someone feels really helpful you can recreate the problem in seconds (copy/paste). When you run the app the DataGrid will display string "OldCollection" which is wrong. If you change the ItemsSource binding to MyCollection it displays "NewCollection" which is correct.

Full Description: At first I had a DataGrid with its ItemsSource bound to MyCollection. I have/need a method UpdateCollection that assigns a new ObservableCollection<> to MyCollection. With the addition of NotifyPropertyChange to MyCollection the UI updates.

Next it became necessary to introduce a CollectionViewSource to enable grouping. With the UI bound to MyCollectionView, calls to UpdateCollection now have no effect. The debugger confirms that MyCollectionView always contains the initial MyCollection. How can I get my NewCollection to be reflected in the View? I have tried View.Refresh(), Binding CollectionViewSource, and countless other strategies.

Note: Primarily others are concerned with the changes to Collection items not updating the view (grouping/sorting) without calling Refresh. My problem is I am assigning a brand new collection to CollectionViewSource and the view/UI never changes.

// MainWindow.xaml
<Window x:Class="CollectionView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid Name="grid" ItemsSource="{Binding MyCollectionView}" />
    </Grid>
</Window>

//MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace CollectionView
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();
        }
    }
}

//MainWindowViewModel.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;

namespace CollectionView
{
    class MainWindowViewModel : INotifyPropertyChanged
    {
        public MainWindowViewModel()
        {
            MyCollection = new ObservableCollection<MyObject>() { new MyObject() { TestString = "OldCollection" } };

            MyCollectionViewSource = new CollectionViewSource();

            // Bind CollectionViewSource.Source to MyCollection
            Binding MyBind = new Binding() { Source = MyCollection };
            BindingOperations.SetBinding(MyCollectionViewSource, CollectionViewSource.SourceProperty, MyBind);

            // The DataGrid is bound to this ICollectionView
            MyCollectionView = MyCollectionViewSource.View;

            // This assignment here to demonstrate that View/UI does not update to show "NewCollection"
            MyCollection = new ObservableCollection<MyObject>() { new MyObject() { TestString = "NewCollection" } };
        }

        // Collection Property
        // NotifyPropertyChanged added specifically to notify of MyCollection re-assignment
        ObservableCollection<MyObject> _MyCollection;
        public ObservableCollection<MyObject> MyCollection
        {
            get { return _MyCollection; }
            set
            {
                if (value != _MyCollection)
                {
                    _MyCollection = value;
                    NotifyPropertyChanged("MyCollection");
                }
            }
        }

        public CollectionViewSource MyCollectionViewSource { get; private set; }
        public ICollectionView MyCollectionView { get; private set; }

        // Method updates MyCollection itself (Called via ICommand from another ViewModel)
        public void UpdateCollection(ObservableCollection<MyObject> NewCollection)
        {
            MyCollection = NewCollection;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }

    class MyObject
    {
        public string TestString { get; set; }
    }
}

Thanks,

like image 745
aidesigner Avatar asked Apr 10 '12 17:04

aidesigner


1 Answers

I would choose one of the two following solutions.

First, you could take your ObservableCollection and create an ICollectionView (grouping, sorting) once. Instead of replacing the ObservableCollection you can use .Clear() and add the items from the new Collection. This has the additional bonus of not breaking your grouping and sorting.

Second approach: whenever you replace your ObservableCollection you have to create a new ICollectionView for sorting and grouping.

 this._view = (ICollectionView)CollectionViewSource.GetDefaultView(this.MyCollection);

You can simply bind to your collection if you take the DefaultView

 <DataGrid Name="grid" ItemsSource="{Binding MyCollection}" />

and you can throw away your CollectionViewSource code binding stuff.

like image 54
blindmeis Avatar answered Nov 15 '22 04:11

blindmeis