WPF 对 ObservableCollection 进行排序会取消选择 ComboBox

WPF Sorting an ObservableCollection de-selects a ComboBox

我有一个 ComboBox,用户可以在其中 select 他们正在处理的 JobType。 ComboBox 有一个 AllJobTypes 列表。问题源于当用户添加新的 JobType 时,我将 JobType 添加到 AllJobTypes ObservableCollection,然后对其进行排序。当排序发生时,ComboBox 得到 de-selected 并且不确定为什么。 JobConfig.SelectedJobType.Name 在此过程中永远不会改变。有没有办法在不破坏 ComboBox 的情况下对可观察集合进行排序?

public class JobTypeList : ObservableCollection<JobType> {

  public void SortJobTypes() {
    var sortableList = new List<JobType>(this);
    sortableList.Sort((x, y) => x.Name.CompareTo(y.Name));
    //this works but it creates a bug in the select for JobTypes
    for (int i = 0; i < sortableList.Count; i++) {
        this.Move(this.IndexOf(sortableList[i]), i);
    }
  }

并且在 XAML

<ComboBox Grid.Column="0" SelectionChanged="JobTypeComboBox_SelectionChanged"
                              Name="JobTypeComboBox"
                              ItemsSource="{Binding Path=AllJobTypes}"
                              DisplayMemberPath="Name"
                              SelectedValuePath="Name"
                              SelectedValue="{Binding 
Path=JobConfig.SelectedJobType.Name}" />

您应该将 ComboBox 的 ItemsSource 绑定到 CollectionViewSource,而不是在视图模型中对集合进行排序,您可以在其中指定 SortDescription:

<Window ...
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    ...>
    <Window.Resources>
        <CollectionViewSource x:Key="cvs" Source="{Binding AllJobTypes}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="Name"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    ...
    <ComboBox ItemsSource="{Binding Source={StaticResource cvs}}"
              DisplayMemberPath="Name"
              SelectedValuePath="Name"
              SelectedValue="{Binding JobConfig.SelectedJobType.Name}"/>
    ...

</Window>

有关详细信息,请参阅 How to: Sort and Group Data Using a View in XAML

这是一个使用 ItemsSource/SelectedItem 的版本。请注意,您可以向列表中添加新项目并对其进行排序,而不会丢失视图中当前选定的项目。

window

<Window
    x:Class="SortingAList.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:SortingAList"
    mc:Ignorable="d"
    Title="MainWindow"
    Height="350"
    Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBox
            Text="{Binding NewJobType, Delay=1000}"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Width="200" />
        <ComboBox
            Grid.Row="1"
            ItemsSource="{Binding JobTypes}"
            SelectedItem="{Binding SelectedJobType}"
            DisplayMemberPath="Name"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Width="200" />
    </Grid>
</Window>

代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

public class Notifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void Notify([CallerMemberName]string property = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
}

public class ViewModel : Notifier
{
    private JobType _selectedJobType;
    private string _newJobType;

    public JobTypeList JobTypes { get; private set; } 
    public JobType SelectedJobType { get => _selectedJobType; set { _selectedJobType = value; Notify(); } }
    public string NewJobType { get => _newJobType; set { _newJobType = value; Notify(); AddNewJobType(value); } }
    public ViewModel()
    {
        JobTypes = new JobTypeList();
        JobTypes.Add(new JobType { Name = "Butcher" });
        JobTypes.Add(new JobType { Name = "Baker" });
        JobTypes.Add(new JobType { Name = "LED maker" });
    }

    private void AddNewJobType(string name)
    {
        if(JobTypes.Any(x => x.Name == name)) return;
        JobTypes.Add(new JobType { Name = name });
        JobTypes.SortJobTypes();
    }
}
public class JobType : Notifier
{
    private string _name;

    public string Name { get => _name; set { _name = value; Notify(); } }
}

使用您的 JobTypesList

public class JobTypeList : ObservableCollection<JobType>
{

    public void SortJobTypes()
    {
        var sortableList = new List<JobType>(this);
        sortableList.Sort((x, y) => x.Name.CompareTo(y.Name));
        //this works but it creates a bug in the select for JobTypes
        for(int i = 0; i < sortableList.Count; i++)
        {
            this.Move(this.IndexOf(sortableList[i]), i);
        }
    }
}