对 ListBox 中的 ObservableCollection<Class> 数据进行排序

Posted

技术标签:

【中文标题】对 ListBox 中的 ObservableCollection<Class> 数据进行排序【英文标题】:Sorting ObservableCollection<Class> data in ListBox 【发布时间】:2020-03-19 17:31:27 【问题描述】:

我的程序有两个 ListBox。他们通过AddRemove 按钮互相交换物品。在添加和删除期间,我想使用OrderBy 对项目进行排序(按ID),但我不知道如何使用此查询结果。在我的情况下如何使用OrderBy

似乎OrderBy 工作正常,但我就是不知道该放在哪里:

IEnumerable<GraphViewModel> query_orderBy_ID = RightListBoxItems.OrderBy(value => value.ID);
??? = query_orderBy_ID;

...好吧,那我把右手边拿回原来的集合里:

RightListBoxItems = (ObservableCollection<GraphViewModel>)RightListBoxItems.OrderBy(value => value.ID).ToList();

错误 CS0030:无法转换类型 'System.Collections.Generic.List' 到 'System.Collections.ObjectModel.ObservableCollection'

我认为强制转换可以解决这个问题,但它不起作用。

这是我的代码:

MainWindow.xaml.cs

已编辑:Add_Button_Click() 中添加了一个query_orderBy_ID 循环并使其更简单,抱歉:

using Sorting_List_with_OrderBy.ViewModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace Sorting_List_with_OrderBy

    public partial class MainWindow : Window
    
        public ObservableCollection<GraphViewModel> LeftListBoxItems  get; set; 
            = new ObservableCollection<GraphViewModel>();
        public ObservableCollection<GraphViewModel> RightListBoxItems  get; set; 
            = new ObservableCollection<GraphViewModel>();

        ObservableCollection<string> TestItems = new ObservableCollection<string>();
        IEnumerable<GraphViewModel> query_orderBy_ID;

        public MainWindow()
        
            InitializeComponent();

            DataContext = this;

            TestItems.Add("T0");
            TestItems.Add("T1");
            TestItems.Add("T2");

            foreach (var test in TestItems.Select((k, i) => new  kvp = k, index = i ))
            
                LeftListBoxItems.Add(new GraphViewModel(test.index, test.kvp));
            
        

        private void Add_Button_Click(object sender, RoutedEventArgs e)
        
            foreach (var graphViewModel in LeftListBox.SelectedItems.Cast<GraphViewModel>().ToList())
            
                LeftListBoxItems.Remove(graphViewModel);

                // 1st Attempt: I don't know where to put this result to ...
                query_orderBy_ID = RightListBoxItems.OrderBy(value => value.ID);

                // 2nd Attempt: This substitution fails ...
                //RightListBoxItems = (ObservableCollection<GraphViewModel>)RightListBoxItems.OrderBy(value => value.ID).ToList();

                RightListBoxItems.Add(graphViewModel);
            
            query_orderBy_ID = RightListBoxItems.OrderBy(value => value.ID);
            foreach (GraphViewModel orderBy_ID in query_orderBy_ID)
            
                MessageBox.Show(orderBy_ID.ID + ": " + orderBy_ID.TestName);
            
        

        private void Remove_Button_Click(object sender, RoutedEventArgs e)
        
            foreach (var graphViewModel in RightListBox.SelectedItems.Cast<GraphViewModel>().ToList())
            
                RightListBoxItems.Remove(graphViewModel);
                LeftListBoxItems.Add(graphViewModel);
            
        

    

MainWindow.xaml

<Window x:Class="Sorting_List_with_OrderBy.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Sorting_List_with_OrderBy"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="600">
    <Grid>
        <ListBox Margin="15,158,0,69.8" x:Name="LeftListBox" HorizontalAlignment="Left" Width="184" Background="AntiqueWhite"
                 SelectionMode="Extended" ItemsSource="Binding LeftListBoxItems" DisplayMemberPath="TestName"/>

        <Button x:Name="Add_Button" Height="26" Margin="232,196,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="80"
                 Click="Add_Button_Click" Content="Add &gt;&gt;"/>
        <Button x:Name="Remove_Button" Margin="230,267,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="82"
                 Click="Remove_Button_Click" Height="26" Content="&lt;&lt; Remove"/>

        <ListBox Margin="343,158,0,71.8" x:Name="RightListBox" HorizontalAlignment="Left" Width="200" Background="AntiqueWhite"
                 SelectionMode="Extended" ItemsSource="Binding RightListBoxItems" DisplayMemberPath="TestName"/>
    </Grid>
</Window>

ViewModel/GraphViewModel.cs

namespace Sorting_List_with_OrderBy.ViewModel

    public class GraphViewModel
    
        public int ID  get; 
        public string TestName  get; 

        public GraphViewModel(int id, string testName) => (ID, TestName) = (id, testName);
    

这个问题的主要目的是对我的ListBoxes进行排序,所以ListBox项应该按照正确的顺序排序[T0T1T2]。如果上面的问题解决了,我相信他们会被排序的。

任何建议都会有所帮助。谢谢。

【问题讨论】:

how to sort ObservableCollection的可能重复 【参考方案1】:

OrderBy 将返回一个IOrderedEnumerable&lt;T&gt;,它根据排序键排序。原始集合保持其原始状态。因此,您需要用排序结果集合覆盖原始集合:

this.LeftListBoxItems = new ObservableCollection<GraphViewModel>(this.LeftListBoxItems.OrderBy(value => value.ID))

要让ObservableCollection 在添加/删除项目时自动排序,您可以通过将SortDescription 添加到ICollectionView 来配置一次(例如,从构造函数中):

CollectionViewSource.GetDefaultView(this.LeftListBoxItems)
  .SortDescriptions
  .Add(new SortDescription(nameof(GraphViewModel.ID), ListSortDirection.Ascending));

注意 WPF 绑定到集合的ICollectionView,而不是直接绑定到集合。在ICollectionView 上设置SortDescription 只会对ICollectionView 进行排序。这意味着 UI 显示为已排序,而基础集合保持未排序(或按不同规则排序)。

【讨论】:

第二个完美运行! (在ICollectionView 中添加SortDescription)谢谢!但是,第一个无法编译(用排序结果集合覆盖原始集合)。因此,我将&lt;string&gt; 替换为&lt;GraphViewModel&gt; 并将行放在Add(graphViewModel) 之后,但有些项目消失了。抱歉,如果我的设置有误,但您可能想编辑第一个。再次,非常感谢! 感谢您的评论。我修复了 sn-p(我不知何故希望这是一个 string 集合,但没有考虑太多。我的错)。但是你说的“一些项目消失了”是什么意思?当使用SortDescription? 对不起,“disappear”是错误的,但只能将一个项目移到右侧,然后永远不能移到另一侧。当用排序结果集合覆盖原始集合时会发生这种情况。我按以下顺序排列:LeftListBoxItems.Remove -> this.LeftListBoxItems = ... -> RightListBoxItems.Add -> this.RightListBoxItems = ... 如果您愿意,可以尝试一下。无论如何,你的第二个工作完美,所以我可以继续。谢谢你帮助我! 好的,现在我明白了。这只是一次,因为视图没有收到有关新集合的通知。在这种情况下,不仅ObservableCollection 的内容发生了变化(由于排序),而且整个集合本身(引用)也被替换了。为了解决这个问题,您可以在MainWindow 上实现INotifyPropertyChanged,或者将两个集合都实现为DependencyProperty。考虑始终将控件上的公共属性设为DependencyProperty。这样,当将新的集合实例分配给属性时,视图将收到通知并进行更新。 啊,我明白了。是的,它适用于INotifyPropertyChanged。有一天我会尝试DependencyProperty,因为我还不熟悉它。 ;-) 非常感谢!

以上是关于对 ListBox 中的 ObservableCollection<Class> 数据进行排序的主要内容,如果未能解决你的问题,请参考以下文章

C# winform中关于两个ListBox清除selectIndex的问题。

VB中的list控件是啥?

VBA中如何使用ListBox控件呢?

vb comboBOx和listBox两个属性有啥不同?

使用 SelectionMode = Multiple 对 ListBox 进行数据绑定

ListBox 不显示对 DataSource 的更改