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.
Vous devez vous connecter pour publier un commentaire.
La norme
ComboBox
n'a pas que de la logique. Et comme vous l'avez mentionnéSelectedItem
devientnull
déjà après l'appel deClear
, de sorte que leComboBox
n'a aucune idée sur vous l'intention d'ajouter l'élément de même plus tard, et par conséquent, il ne fait rien pour le sélectionner. Cela étant dit, vous devrez mémoriser l'élément précédemment sélectionné manuellement et après que vous avez mis à jour, vous restauration de collection de la sélection manuellement. Habituellement, il est fait quelque chose comme ceci:Si vous souhaitez appliquer le même comportement à tous les
ComboBoxes
(ou peut-être tous lesSelector
de contrôle), vous pouvez envisager de créer unBehavior
(un attaché à la propriété ou mélange comportement). Ce comportement s'abonner à laSelectionChanged
etCollectionChanged
événements et les sauvegarder/restaurer l'élément sélectionné lorsque cela est approprié.C'est le top de résultats sur google pour "wpf itemssource est égal à", donc à toute personne qui cherche la même approche que dans la question, il ne travailler aussi longtemps que vous entièrement mettre en œuvre l'égalité des fonctions. Voici une complète MyItem mise en œuvre:
J'ai testé avec succès ce avec une zone de liste à sélection multiple, où
listbox.SelectedItems.Add(item)
n'était pas pour sélectionner l'élément correspondant, mais il a travaillé après j'ai mis en place le dessus suritem
.Malheureusement lors de la configuration de ItemsSource sur un Sélecteur d'objet il définit immédiatement SelectedValue ou SelectedItem null même si l'élément correspondant est à nouveau ItemsSource.
Peu importe si vous implémenter Equals.. fonctions ou si vous utilisez un implicitement type comparable pour votre SelectedValue.
Bien, vous pouvez enregistrer SelectedItem/Valeur avant le réglage de l'ItemsSource et de la restauration. Mais que faire si il y a une liaison sur SelectedItem/Valeur qui sera appelé à deux reprises:
la valeur null
restauration d'origine.
C'est une charge supplémentaire et même il peut causer certains un comportement indésirable.
Voici une solution que j'ai faite. Fonctionnera pour tout le Sélecteur d'objet. Juste SelectedValue de liaison avant le réglage de l'ItemsSource.
UPD: Ajout de try/finally pour protéger des exceptions dans les gestionnaires, a également ajouté null vérifier pour la liaison.
Voici un exemple de code XAML:
De Test De L'Unité
Ici est une unité de cas de test prouvant qu'il fonctionne. Juste un commentaire le
#define USE_DECORATOR
de voir le test échoue lors de l'utilisation de la norme liaisons.Vous pouvez envisager d'utiliser un valueconverter afin de sélectionner le bon SlectedItem à partir de votre collection
Clear
est appelé il n'y a rien à sélectionner. Vous aurez besoin de stocker l'élément précédemment sélectionné quelque part de toute façon.La véritable solution à ce problème est de ne pas enlever les éléments qui sont dans la nouvelle liste. C'est à dire. Ne pas effacer toute la liste, il suffit de supprimer ceux qui ne sont pas dans la nouvelle liste, puis ajouter ceux que la nouvelle liste a qui n'étaient pas dans l'ancienne liste.
Exemple.
Actuel De Zone De Liste Déroulante D'Éléments
Pomme, Orange, Banane
De Nouveaux Éléments De La Zone De Liste Déroulante
Pomme, Orange, Poire
Pour Remplir les nouveaux éléments
Supprimer la Banane et Ajouter la Poire
Maintenant le combo arc est toujours valable pour les éléments que vous pourriez avoir sélectionné et les éléments sont maintenant effacés s'ils ont été sélectionnés.
J'ai juste mis en œuvre très simple de remplacement et il semble être de travailler visuellement, cependant, cette coupe des tas de logique interne, donc je ne suis pas sûr que c'est une solution sûre:
Donc, si vous utilisez cette commande, puis modifier des Éléments/ItemsSource n'affectera pas SelectedValue et le Texte -, ils restent intactes.
S'il vous plaît laissez-moi savoir si vous trouvez des problèmes qu'elle provoque.
Après avoir perdu la moitié de ma tête et les poils et de casser mon clavier plusieurs fois,
je pense que pour le contrôle zone de liste déroulante, il est préférable de ne pas écrire le selectedItem,Selectedindex et ItemsSource de liaison de l'expression dans le code XAML comme nous ne peut pas vérifier si l'ItemsSource a changé, lors de l'utilisation de propriété ItemsSource de cours.
Dans la fenêtre ou de contrôle de l'utilisateur du constructeur j'ai défini la propriété ItemsSource de la zone de liste déroulante, puis dans le chargé de gestionnaire d'événement de la fenêtre ou de contrôle de l'utilisateur, j'ai défini l'expression de liaison et il fonctionne parfaitement. Si je mettrais ItemsSource expression de liaison dans le XAML, sans le "selectedItem", je ne trouve pas tout gestionnaire d'événements pour définir la SelectedItem expression de liaison, tout en empêchant la zone de liste déroulante source de mise à jour avec une référence null (selectedIndex = -1).