IanHacker
IanHacker

Reputation: 581

Sorting ObservableCollection<Class> data in ListBox

My program has two ListBoxes. They exchange their items each other by Add and Remove buttons. During the addition and the removal, I'd like to sort the items (by the IDs), using OrderBy, but I don't know how to use this query result. How can I use OrderBy in my case?

enter image description here

It seems that OrderBy is working, but I just don't know where to put:

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

... OK, then I took the right-hand side and put it back into the original collection:

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

error CS0030: Cannot convert type 'System.Collections.Generic.List' to 'System.Collections.ObjectModel.ObservableCollection'

I thought that casting would solve this problem, but it doesn't work.

Here is my code:

MainWindow.xaml.cs

EDITED: Added a query_orderBy_ID loop in Add_Button_Click() and made it simpler, sorry:

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

The main purpose of this question is to sort my ListBoxes, so the ListBox items should be sorted in the right order [T0, T1, T2]. I believe they will be sorted, if the above problem is solved.

Any suggestion would be helpful. Thank you.

Upvotes: 0

Views: 301

Answers (1)

BionicCode
BionicCode

Reputation: 28968

OrderBy will return a IOrderedEnumerable<T> which is sorted according to the sort key. The original collection remains in its original state. You therefore need to overwrite the original collection with the sorted result collection:

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

To have the ObservableCollection sort automatically when adding/removing items, you can configure it once (e.g., from the constructor) by adding a SortDescription to the ICollectionView:

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

Note
WPF is binding to the ICollectionView of a collection rather than to the collection directly. Setting the SortDescription on a ICollectionView will only sort this ICollectionView. This means the UI appears sorted whereas the underlying collection remains unsorted (or sorted by different rules).

Upvotes: 2

Related Questions