简体   繁体   English

在 MVVM 中对绑定到 DataGrid 的 ObservableCollection 进行排序

[英]Sort ObservableCollection bound to DataGrid in MVVM

I have a DataGrid that I'm binding to an ObservableCollection in my view model and I need to be able to sort the collection when the DataGrid is sorted to so I can use this sorted order elsewhere.我有一个 DataGrid 绑定到我的视图模型中的 ObservableCollection 并且我需要能够在 DataGrid 排序时对集合进行排序,以便我可以在其他地方使用这个排序顺序。 I'm currently using a wrapper on the ObvservableCollection to support sorting.我目前在 ObvservableCollection 上使用包装器来支持排序。 When the DataGrid is sorted it only sorts the displayed data and not the underlying data in the collection.对 DataGrid 进行排序时,它仅对显示的数据进行排序,而不对集合中的基础数据进行排序。 The data consists of one integer column and one string column and needs to support ascending and descending sort on both.数据由1个整数列和1个字符串列组成,需要同时支持升序和降序。 I also want to maintain the same usage as the standard DataGrid sort where you click a column header and it toggles between ascending and descending sort.我还希望保持与标准 DataGrid 排序相同的用法,在该排序中单击列标题并在升序和降序排序之间切换。 I'm relatively new to WPF so I don't know all the ins and outs of data and command binding, but I would think there would be a way to accomplish what I want to do.我对 WPF 比较陌生,所以我不知道数据和命令绑定的所有来龙去脉,但我认为会有一种方法可以完成我想做的事情。 Here is a sample xaml to illustrate my view set up.这是一个示例 xaml 来说明我的视图设置。

<DataGrid ItemsSource="{Binding mySource}" 
          Name="myDataGrid"
          CanUserResizeRows="False"
          AutoGenerateColumns="False"
          HeadersVisibility="Column" >
    <DataGrid.Columns>

      <DataGridTemplateColumn Header="Header 1" CanUserSort="True" SortMemberPath="firstValue">
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding firstValue}" />
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
      </DataGridTemplateColumn>

      <DataGridTemplateColumn Header="Header 2" Width="*" CanUserSort="True" SortMemberPath="secondValue">
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding secondValue}" />
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
      </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>

The source data is of a type something like:源数据的类型类似于:

public class myType
{
    public int firstValue { get; set; }
    public string secondValue { get; set; }

    // some functions and variables...
}

Now, like I said above, I need access to the items in the collection in their sorted order, but it does not need to specifically be an ObservableCollection.现在,就像我上面说的,我需要按排序顺序访问集合中的项目,但它不需要特别是 ObservableCollection。 As long as I can iterate through the items in the collection in whatever the current order is when I access them, all is good.只要我可以在访问集合中的项目时以当前的顺序遍历它们,一切都很好。 I was thinking maybe a ListCollectionView or something.我在想也许是 ListCollectionView 之类的。 I also don't want the collection to re-sort itself when new items are added to the collection.当新项目添加到集合中时,我也不希望集合重新排序。 Any new items should just be added to the end of the collection as would normally happen.任何新项目都应该像通常那样添加到集合的末尾。

Any ideas?有任何想法吗?

The DataGrid uses an underlying ICollectionView based on the DataSource, so if you directly bind a ICollectionView you can access the sorted values as the DataGrid will directly change the ICollectionView when sorting. DataGrid使用基于 DataSource 的底层ICollectionView ,因此如果您直接绑定ICollectionView您可以访问排序后的值,因为DataGrid在排序时会直接更改ICollectionView

Small Example:小例子:

Code:代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        // dummy data source / Your ObservableCollection
        var yourObservableCollection = Enumerable.Range(0, 30)
            .Select(i => new MyType { FirstValue = i, SecondValue = i.ToString() });

        // Create CollectionView based on the data you want to show
        MySource = CollectionViewSource.GetDefaultView(yourObservableCollection);
        InitializeComponent();
    }

    public ICollectionView MySource { get; set; }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        foreach (var item in MySource.OfType<MyType>())
        {
            Console.WriteLine("MyType - First: {0}, Second: {1}",
                item.FirstValue,
                item.SecondValue);
        }
    }
}

public class MyType
{
    public int FirstValue { get; set; }
    public string SecondValue { get; set; }
}

Xaml: Xml:

<Window x:Class="WpfApplication8.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"`enter code here` Name="UI" Width="262" Height="318">
  <Grid DataContext="{Binding ElementName=UI}">
    <DataGrid Name="myDataGrid"
              ItemsSource="{Binding MySource}"
              CanUserSortColumns="True"
              Margin="0,0,0,37" />
    <Button Content="Print To Output Window"
            HorizontalAlignment="Left"
            VerticalAlignment="Bottom"
            Margin="10,0,0,10"
            Width="144"
            Click="Button_Click_1"/>
  </Grid>
</Window>

I know this is an old question but I struggled to find an answer when I had a similar issue where I wanted to print/export the data in the same order it was displayed on the screen.我知道这是一个老问题,但是当我遇到类似的问题时,我想按照屏幕上显示的相同顺序打印/导出数据时,我很难找到答案。 Although @sa_ddam213 answer is correct it needs a bit of adjusting to fit the MVVM pattern, so hopefully this answer is of use to somebody else.尽管@sa_ddam213 答案是正确的,但它需要进行一些调整以适应 MVVM 模式,因此希望此答案对其他人有用。 To achieve this first add to your View Model an ICollectionView (I believe this is the same data structure used by the UI):要实现这一点,首先向您的视图模型添加一个ICollectionView (我相信这与 UI 使用的数据结构相同):

private ObservableCollection<yourDataType> tableData = new ObservableCollection<yourDataType>();    // The DataGridknow  was originally bound to this
private ICollectionView tableDataWrapper;

Then add a new Getter/Setter for your ICollectionView:然后为您的 ICollectionView 添加一个新的 Getter/Setter:

public ICollectionView TableDataWrapper
{
    get { return this.tableDataWrapper; }
    set
    {
        this.tableDataWrapper = value;
        OnPropertyChanged("TableDataWrapper");
    }
}

And in your original TableData Setter add a line to set the wrapper each time the table data is set:并在您的原始 TableData Setter 中添加一行以在每次设置表数据时设置包装器:

public ObservableCollection<yourDataType> TableData
{
    get { return this.tableData; }
    set
    {
        this.tableData = value;
        this.TableDataWrapper = CollectionViewSource.GetDefaultView(tableData); // Add this
        OnPropertyChanged("TableData");
    }
}

Now update the DataGrid's Bindings so it is bound to TableDataWrapper rather than TableData Now to access the data in the sorted order:现在更新 DataGrid 的绑定,使其绑定到TableDataWrapper而不是TableData Now 以按排序顺序访问数据:

this.TableDataWrapper.Cast<yourDataType>().ToList<yourDataType>()

or if you want it as an ObservableCollection :或者如果你想要它作为ObservableCollection

new ObservableCollection( this.TableDataWrapper.Cast<yourDataType>().ToList<yourDataType>() )

You can use ICollectionView to sort an existing collection.您可以使用 ICollectionView 对现有集合进行排序。 It is found under System.ComponentModel它位于 System.ComponentModel 下

The code goes like this代码是这样的

var collectionView = CollectionViewSource.GetDefaultView(ObservableCollection_Name);
       collectionView.SortDescriptions.Add(new SortDescription("Your Property", ListSortDirection.Descending));

In your GridView or ListView you can bind the existing ObservableCollection.在您的 GridView 或 ListView 中,您可以绑定现有的 ObservableCollection。 No need to change.没有必要改变。

I've tried the solution of John Cummings & stuicidle.我已经尝试过 John Cummings & stuicidle 的解决方案。 DataGrid in XAML does not show anything. XAML 中的 DataGrid 不显示任何内容。 Because the ItemsSource of the DataGrid is not ObservableCollection.因为 DataGrid 的 ItemsSource 不是 ObservableCollection。

In ViewModel:在视图模型中:

    public ICollectionView DataGridWraper
    {
        get { return _dataGridWraper; }
        set
        {
            if (_dataGridWraper != value)
            {
                _dataGridWraper = value;
                OnPropertyChanged(nameof(DataGridWraper));
            }
        }
    }
    public ObservableCollection<Customer> Customers
    {
        get { return _customers; }
        set
        {
            if (_customers != value)
            {
                _customers = value;
                DataGridWraper = CollectionViewSource.GetDefaultView(_customer);
                OnPropertyChanged(nameof(Customers));
            }
        }
    }

In XAML:在 XAML 中:

    <DataGrid
        ItemsSource="{Binding DataGridWraper, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
        </DataGrid.Columns>
    </DataGrid>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM