为什么ComboBox在对ItemsSource进行排序时会丢失其SelectedItem?

时间:2017-02-13 13:11:35

标签: c# wpf sorting combobox observablecollection

考虑这个简单的例子:

MainWindow.xaml

<Window x:Class="WPF_Sandbox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        x:Name="ThisControl">
    <StackPanel>
        <ComboBox ItemsSource="{Binding Collection, ElementName=ThisControl}" SelectedItem="a" />
        <Button x:Name="SortButton">Sort</Button>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace WPF_Sandbox
{
    public partial class MainWindow
    {
        public ObservableCollection<string> Collection { get; } = new ObservableCollection<string>(new [] { "b", "a", "c" });

        public MainWindow()
        {
            InitializeComponent();

            SortButton.Click += (s, e) => Sort(Collection);
        }

        public static void Sort<T>(ObservableCollection<T> collection)
        {
            var sortableList = new List<T>(collection);
            sortableList.Sort();

            for (var i = 0; i < sortableList.Count; i++)
                collection.Move(collection.IndexOf(sortableList[i]), i);
        }
    }
}

启动程序时,会选择a。在按Sort时,选择不会改变,但列表会被排序(仍然如预期的那样) 如果您a)再次按Sort或b)在排序前选择bcComboBox将失去其选择,SelectedItem变为null

我将问题确定为ObservableCollection.Move方法。看来,只要您致Move(i, i)(所以实际上没有移动任何东西)iSelectedItem,选择就会变成地狱。

我不是在寻找解决方案。明显的解决方法是不对ObservableCollection进行排序,并使用CollectionViewSource或调整Sort方法仅在两个索引实际不同时调用Move

我的问题是,为什么这首先发生?在Move方法的文档中没有任何迹象表明您不能两次传递相同的参数。此外,没有提示为什么这不适用于CollectionChanged eventCollectionChangedEventArgs class的文档。这是WPF中的错误吗?

1 个答案:

答案 0 :(得分:0)

我认为这是ItemControl's事件处理实现中的一个错误。看看这里:

case NotifyCollectionChangedAction.Move:
    // items between New and Old have moved.  The direction and
    // exact endpoints depends on whether New comes before Old.
    int left, right, delta;
    if (e.OldStartingIndex < e.NewStartingIndex)
    {
        left = e.OldStartingIndex + 1;
        right = e.NewStartingIndex;
        delta = -1;
    }
    else
    {
        left = e.NewStartingIndex;
        right = e.OldStartingIndex - 1;
        delta = 1;
    }

    foreach (ItemInfo info in list)
    {
        int index = info.Index;
        if (index == e.OldStartingIndex)
        {
             info.Index = e.NewStartingIndex;
        }
        else if (left <= index && index <= right)
        {
             info.Index = index + delta;
        }
    }
break;

Source

if语句似乎不希望e.OldStartingIndexe.NewStartingIndex具有相同的值,导致delta1,导致某些foreach <div id="main"> <div class="inline-control-group"> <input type="radio" value="7" id="cf4all1" class="cf4all_radio" name="name1" disabled="disabled"> <label class="cf4all1" for="cf4all1"> <div class="cf4all1inner" style="background-color:#ff2962; width:100%;"> </div> </label> </div> <div class="inline-control-group"> <input type="radio" value="9" id="cf4all2" class="cf4all_radio" name="name2"> <label class="cf4all2" for="cf4all2"> <div class="cf4all2inner" style="background-color:#000000; width:100%;"></div></label> </div> </div> 循环内部的意外索引操作。我很惊讶它“只”取消选择该项目并没有完全破坏整个系列。