WPF MVVM ComboBox SelectedItem ou SelectedValue ne fonctionne pas
mise à Jour
Après un peu d'investigation. Ce qui semble être le problème, c'est que la SelectedValue/SelectedItem se produit avant que la source de L'article soit terminé chargement. Si je m'assois dans un point d'arrêt et que j'attends quelques secondes, ça marche comme prévu. Ne sais pas comment je vais l'obtenir autour de celui-ci.
Mise À Jour
j'ai une application qui utilise dans WPF en utilisant MVVM avec un ComboBox. Ci-dessous, L'exemple ViewModel. La question que je suis avoir est quand nous quittons notre page et migrons de nouveau le ComboBox ne sélectionne pas la valeur courante qui est sélectionnée.
Modèle De Vue
public class MyViewModel
{
private MyObject _selectedObject;
private Collection<Object2> _objects;
private IModel _model;
public MyViewModel(IModel model)
{
_model = model;
_objects = _model.GetObjects();
}
public Collection<MyObject> Objects
{
get
{
return _objects;
}
private set
{
_objects = value;
}
}
public MyObject SelectedObject
{
get
{
return _selectedObject;
}
set
{
_selectedObject = value;
}
}
}
pour les besoins de cet exemple, disons que MyObject possède deux propriétés (Text et Id). Mon XAML pour le ComboBox ressemble à ça.
XAML
<ComboBox Name="MyComboBox" Height="23" Width="auto"
SelectedItem="{Binding Path=SelectedObject,Mode=TwoWay}"
ItemsSource="{Binding Objects}"
DisplayMemberPath="Text"
SelectedValuePath="Id">
peu importe de quelle façon je le configure quand je reviens à la page et que l'objet est réassemblé, le ComboBox ne sélectionnera pas la valeur. L'objet renvoie l'objet correct via l'get dans la propriété.
Je ne suis pas sûr que ce soit juste un problème avec la façon dont le modèle ComboBox et MVVM fonctionne. La zone de texte de liaison que nous faisons fonctionne correctement.
18 réponses
avez-vous essayé d'implémenter INotifyPropertyChanged
dans votre viewmodel, puis de relancer le PropertyChanged
cas SelectedItem
est défini?
si cela ne le corrige pas, alors vous pourrez soulever manuellement le PropertyChanged
event yourself lors de la navigation de retour à la page, et cela devrait être suffisant pour que WPF se redessine et affiche le bon élément sélectionné.
vous devez placer la propriété ItemsSource avant la propriété SelectedItem. Je suis tombé sur un blog il y a quelques jours mentionnant le problème.
j'ai eu des problèmes similaires et il a été résolu en m'assurant que je mettais en œuvre IEquatable correctement. Lorsque la liaison se produit, il essaie de voir si les objets correspondent donc assurez-vous que vous mettez en œuvre correctement votre vérification de l'égalité.
dans ce cas, le bind selecteditem ne fonctionne pas, car l'id de hachage des objets est différent.
Une solution possible est:
basé sur l'id de l'item sélectionné, récupérez l'objet sur la collection itemsource et définissez la propriété de l'item sélectionné à avec elle.
Exemple:
<ctrls:ComboBoxControlBase SelectedItem="{Binding Path=SelectedProfile, Mode=TwoWay}" ItemsSource="{Binding Path=Profiles, Mode=OneWay}" IsEditable="False" DisplayMemberPath="Name" />
la propriété liée à ItemSource est:
public ObservableCollection<Profile> Profiles
{
get { return this.profiles; }
private set { profiles = value; RaisePropertyChanged("Profiles"); }
}
la propriété liée à SelectedItem est:
public Profile SelectedProfile
{
get { return selectedProfile; }
set
{
if (this.SelectedUser != null)
{
this.SelectedUser.Profile = value;
RaisePropertyChanged("SelectedProfile");
}
}
}
le code de récupération est:
[Command("SelectionChanged")]
public void SelectionChanged(User selectedUser)
{
if (selectedUser != null)
{
if (selectedUser is User)
{
if (selectedUser.Profile != null)
{
this.SelectedUser = selectedUser;
this.selectedProfile = this.Profiles.Where(p => p.Id == this.SelectedUser.Profile.Id).FirstOrDefault();
MessageBroker.Instance.NotifyColleagues("ShowItemDetails");
}
}
}
}
j'espère que ça vous aidera. J'ai passé beaucoup de temps à chercher des réponses, mais je n'ai pas pu les trouver.
en quittant la page courante, le CollectionView
associé ItemsSource
propriété du ComboBox
est purgé. Et parce que le ComboBox
IsSyncronizedWithCurrent
la propriété est true par défaut, le SelectedItem
et SelectedValue
les propriétés sont réinitialisées.
Il semble qu'il s'agisse d'une question de type de données internes dans la reliure. Comme d'autres ont suggéré ci-dessus, si vous utilisez SelectedValue
au lieu de cela, en se liant à une propriété int sur le viewmodel, cela fonctionnera.
Un raccourci pour vous serait de remplacer le Equals
opérateur sur MyObject de sorte que lors de la comparaison de deux MyObjects, le réel Id
propriétés sont comparées.
un autre indice: si vous restructurez vos modèles de vue et utilisez SelectedValue
, utiliser uniquement quand SelectedValuePath=Id
où Id
int
. Si vous utilisez une clé de chaîne, lier à l' Text
propriété du ComboBox
au lieu de SelectedValue
.
j'ai remarqué ce problème avant. J'ai remarqué que la propriété SelectedIndex ne cause pas le même bogue. Si vous pouvez restructurer votre ViewModel pour exposer l'index de l'élément sélectionné, et de le lier à cela, vous devriez être bon d'aller.
j'ai eu le même problème. La chose est. L'élément sélectionné ne sait pas quel objet il doit utiliser de la collection. Vous devez donc dire à l'article sélectionné d'utiliser l'article de la collection.
public MyObject SelectedObject
{
get
{
Objects.find(x => x.id == _selectedObject.id)
return _selectedObject;
}
set
{
_selectedObject = value;
}
}
j'espère que cette aide.
j'ai une réponse très simple pour ce problème. Ajoutez d'abord le code suivant à la vue Isynchronizedwithcurrentitem="True".
suivant quand jamais vous assignez un nouvel objet dans le ViewModel à cette propriété SelectedObject devrait être sauvé à cette propriété et pas le membre privé.
Le Proptery viewmodel doit ressembler à ceci
public Role SelectedObject
{
get { return object; }
set
{
if (value != null)
{
if (!object.Equals(value))
{
object = value;
OnPropertyChanged(() => SelectedObject );
}
}
}
}
Cela devrait résoudre le problème.
je me suis battu avec ce numéro pendant un moment. Dans mon cas, j'utilisais le type complexe (List) comme Source D'Item et j'utilisais un type clé comme valeur sélectionnée. Sur l'événement load, le KeyType était positionné à null. Cela a causé de tout casser. Aucun des sous-éléments serait mise à jour lorsque la clé changé. Il s'est avéré que lorsque j'ai ajouté un contrôle pour s'assurer que la valeur proposée Pour KeyType N'était pas null, tout a fonctionné comme prévu.
#region Property: SelectedKey
// s.Append(string.Format("SelectedKey : {0} " + Environment.NewLine, SelectedKey.ToString()));
private KeyType _SelectedKey = new KeyType();
public KeyType SelectedKey
{
get { return _SelectedKey; }
set
{
if(value != null )
if (!_SelectedKey.Equals(value))
{
_SelectedKey = value;
OnPropertyChanged("SelectedKey");
}
}
}
#endregion SelectedKey
le type de SelectedValuePath
et SelectedValue
doit être EXACTEMENT le même.
Si, par exemple, le type de SelectedValuePath
Int16
et le type de la propriété qui se lie à SelectedValue
int
ça ne marchera pas.
je passe des heures à trouver cela, et c'est pourquoi je réponds ici après tant de temps la question a été posée. Peut-être qu'un pauvre type comme moi avec le même problème peut le voir.
j'ai eu ce problème avec un ComboBox affichant une liste de couleurs ( liste
Sélectionner une couleur était possible mais elle n'était pas affichée lorsque la sélection fermé (bien que la propriété a été changé!)
La solution était d'écraser la Equals(object obj) méthode pour le type sélectionné dans le ComboBox (pinceau), qui n'était pas simple parce que le pinceau est scellé. J'ai donc écrit une classe EqualityBrush contenant un pinceau et de mise en oeuvre Est égal à:
public class EqualityBrush
{
public SolidColorBrush Brush { get; set; }
public override bool Equals(object o)
{
if (o is EqualityBrush)
{
SolidColorBrush b = ((EqualityBrush)o).Brush;
return b.Color.R == this.Brush.Color.R && b.Color.G == this.Brush.Color.G && b.Color.B == this.Brush.Color.B;
}
else
return false;
}
}
L'utilisation d'une liste de ma nouvelle classe EqualityBrush au lieu de la classe de pinceau normale a corrigé le problème!
Mon Combobox XAML ressemble à ceci:
<ComboBox ItemsSource="{Binding BuerkertBrushes}" SelectedItem="{Binding Brush, Mode=TwoWay}" Width="40">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type tree:EqualityBrush}">
<Rectangle Width="20" Height="12" Fill="{Binding Brush}"/>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
rappelez-vous que ma "brosse"-propriété dans le ViewModel doit maintenant être de Type EqualityBrush!
cela pourrait être la façon dont vous appliquez le DataContext à la Page. Dans WPF, chaque fois que vous naviguez vers une Page, tout est réinitialisé, le constructeur est appelé, les méthodes chargées, tout. donc, si vous définissez votre DataContext à l'intérieur de votre vue, vous serez sans doute souffler que SelectedItem que l'Utilisateur a sélectionné. Pour éviter cela, utilisez la propriété KeepAlive de vos pages.
<Page KeepAlive="True" ...>
...
</Page>
il n'y aura que L'événement chargé qui sera déclenché lors de la navigation retour à une page que vous avez déjà visitée. Vous devrez donc vous assurer que vous paramétrez le DataContext sur Initialize (extérieurement ou à l'intérieur du constructeur) plutôt que Load.
cependant, cela ne fonctionnera que pour cette instance de la Page. Si vous accédez à une nouvelle instance de cette page, ce constructeur sera appelé à nouveau.
Utiliser Chargés de l'événement:
private void cmb_Loaded(object sender, RoutedEventArgs e) {
if (cmb.Items.Count > 0) cmb.SelectedIndex = 0;
}
ça marche pour moi.
j'ai résolu le problème en ajoutant dispatcher dans L'événement UserControl_Loaded
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
combobox.SelectedIndex = 0;
}));
IsSynchronizedWithCurrentItem="True"
a fonctionné pour moi aussi