2017-02-13 3 views
0

Рассмотрим простой пример:Почему ComboBox потеряет свой SelectedItem при сортировке ItemsSource?

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 выбор не изменяется, но список сортируется (по-прежнему, как и ожидалось).
Если вы а) еще раз нажмите Sort или б) выбрать b или c до сортировки, то ComboBox теряет свой выбор и SelectedItem становится null.

Я определил проблему до метода ObservableCollection.Move. Похоже, что когда вы вызываете Move(i, i) (так что вы ничего не двигаете), i является SelectedItem, выбор идет в ад.

Я не ищу решение. Очевидным обходным решением было бы не сортировать ObservableCollection и использовать CollectionViewSource или настроить метод Sort только для звонка Move, когда два индекса фактически отличаются.

Вопрос, который у меня есть, почему это происходит в первую очередь? В документации для метода Move нет указаний на то, что вы не должны передавать один и тот же параметр дважды. Также нет намека на то, почему это не будет работать в документации для CollectionChanged event или CollectionChangedEventArgs class. Это ошибка в WPF?

ответ

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.OldStartingIndex и e.NewStartingIndex быть одного и того же значения, что приводит к delta будучи 1, которая затем вызывает некоторые непреднамеренные манипуляции индекса внутри из foreach петля. Я удивлен, что «только» отменяет выбор предмета, а не полностью разрушает всю коллекцию.

Смежные вопросы