0

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.

IanHacker
  • 541
  • 9
  • 27
  • Possible duplicate of [how to sort ObservableCollection](https://stackoverflow.com/questions/7284805/how-to-sort-observablecollection) – Klaus Gütter Nov 24 '19 at 06:45

1 Answers1

2

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

BionicCode
  • 1
  • 4
  • 28
  • 44
  • The 2nd one perfectly works! (Adding a `SortDescription` to the `ICollectionView`) Thank you! However, the 1st one cannot be compiled (Overwriting the original collection with the sorted result collection). So, I replaced `` with `` and put the line after `Add(graphViewModel)`, but some items disappear. Sorry if my setting is wrong, but you might want to edit the 1st one. Again, thank you so much! – IanHacker Nov 24 '19 at 09:07
  • Thanks for your comment. I fixed the snippet (I somehow expected this to be a `string` collection without thinking much about it. My bad). But what do you mean by _"some items disappear"_? When using the `SortDescription`? – BionicCode Nov 24 '19 at 09:18
  • Sorry, "_disappear_" was wrong, but only one item can be moved to the right side, and then never move to the other side. It happens when _overwriting the original collection with the sorted result collection_. I put the line in this order: `LeftListBoxItems.Remove` -> `this.LeftListBoxItems = ...` -> `RightListBoxItems.Add` -> `this.RightListBoxItems = ...` You can try it on your side, if you want to. Anyway, your 2nd one works perfectly, so I can move on. Thank you for helping me! – IanHacker Nov 24 '19 at 09:32
  • 1
    Ok, now I understand. It's only one time because the view is not notified about the new collection. In this scenario not only the content of the `ObservableCollection` is changing (due to sorting) but the complete collection itself (the reference) is being replaced. To solve this, you can implement either `INotifyPropertyChanged` on the `MainWindow` or implement both collections as `DependencyProperty`. Consider to always make public properties on a control a `DependencyProperty`. This way when assigning a new collection instance to the properties the view will get notified and will update. – BionicCode Nov 24 '19 at 10:23
  • Ah, I see. Yes, it works with `INotifyPropertyChanged`. I will try `DependencyProperty` someday since I'm not familiar with it yet. ;-) Thank you sooo much! – IanHacker Nov 24 '19 at 11:12