ComboBox ItemsSource changé => SelectedItem est ruiné

Ok, cela a été m'énerve pour un certain temps maintenant. Et je me demande comment les autres gérer les cas suivants:

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

Le DataContext de l'objet 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;
    }
}

Évidemment, quand le RefreshMyItems() méthode est appelée la zone de liste modifiable reçoit la Collecte des événements de modification, mise à jour de ses éléments et ne trouve pas le SelectedItem dans la nouvelle collection => définit les SelectedItem à null. Mais j'aurais besoin de la liste déroulante pour utiliser Equals méthode pour sélectionner l'élément voulu dans la nouvelle collection.

En d'autres termes - le ItemsSource collection contient toujours le bon MyItem, mais c'est un new objet. Et je veux la liste déroulante utiliser quelque chose comme Equals pour sélectionner automatiquement (c'est d'autant plus difficile parce que tout d'abord la source des appels de collecte Clear() qui réinitialise la collecte et déjà à ce stade de la SelectedItem est fixé à null).

Mise à JOUR 2 Avant de copier-coller le code ci-dessous, veuillez noter qu'il est loin de la perfection! Et notez qu'il ne se lie pas deux façons par défaut.

Mise à JOUR Juste au cas où quelqu'un a le même problème (une propriété attachée proposé par Pavlo Glazkov dans sa réponse):

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]);
}
};
}
}
}
  • iv e suis tombé sur ce problème et résolu de la manière suivante stackoverflow.com/questions/12337442/...
  • Pour ceux qui ont des problèmes avec cette approche et qui, comme moi, ne s'en rend pas compte au premier abord: cette solution fonctionne mieux lorsqu'il est utilisé avec nmclean de réponse.
InformationsquelleAutor Jefim | 2011-03-09