ComboBox ItemsSource已更改=> SelectedItem毁了 [英] ComboBox ItemsSource changed => SelectedItem is ruined

查看:102
本文介绍了ComboBox ItemsSource已更改=> SelectedItem毁了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,这已经困扰了我一段时间了。而且我想知道其他人如何处理以下情况:

Ok, this has been bugging me for a while now. And I wonder how others handle the following case:

<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}"/>

DataContext对象的代码:

The DataContext object's code:

public ObservableCollection<MyItem> MyItems { get; set; }
public MyItem SelectedItem { get; set; }

public void RefreshMyItems()
{
    MyItems.Clear();
    foreach(var myItem in LoadItems()) MyItems.Add(myItem);
}

public class MyItem
{
    public int Id { get; set; }
    public override bool Equals(object obj)
    {
        return this.Id == ((MyItem)obj).Id;
    }
}

很明显,当 RefreshMyItems( )方法被称为组合框,它接收Collection Change事件,更新其项,并且在刷新的collection中找不到SelectedItem =>将SelectedItem设置为 null 。但是我需要组合框使用等于方法在新集合中选择正确的项目。

Obviously when the RefreshMyItems() method is called the combo box receives the Collection Changed events, updates its items and does not find the SelectedItem in the refreshed collection => sets the SelectedItem to null. But I would need the combo box to use Equals method to select the correct item in the new collection.

在换句话说-ItemsSource集合仍包含正确的 MyItem ,但这是一个 new 对象。而且我希望组合框使用类似 Equals 的东西来自动选择它(这变得更加困难,因为首先源集合调用 Clear()会重置集合,并已将SelectedItem设置为 null )。

In other words - the ItemsSource collection still contains the correct MyItem, but it is a new object. And I want the combo box to use something like Equals to select it automatically (this is made even harder because first the source collection calls Clear() which resets the collection and already at that point the SelectedItem is set to null).

更新2 ,在复制粘贴下面的代码之前,请注意,这还远非完美!并且请注意,默认情况下它不绑定两种方式。

UPDATE 2 Before copy-pasting the code below please note that it is far from perfection! And note that it does not bind two ways by default.

更新以防万一有人遇到相同的问题(由Pavlo Glazkov在他的回答中):

UPDATE Just in case someone has the same problem (an attached property as proposed by Pavlo Glazkov in his answer):

public static class CBSelectedItem
{
    public static object GetSelectedItem(DependencyObject obj)
    {
        return (object)obj.GetValue(SelectedItemProperty);
    }

    public static void SetSelectedItem(DependencyObject obj, object value)
    {
        obj.SetValue(SelectedItemProperty, value);
    }

    // Using a DependencyProperty as the backing store for SelectedIte.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(CBSelectedItem), new UIPropertyMetadata(null, SelectedItemChanged));


    private static List<WeakReference> ComboBoxes = new List<WeakReference>();
    private static void SelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ComboBox cb = (ComboBox) d;

        // Set the selected item of the ComboBox since the value changed
        if (cb.SelectedItem != e.NewValue) cb.SelectedItem = e.NewValue;

        // If we already handled this ComboBox - return
        if(ComboBoxes.SingleOrDefault(o => o.Target == cb) != null) return;

        // Check if the ItemsSource supports notifications
        if(cb.ItemsSource is INotifyCollectionChanged)
        {
            // Add ComboBox to the list of handled combo boxes so we do not handle it again in the future
            ComboBoxes.Add(new WeakReference(cb));

            // When the ItemsSource collection changes we set the SelectedItem to correct value (using Equals)
            ((INotifyCollectionChanged) cb.ItemsSource).CollectionChanged +=
                delegate(object sender, NotifyCollectionChangedEventArgs e2)
                    {
                        var collection = (IEnumerable<object>) sender;
                        cb.SelectedItem = collection.SingleOrDefault(o => o.Equals(GetSelectedItem(cb)));
                    };

            // If the user has selected some new value in the combo box - update the attached property too
            cb.SelectionChanged += delegate(object sender, SelectionChangedEventArgs e3)
                                       {
                                           // We only want to handle cases that actually change the selection
                                           if(e3.AddedItems.Count == 1)
                                           {
                                               SetSelectedItem((DependencyObject)sender, e3.AddedItems[0]);
                                           }
                                       };
        }

    }
}


推荐答案

标准 ComboBox 没有这种逻辑。正如您提到的,在调用清除 SelectedItem 变为 null >,因此 ComboBox 不知道您打算以后添加相同的项目,因此不会选择它。话虽这么说,您将必须手动记住先前选择的项目,并且在更新集合后还必须手动恢复选择。通常这样做是这样的:

The standard ComboBox doesn't have that logic. And as you mentioned SelectedItem becomes null already after you call Clear, so the ComboBox has no idea about you intention to add the same item later and therefore it does nothing to select it. That being said, you will have to memorize the previously selected item manually and after you've updated you collection restore the selection also manually. Usually it is done something like this:

public void RefreshMyItems()
{
    var previouslySelectedItem = SelectedItem;

    MyItems.Clear();
    foreach(var myItem in LoadItems()) MyItems.Add(myItem);

    SelectedItem = MyItems.SingleOrDefault(i => i.Id == previouslySelectedItem.Id);

}

如果要将相同的行为应用于所有 ComboBoxes (或可能是所有 Selector 控件),则可以考虑创建行为附加属性混合行为)。此行为将预订 SelectionChanged CollectionChanged 事件,并在适当时保存/恢复所选项目。

If you want to apply the same behavior to all ComboBoxes (or perhaps all Selector controls), you can consider creating a Behavior(an attached property or blend behavior). This behavior will subscribe to the SelectionChanged and CollectionChanged events and will save/restore the selected item when appropriate.

这篇关于ComboBox ItemsSource已更改=&gt; SelectedItem毁了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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