【问题标题】:Sort ObservableCollection bound to DataGrid in MVVM在 MVVM 中对绑定到 DataGrid 的 ObservableCollection 进行排序
【发布时间】:2013-08-30 19:36:51
【问题描述】:

我有一个 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 之类的。当新项目添加到集合中时,我也不希望集合重新排序。任何新项目都应该像往常一样添加到集合的末尾。

有什么想法吗?

【问题讨论】:

    标签: c# wpf mvvm datagrid observablecollection


    【解决方案1】:

    我已经尝试过 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>
    

    【讨论】:

      【解决方案2】:

      我知道这是一个老问题,但是当我遇到类似的问题时,我想按屏幕上显示的相同顺序打印/导出数据,我很难找到答案。尽管@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>() )
      

      【讨论】:

        【解决方案3】:

        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>
        

        【讨论】:

        • 我不得不稍微修改一下以在我的 mvvm 设置中工作,但我得到了它的工作。感谢您提供有关 ICollectionView 的提示。
        【解决方案4】:

        您可以使用 ICollectionView 对现有集合进行排序。它位于 System.ComponentModel 下

        代码是这样的

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-18
          • 2012-03-09
          • 1970-01-01
          • 2011-06-13
          • 2016-08-28
          • 1970-01-01
          • 1970-01-01
          • 2021-08-31
          相关资源
          最近更新 更多