8

编辑:我创建了一个新的 VS2010 WPF 应用程序,只有 3 个文件 MainWindow.xaml、MainWindow.xaml.cs 和 MainWindowViewModel.cs(如下所列)。如果有人觉得真的很有帮助,您可以在几秒钟内重现问题(复制/粘贴)。当您运行应用程序时,DataGrid 将显示错误的字符串“OldCollection”。如果将 ItemsSource 绑定更改为 MyCollection,它会显示正确的“NewCollection”。

完整描述: 起初我有一个 DataGrid,它的 ItemsSource 绑定到 MyCollection。我有/需要一个将新的 ObservableCollection<> 分配给 MyCollection 的方法 UpdateCollection。将 NotifyPropertyChange 添加到 MyCollection 后,UI 会更新。

接下来有必要引入一个 CollectionViewSource 来启用分组。将 UI 绑定到 MyCollectionView 后,对 UpdateCollection 的调用现在无效。调试器确认 MyCollectionView 始终包含初始 MyCollection。如何让我的 NewCollection 反映在视图中?我尝试过 View.Refresh()、Binding CollectionViewSource 和无数其他策略。

注意:主要是其他人关心对 Collection 项的更改,而不是在不调用 Refresh 的情况下更新视图(分组/排序)。我的问题是我正在为 CollectionViewSource 分配一个全新的集合,并且视图/用户界面永远不会改变。

// 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; }
    }
}

谢谢,

4

3 回答 3

8

我会选择以下两种解决方案之一。

首先,您可以使用ObservableCollection并创建一次ICollectionView(分组、排序)。ObservableCollection您可以使用.Clear()和添加新集合中的项目,而不是替换。这具有不破坏分组和排序的额外好处。

第二种方法:每当您更换您的时,ObservableCollection您都必须创建一个新 ICollectionView的用于排序和分组。

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

如果您使用 DefaultView,您可以简单地绑定到您的集合

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

你可以扔掉你的 CollectionViewSource 代码绑定的东西。

于 2012-04-11T08:03:21.320 回答
0

问题肯定是您没有将源绑定到您的 MyCollection 属性,您只分配了一次并且它永远不会再次更新。

您应该执行以下操作:

MyCollectionViewSource = new CollectionViewSource();
Binding binding = new Binding();
binding.Source = MyCollection;
BindingOperations.SetBinding( MyCollectionViewSource , 
                              CollectionViewSource.SourceProperty, 
                              binding );

如果没有调整上述内容无法立即工作,我深表歉意 - 这通常是我在 xaml 中设置的类型,因为在那里更容易。

<CollectionViewSource Source="{Binding MyCollection}" />

另请参阅:如何更改 CollectionView.Source 的绑定?

于 2012-04-10T17:39:30.450 回答
0

绑定不起作用的事实令人难以置信。我在使用 Xceed DataGridCollectionViewSource 时遇到了完全相同的问题 - 但我只是认为这是因为 Xceed 搞砸了。

我的解决方案是在每次替换基础集合时实际创建一个全新的 DataGridCollectionViewSource,并以编程方式重新配置它,然后更新 myDataGrid.Source 属性以指向新的 DataGridCollectionViewSource。该解决方法肯定会起作用,但这完全违背了绑定的目的,这应该是有效的:

void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    //Xceed DataGridCollectionView are terrible and do not update when the bound table changes, so we need to replace them each change.
   if (e.PropertyName == "MyCollection")
   {
       DataGridCollectionView GridView = new DataGridCollectionView(MyViewModel.MyCollection.DefaultView);
       GridView.SortDescriptions.Add(new SortDescription("DataSetID", ListSortDirection.Ascending));
       uiMyCollectionGrid.ItemsSource = GridView;
   }
}

也许这种蛮力解决方案可以解决您的问题?我理解如果你甚至不想考虑这个,因为它太脏了。

于 2012-04-10T19:54:11.683 回答