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

4

我有一个DataGrid,它绑定到我的视图模型中的ObservableCollection,当DataGrid被排序时,我需要能够对集合进行排序,以便我可以在其他地方使用这个排序顺序。目前,我正在使用ObvservableCollection上的包装器来支持排序。当DataGrid被排序时,它只对显示的数据进行排序,而不是对集合中的基础数据进行排序。数据由一个整数列和一个字符串列组成,并且需要支持升序和降序排序。我还想保持与标准DataGrid排序相同的用法,即点击列标题时,在升序和降序之间切换。我相对于WPF比较新,所以我不知道数据和命令绑定的所有细节,但我认为应该有一种方法来实现我想要做的事情。以下是一个示例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>

源数据的类型类似于这样:
public class myType
{
    public int firstValue { get; set; }
    public string secondValue { get; set; }

    // some functions and variables...
}

现在,正如我上面所说的,我需要按照其排序顺序访问集合中的项,但它不一定要特别是一个ObservableCollection。只要我能够按照当前顺序迭代访问集合中的项,那就没问题了。我想也许可以使用ListCollectionView或类似的东西。我也不希望当向集合添加新项时,集合重新进行排序。任何新项应该像通常情况下一样被添加到集合的末尾。
有什么好的想法吗?
4个回答

4

我知道这是一个老问题,但当我遇到类似的问题时,我很难找到答案,即我想要按照屏幕上显示的顺序打印/导出数据。尽管@sa_ddam213的答案是正确的,但需要进行一些调整以适应MVVM模式,因此希望这个答案对其他人有用。为了实现这一点,请首先向您的视图模型添加一个ICollectionView(我认为这是UI使用的相同数据结构):

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

然后为您的ICollectionView添加一个新的Getter/Setter:

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

在您的原始TableData Setter中添加一行代码,每次设置表格数据时都设置包装器:
public ObservableCollection<yourDataType> TableData
{
    get { return this.tableData; }
    set
    {
        this.tableData = value;
        this.TableDataWrapper = CollectionViewSource.GetDefaultView(tableData); // Add this
        OnPropertyChanged("TableData");
    }
}

现在更新DataGrid的绑定,使其绑定到TableDataWrapper而不是TableData。 现在可以按排序顺序访问数据了:

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

或者,如果您希望将其作为ObservableCollection:
new ObservableCollection( this.TableDataWrapper.Cast<yourDataType>().ToList<yourDataType>() )

4

DataGrid 使用基于 DataSource 的底层 ICollectionView,因此如果您直接绑定 ICollectionView,您可以访问排序后的值,因为 DataGrid 在排序时会直接更改 ICollectionView

小例子:

代码:

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:

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

1
我不得不稍微修改一下以适应我的MVVM设置,但我已经让它工作了。感谢您提供关于ICollectionView的提示。 - climbak

2
您可以使用ICollectionView对现有集合进行排序。它位于System.ComponentModel下。
代码如下:
var collectionView = CollectionViewSource.GetDefaultView(ObservableCollection_Name);
       collectionView.SortDescriptions.Add(new SortDescription("Your Property", ListSortDirection.Descending));

在你的GridView或ListView中,可以绑定现有的ObservableCollection。无需更改。


0

我尝试了John Cummings和stuicidle的解决方案。在XAML中,DataGrid没有显示任何内容。因为DataGrid的ItemsSource不是ObservableCollection。

在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));
            }
        }
    }

在XAML中:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接