组合框的的SelectedItem意外设置为空之前,实际值 [英] ComboBox's SelectedItem is unexpectedly set to null before actual value

查看:148
本文介绍了组合框的的SelectedItem意外设置为空之前,实际值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个组合框,它的ItemsSource绑定到一个新的(不是默认)的ListCollectionView,它链接到一个ObservableCollection。组合框SelectedItem属性绑定到公共SelectedHat属性。

第1步:选择ComboBox中第二个项目。 SelectedHat现在是在列表中的第2帽,如预期。
步骤2:(点击按钮)在列表中的第2个点设置为一顶新帽子。 SelectedHat首先设置为空,则设置为新帽子。

为什么新帽子之前SelectedHat设置为空?

我希望能够vm.Collection [索引] =新帽子()和结果
(1)如果ComboBox的指数选择了,保持它选择的,而不是一片空白结果
(2)只设置SelectedHat一次新的帽子,而不是空,然后将新帽子

C#:

 公共部分类主窗口:窗口
{
    私人只读视图模型VM;    公共主窗口()
    {
        的InitializeComponent();
        VM =新视图模型();
        的DataContext = VM;
    }    私人无效Button_Click(对象发件人,RoutedEventArgs E)
    {
        帽子项目=新帽子{名称=帽子2,颜色=红色};
        vm.Collection [1] =项目;
    }
}公共类视图模型:BaseNotifyPropertyChanged
{
    公众的ObservableCollection<帽子和GT;集合{搞定;组; }
    公众的ListCollectionView查看{搞定;组; }    私人帽子selectedHat;
    公共帽子SelectedHat
    {
        {返回selectedHat; }
        组
        {
            selectedHat =价值;
            Console.WriteLine(的String.Format(SelectedHat设置为[{0}],值));
            NotifyPropertyChanged(SelectedHat);
        }
    }    公共视图模型()
    {
        集合=新的ObservableCollection<帽子和GT;()
        {
            顶新帽子{名称=帽子1,颜色=黑色},
            顶新帽子{名称=帽子2,颜色=黑色},
            顶新帽子{名称=帽子3,颜色=黑色},
        };        查看=新的ListCollectionView(集合);
        View.SortDescriptions.Add(新SortDescription(名,ListSortDirection.Ascending));
    }
}公共类帽子
{
    公共字符串名称{;组; }
    公共串色{搞定;组; }    公共重写字符串的ToString()
    {
        返回的String.Format({0}({1}),名称,颜色);
    }
}公共抽象类BaseNotifyPropertyChanged:INotifyPropertyChanged的
{
    公共事件PropertyChangedEventHandler的PropertyChanged;    受保护的虚拟无效NotifyPropertyChanged(字符串propertyName的)
    {
        如果(的PropertyChanged!= NULL)
            的PropertyChanged(这一点,新PropertyChangedEventArgs(propertyName的));
    }
}

XAML:

 <&StackPanel的GT;
    < TextBlock的文本={绑定路径= SelectedHat,模式=单向}/>
    <组合框的ItemsSource ={绑定路径=查看}的SelectedItem ={绑定路径= SelectedHat,UpdateSourceTrigger =的PropertyChanged}/>
    <按钮内容=点击我点击=Button_Click/>
< / StackPanel的>


解决方案

这是ObservableCollection.SetItem实施

 保护覆盖无效SetItem(INT指数,T项)
{
  this.CheckReentrancy();
  ŧ的obj =本[指数]
  base.SetItem(索引项);
  this.OnPropertyChanged(项目[]);
  this.OnCollectionChanged(NotifyCollectionChangedAction.Replace,(对象)目标文件(对象)项指数);
}

,你可以看到它提出了 OnPropertyChanged(项目[])然后 OnCollectionChanged(NotifyCollectionChangedAction.Replace,(对象)目标文件,(对象)项指数)
OnCollectionChanged有参数'oldItem'和'的newitem。我希望,如果我们通过对组合框实现,我们会看到旧的已删除项目和空替代,然后将新的项目插入,这就是为什么你得到你遇到的行为(我看追查code它)。

我的解决办法是,而不是替换的项目,添加新项目,改变当前选择的项目,然后删除旧的项目。

 私人无效ButtonClick(对象发件人,System.Windows.RoutedEventArgs E)
{
    帽子newHat =新帽子{名称=帽子2,颜色=红色};    VAR视图模型=(视图模型)的DataContext;    变种oldHat = viewModel.Collection [1];    如果(viewModel.SelectedHat == oldHat)
    {
        viewModel.Collection.Add(newHat);
        viewModel.SelectedHat = newHat;
        viewModel.Collection.Remove(oldHat);
    }
    其他
    {
        viewModel.Collection [1] = newHat;
    }
}

I have a ComboBox, whose ItemsSource is bound to a new (not default) ListCollectionView, which is linked to an ObservableCollection. The ComboBox SelectedItem property is bound to a public SelectedHat property.

Step 1: Select the 2nd item in the ComboBox. SelectedHat is now the 2nd Hat in the list, as expected. Step 2: (Click the button to) Set the 2nd spot in the list to a new Hat. SelectedHat is first set to null, then set to the 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 that index selected, keep it selected instead of going blank
(2) only set SelectedHat once to the new Hat, instead of null and THEN the new Hat

C#:

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>

解决方案

This is the 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 can see it raises OnPropertyChanged("Item[]") and then OnCollectionChanged(NotifyCollectionChangedAction.Replace, (object) obj, (object) item, index). OnCollectionChanged has parameters 'oldItem' and 'newItem'. I expect if we traced the code through to the combo box implementation we would see that the old item was removed and replaced with null, and then the new item was inserted, which is why you get the behaviour your experiencing (I see it as well).

My work around is instead of replacing the item, add the new item, change the currently selected item and then remove the old item.

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

这篇关于组合框的的SelectedItem意外设置为空之前,实际值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆