WPF Sorting an ObservableCollection de-selects a ComboBox

前端 未结 2 1518
隐瞒了意图╮
隐瞒了意图╮ 2020-12-22 12:43

I have a ComboBox where a user can select what JobType they are working on. The ComboBox has a list of AllJobTypes. The problem stems from when a user adds a new JobType,

相关标签:
2条回答
  • 2020-12-22 13:12

    Here's a version using ItemsSource/SelectedItem. Note that you can add a new item to the list and sort it without losing the currently selected item in the view.

    The 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>
    

    The code

    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(); } }
    }
    

    Using your 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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-22 13:33

    Instead of sorting the collection in the view model, you should bind the ComboBox's ItemsSource to a CollectionViewSource, where you can specify a 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>
    

    For further information see How to: Sort and Group Data Using a View in XAML

    0 讨论(0)
提交回复
热议问题