ComboBox SelectedItem is unexpectedly set to null before the actual value

I have a ComboBox whose ItemSource is bound to a new (not default) ListCollectionView that is associated with an ObservableCollection. The ComboBox SelectedItem property is bound to the public SelectedHat property.

Step 1: Select the second item in the ComboBox. SelectedHat is now the second hat on the list, as expected. Step 2: (Press the button for) Set the second place in the list on the new hat. SelectedHat is null first, and then installs a new Hat.

Why is SelectedHat set to null before the new Hat?

I want to be able to vm.Collection [index] = new Hat () and
(1) if the ComboBox has this index, select it instead of going empty
(2) just set SelectedHat once on the new Hat instead of NULL and THEN the new Hat

WITH#:

public partial class MainWindow : Window
{
    private readonly ViewModel vm;

    public MainWindow()
    {
        InitializeComponent();
        vm = new ViewModel();
        DataContext = vm;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Hat item = new Hat { Name = "hat 2", Color = "Red"};
        vm.Collection[1] = item;
    }
}

public class ViewModel : BaseNotifyPropertyChanged
{
    public ObservableCollection<Hat> Collection { get; set; }
    public ListCollectionView View { get; set; }

    private Hat selectedHat;
    public Hat SelectedHat
    {
        get { return selectedHat; }
        set
        {
            selectedHat = value;
            Console.WriteLine(string.Format("SelectedHat set to [{0}]", value));
            NotifyPropertyChanged("SelectedHat");
        }
    }

    public ViewModel()
    {
        Collection = new ObservableCollection<Hat>()
        {
            new Hat { Name = "hat 1", Color = "Black" },
            new Hat { Name = "hat 2", Color = "Black" },
            new Hat { Name = "hat 3", Color = "Black" },
        };

        View = new ListCollectionView(Collection);
        View.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
    }
}

public class Hat
{
    public string Name { get; set; }
    public string Color { get; set; }

    public override string ToString()
    {
        return string.Format("{0} ({1})", Name, Color);
    }
}

public abstract class BaseNotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML:

<StackPanel>
    <TextBlock Text="{Binding Path=SelectedHat, Mode=OneWay}" />
    <ComboBox ItemsSource="{Binding Path=View}" SelectedItem="{Binding Path=SelectedHat, UpdateSourceTrigger=PropertyChanged}" />
    <Button Content="click me" Click="Button_Click" />
</StackPanel>
+2
source share
2 answers

This is an implementation of ObservableCollection.SetItem

protected override void SetItem(int index, T item)
{
  this.CheckReentrancy();
  T obj = this[index];
  base.SetItem(index, item);
  this.OnPropertyChanged("Item[]");
  this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, (object) obj, (object) item, index);
}

as you see, he raises OnPropertyChanged("Item[]")and then OnCollectionChanged(NotifyCollectionChangedAction.Replace, (object) obj, (object) item, index). OnCollectionChanged has the parameters "oldItem" and "newItem". I expect that if we trace the code to the combo box implementation, we will see that the old element has been deleted and replaced with zero, and then a new element is inserted, so you get the behavior that you experience (I also see it).

, , .

private void ButtonClick(object sender, System.Windows.RoutedEventArgs e)
{
    Hat newHat = new Hat { Name = "hat 2", Color = "Red" };

    var viewModel = (ViewModel)DataContext;

    var oldHat = viewModel.Collection[1];

    if (viewModel.SelectedHat == oldHat)
    {
        viewModel.Collection.Add(newHat);
        viewModel.SelectedHat = newHat;
        viewModel.Collection.Remove(oldHat);
    }
    else
    {
        viewModel.Collection[1] = newHat;
    }
}
+2

. . vm.Collection [1].name = "hat 2"

0

All Articles