在绑定数据更改后重新排序WPF DataGrid

acruukt9  于 2023-10-22  发布在  其他
关注(0)|答案(5)|浏览(110)

我正在寻找一种方法来重新排序我的DataGrid时,底层数据已更改
(The设置相当标准:DataGrid的ItemSource属性绑定到ObservableCollection;列为DataGridTextColumns; DataGrid中的数据对ObservableCollection中的更改做出正确React;当用鼠标单击时,排序工作正常)
有什么想法吗?

rjee0c15

rjee0c151#

我花了整个下午的时间,但我终于找到了一个解决方案,它令人惊讶地简单,简短和高效:
要控制正在讨论的UI控件(这里是DataGrid)的行为,可以简单地使用CollectionViewSource。它充当ViewModel中UI控件的一种代表,而不会完全破坏MVMM模式。
在ViewModel中声明一个CollectionViewSource和一个普通的ObservableCollection<T>,并将CollectionViewSource包裹在ObservableCollection周围:

// Gets or sets the CollectionViewSource
public CollectionViewSource ViewSource { get; set; }

// Gets or sets the ObservableCollection
public ObservableCollection<T> Collection { get; set; }

// Instantiates the objets.
public ViewModel () {

    this.Collection = new ObservableCollection<T>();
    this.ViewSource = new CollectionViewSource();
    ViewSource.Source = this.Collection;
}

然后在应用程序的View部分,您无需将CollectionControlItemsSource绑定到CollectionViewSource的View属性,而不是直接绑定到ObservableCollection

<DataGrid ItemsSource="{Binding ViewSource.View}" />

从现在开始,您可以使用ViewModel中的CollectionViewSource对象直接操作View中的UI控件。
例如,排序(这是我的主要问题)看起来像这样:

// Specify a sorting criteria for a particular column
ViewSource.SortDescriptions.Add(new SortDescription ("columnName", ListSortDirection.Ascending));

// Let the UI control refresh in order for changes to take place.
ViewSource.View.Refresh();

你看,非常非常简单和直观。希望这能帮助其他人,就像它帮助我一样。

u2nhd7ah

u2nhd7ah2#

这更多的是为了澄清而不是回答,但是WPF总是绑定到ICollectionView而不是源集合。CollectionViewSource只是一种用于创建/检索集合视图的机制。
这里有一个关于这个主题的很好的资源,可以帮助你更好地使用WPF中的集合视图:http://bea.stollnitz.com/blog/?p=387
在XAML中使用CollectionViewSource实际上可以简化您的代码:

<Window
    ...
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">

    <Window.Resources>
        <CollectionViewSource Source="{Binding MySourceCollection}" x:Key="cvs">
          <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="ColumnName" />
          </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
        
    ...
        
    <DataGrid ItemsSource="{Binding Source={StaticResource cvs}}">
    </DataGrid>
</Window>

有些人认为,当遵循MVVM模式时,视图模型应该始终公开集合视图,但在我看来,这取决于用例。如果视图模型永远不会直接与集合视图交互,那么在XAML中配置它会更容易。

lokaqttq

lokaqttq3#

sellmeadog给出的答案要么过于复杂,要么已经过时。超级简单您所要做的就是:

<UserControl.Resources>
    <CollectionViewSource 
        Source="{Binding MyCollection}" 
        IsLiveSortingRequested="True" 
        x:Key="MyKey" />
</UserControl.Resources>

<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...
13z8s7eq

13z8s7eq4#

我看不出有什么明显容易的方法,所以我会尝试依恋行为。这是一个有点私生子,但会给你给予你想要的:

public static class DataGridAttachedProperty
{
     public static DataGrid _storedDataGrid;
     public static Boolean GetResortOnCollectionChanged(DataGrid dataGrid)
     {
         return (Boolean)dataGrid.GetValue(ResortOnCollectionChangedProperty);
     }

     public static void SetResortOnCollectionChanged(DataGrid dataGrid, Boolean value)
     {
         dataGrid.SetValue(ResortOnCollectionChangedProperty, value);
     }

    /// <summary>
    /// Exposes attached behavior that will trigger resort
    /// </summary>
    public static readonly DependencyProperty ResortOnCollectionChangedProperty = 
         DependencyProperty.RegisterAttached(
        "ResortOnCollectionChangedProperty", typeof (Boolean),
         typeof(DataGridAttachedProperty),
         new UIPropertyMetadata(false, OnResortOnCollectionChangedChange));

    private static void OnResortOnCollectionChangedChange
        (DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
      _storedDataGrid = dependencyObject as DataGrid;
      if (_storedDataGrid == null)
        return;

      if (e.NewValue is Boolean == false)
        return;

      var observableCollection = _storedDataGrid.ItemsSource as ObservableCollection;
      if(observableCollection == null)
        return;
      if ((Boolean)e.NewValue)
        observableCollection.CollectionChanged += OnCollectionChanged;
      else
        observableCollection.CollectionChanged -= OnCollectionChanged;
    }

    private static void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
      if (e.OldItems == e.NewItems)
        return;

      _storedDataGrid.Items.Refresh()
    }
}

然后,您可以通过以下方式连接它:

<DataGrid.Style>
  <Style TargetType="DataGrid">
    <Setter 
      Property="AttachedProperties:DataGridAttachedProperty.ResortOnCollectionChangedProperty" 
                                    Value="true" 
      />
   </Style>
 </DataGrid.Style>
f3temu5u

f3temu5u5#

对于任何有这个问题的人来说,这可能会让你开始...如果你有一个INotifyPropertyChanged项目的集合,你可以使用这个代替ObservableCollection -当集合中的单个项目发生变化时,它会刷新:注意:由于这标志着项目为删除然后重新读取(即使他们实际上并没有删除和添加,)选择可能会得到不同步.对于我的小型个人项目来说,它已经足够好了,但它还没有准备好向客户发布。

public class ObservableCollection2<T> : ObservableCollection<T>
{
    public ObservableCollection2()
    {
        this.CollectionChanged += ObservableCollection2_CollectionChanged;
    }

    void ObservableCollection2_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems != null)
            foreach (object o in e.OldItems)
                remove(o);
        if (e.NewItems != null)
            foreach (object o in e.NewItems)
                add(o);
    }
    void add(object o)
    {
        INotifyPropertyChanged ipc = o as INotifyPropertyChanged;
        if(ipc!=null)
            ipc.PropertyChanged += Ipc_PropertyChanged;
    }
    void remove(object o)
    {
        INotifyPropertyChanged ipc = o as INotifyPropertyChanged;
        if (ipc != null)
            ipc.PropertyChanged -= Ipc_PropertyChanged;
    }
    void Ipc_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyCollectionChangedEventArgs f;

        f = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, sender);
        base.OnCollectionChanged(f);
        f = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, sender);
        base.OnCollectionChanged(f);
    }
}

相关问题