Search code examples
c#wpfmvvm

How does this actually reorder items in the list?


I was searching for a way to reorder items in a wpf list using MVVM. I found those two code sample which look amazing:

I studied the code and understood how it use the ItemsReorderedEvent to notify whoever wants to handle it that the items in the ListBox have been reordered.

I just don't get how it does reorder.. In fact, I've tried the code in a simple application and, even if I handle the event, nothing in my UI actually moves in my collection.

I was supposing that I needed to add code in the OnPreviewMouseLeftButtonUp. Playing with the originalItem, its index and the new index.

Since the code was already all written, I was wondering why I needed to complete it. And if I'm wrong and it actually does not require any more code from me, what am I missing?

Here is the code of my simple application:

    public partial class MainWindow : Window
{
    public ObservableCollection<String> list { get; set; }

    public MainWindow()
    {
        DataContext = this;
        list = new ObservableCollection<string>();
        list.Add("one");
        list.Add("two");
        list.Add("three");

        InitializeComponent();
    }

    private void ReorderableListBox_ItemsReordered(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("sdags");
    }
}

and the xaml:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ReorderableListBoxProject_XavText" x:Class="ReorderableListBoxProject_XavText.MainWindow"
    Title="MainWindow" Height="350" Width="525">
<Grid>

    <local:ReorderableListBox ItemsSource="{Binding list}"  ItemsReordered="ReorderableListBox_ItemsReordered"/>

</Grid>

EDIT

Here it is: enter image description here


Solution

  • Probably because it expects you to do it manually,

    A while ago I needed the same thing and ended up writing a small one.

    There's no whistles and bells in it but it does its job.

    enter image description here

    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace ReorderableListBox
    {
        public class ReorderableListBox : ListBox
        {
            private bool _canDrag;
            private int _sourceIndex;
            private int _targetIndex;
    
            protected override void OnSelectionChanged(SelectionChangedEventArgs e)
            {
                base.OnSelectionChanged(e);
    
                if (Items == null || SelectedItem == null) return;
                if (_canDrag)
                    _sourceIndex = Items.IndexOf(SelectedItem);
    
                if (_targetIndex == -1 || _sourceIndex == -1 || !_canDrag) return;
                var item1 = (ListBoxItem)Items[_targetIndex];
                var item2 = (ListBoxItem)Items[_sourceIndex];
                object content1 = item1.Content;
                object content2 = item2.Content;
                item1.Content = content2;
                item2.Content = content1;
    
                _canDrag = false;
            }
    
            protected override void OnPreviewMouseMove(MouseEventArgs e)
            {
                base.OnPreviewMouseMove(e);
    
                var canDrag = e.LeftButton == MouseButtonState.Pressed;
                if (canDrag && Items != null && SelectedItem != null)
                    _targetIndex = Items.IndexOf(SelectedItem);
                _canDrag = canDrag;
            }
        }
    }
    

    As you can see it's very simple but effective, feel free to add Adorner, previews etc ..

    EDIT

    You need to wrap items in a ListBoxItem

    IEnumerable<int> enumerable = Enumerable.Range(0, 10);
    foreach (int i in enumerable)
    {
        ListBox1.Items.Add(new ListBoxItem {Content = i});
    }
    

    I've tried to upgrade the code by removing the cast to allow any type of object, unfortunately it doesn't works reliably, one would need more time to fix it. (I don't have that time)