Подтвердить что ты не робот

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

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

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

Любые идеи?

4b9b3361

Ответ 1

Мне потребовался целый день, но я, наконец, нашел решение, которое удивительно просто, коротко и эффективно:

Чтобы управлять поведением элемента управления пользовательского интерфейса (здесь a 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;
}

Затем в части "Просмотр" приложения вам нечего делать, чтобы привязать ItemsSource к CollectionControl к представлению свойства CollectionViewSource, а не непосредственно к ObservableCollection:

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

С этого момента вы можете использовать объект CollectionViewSource в ViewModel для непосредственного управления элементом управления пользовательским интерфейсом в представлении.

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

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

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

Ответ 2

Это больше для пояснения, чем для ответа, но 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.

Ответ 4

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

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

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

Ответ 5

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

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>

Ответ 6

Для всех, у кого есть эта проблема, вы можете начать... Если у вас есть коллекция элементов 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);
    }
}