Ok, esto me ha estado molestando por un tiempo. Y me pregunto cómo otros manejan el siguiente caso:ComboBox ItemsSource changed => SelectedItem está en ruinas
<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}"/>
El código del objeto DataContext:
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;
}
}
Obviamente, cuando el método RefreshMyItems()
se llama el cuadro combinado recibe los acontecimientos de colecciones cambiado, actualiza sus artículos y lo hace no encontrar el elemento seleccionado en la colección actualizada => establece el elemento seleccionado en null
. Pero necesitaría el cuadro combinado para usar el método Equals
para seleccionar el elemento correcto en la nueva colección.
En otras palabras, la colección ItemsSource todavía contiene el MyItem
correcto, pero es un objeto new
. Y quiero que el cuadro combinado use algo como Equals
para seleccionarlo automáticamente (esto se hace aún más difícil porque primero la colección fuente llama a Clear()
que restablece la colección y ya en ese punto el elemento seleccionado se establece en null
).
ACTUALIZACIÓN 2 ¡Antes de copiar y pegar el siguiente código, tenga en cuenta que está lejos de la perfección! Y tenga en cuenta que no se une de dos maneras por defecto.
ACTUALIZACIÓN Por si alguien tiene el mismo problema (una propiedad que se adjunta como propuesto por Pavlo Glazkov en su respuesta):
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 se encontró con este problema y lo resolvió de la siguiente manera http://stackoverflow.com/questions/12337442/proper-use-of-propertychangedtrigger-and-changepropertyaction/12341649#12341649 –