ComboBox ItemsSource已更改=> SelectedItem毁了 [英] ComboBox ItemsSource changed => SelectedItem is ruined
问题描述
好吧,这已经困扰了我一段时间了。而且我想知道其他人如何处理以下情况:
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已更改=> SelectedItem毁了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!