it-swarm.com.ru

Пересортируйте WPF DataGrid после изменения ограниченных данных.

Я ищу способ повторной сортировки my DataGrid, когда базовые данные изменились .

(Настройка является достаточно стандартной: свойство DataGrid ItemSource привязано к ObservableCollection; столбцы являются DataGridTextColumns; данные внутри DataGrid корректно реагируют на изменения внутри ObservableCollection; сортировка работает нормально при щелчке мышью)

Есть идеи ?

26
marc wellman

Это заняло у меня целый день, но я, наконец, нашел решение которое удивительно simple, short и ffective:

Для управления поведением рассматриваемого элемента управления пользовательского интерфейса (здесь DataGrid) можно просто использовать CollectionViewSource. Он действует как своего рода представитель для элемента управления пользовательского интерфейса внутри вашей ViewModel, не нарушая полностью шаблон 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 приложения вам больше ничего не нужно делать, чтобы связать ItemsSourceCollectionControl со свойством View CollectionViewSource, а не напрямую с ObservableCollection:

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

С этого момента вы можете использовать объект CollectionViewSource в вашей ViewModel, чтобы напрямую манипулировать элементом управления UI в View.

Например, сортировка - как и было моей основной проблемой - будет выглядеть так:

// 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();

Видите ли, очень очень просто и интуитивно понятно. Надеюсь, что это помогает другим людям, как это помогло мне.

27
marc wellman

Это скорее для пояснения, чем для ответа, но WPF всегда связывается с ICollectionView, а не с исходной коллекцией. CollectionViewSource - это просто механизм, используемый для создания/получения представления коллекции.

Вот большой ресурс по этой теме, который должен помочь вам лучше использовать представления коллекций в WPF: http://bea.stollnitz.com/blog/?p=387

Использование CollectionViewSource в XAML может немного упростить ваш код:

<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>

Некоторые люди утверждают, что при следовании шаблону MVVM модель представления всегда должна предоставлять представление сбора, но, на мой взгляд, это зависит только от варианта использования. Если модель представления никогда не будет напрямую взаимодействовать с представлением коллекции, ее проще настроить в XAML.

17
sellmeadog
2
AnjumSKhan

Ответ Sellmeadog либо слишком сложен, либо устарел. Это супер просто. Все, что вам нужно сделать, это:

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

<DataGrid ItemsSource="{Binding Source={StaticResource MyKey} }" >...
1
Anthony Nichols

Я не вижу никаких очевидных простых способов, поэтому я бы попробовал присоединенное поведение. Это немного ублюдка, но даст вам то, что вы хотите:

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>
0
Justin Pihony

Для всех, у кого есть эта проблема, это может помочь вам начать ... Если у вас есть коллекция элементов INotifyPropertyChanged, вы можете использовать ее вместо ObservableCollection - она ​​будет обновляться при изменении отдельных элементов в коллекции: Note : поскольку это помечает элементы как удаленные, а затем повторно читаемые (даже если они на самом деле не удаляются и не добавляются), выборки могут быть не синхронизированы. Это достаточно хорошо для моих небольших личных проектов, но не готово к выпуску для клиентов ...

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);
    }
}
0
DanW