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.

39
demandé sur Nathan 2009-03-19 23:16:50

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é.

27
répondu Orion Edwards 2009-03-19 20:33:28

IsSynchronizedWithCurrentItem="True" a fonctionné pour moi!

27
répondu rp7799 2013-06-10 21:23:53

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.

20
répondu GuestPerson 2011-01-13 02:45:09

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é.

11
répondu 2009-03-19 21:50:55

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.

10
répondu Breno P. Lucena 2012-10-03 22:03:54

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=IdIdint. Si vous utilisez une clé de chaîne, lier à l' Text propriété du ComboBox au lieu de SelectedValue.

4
répondu Dave 2013-06-10 21:27:30

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.

3
répondu Abe Heidebrecht 2009-03-20 21:30:28

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.

2
répondu StefanHa 2010-05-14 15:21:56

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.

2
répondu Tim 2011-09-08 02:13:45

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
1
répondu Scott McFadden 2011-02-08 23:42:56

le type de SelectedValuePath et SelectedValue doit être EXACTEMENT le même.

Si, par exemple, le type de SelectedValuePathInt16 et le type de la propriété qui se lie à SelectedValueint ç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.

1
répondu Dummy01 2011-04-21 10:19:02

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!

1
répondu JCH2k 2012-01-11 16:52:32

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.

0
répondu markti 2009-03-21 15:24:19

zone de liste déroulante.SelectionBoxItem.ToString ()

0
répondu Sean Campbell 2010-06-28 10:32:28

IsSyncronizedWithCurrent=False va le faire fonctionner.

0
répondu Rohit 2010-12-08 07:38:35

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.

0
répondu Vinicius 2012-11-30 19:01:26

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;
 }));
0
répondu Peter T. 2014-08-16 20:48:07
IsSynchronizedWithCurrentItem="True" 

a fonctionné pour moi aussi

-1
répondu user4773407 2018-08-21 08:01:42