Lier un ComboBox WPF à une liste personnalisée

J’ai un ComboBox qui ne semble pas mettre à jour SelectedItem / SelectedValue.

ComboBox ItemsSource est lié à une propriété dans une classe ViewModel qui répertorie un groupe d’entrées d’annuaire RAS en tant que CollectionView. Ensuite, j’ai lié (à des moments différents) à la fois SelectedItem ou SelectedValue à une autre propriété de ViewModel. J’ai ajouté un MessageBox dans la commande save pour déboguer les valeurs définies par la liaison de données, mais la liaison SelectedItem / SelectedValue n’est pas définie.

La classe ViewModel ressemble à ceci:

 public ConnectionViewModel { private readonly CollectionView _phonebookEnsortinges; private ssortingng _phonebookeEntry; public CollectionView PhonebookEnsortinges { get { return _phonebookEnsortinges; } } public ssortingng PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } } 

La collection _phonebookEnsortinges est initialisée dans le constructeur à partir d’un object métier. La ComboBox XAML ressemble à ceci:

  

Je ne m’intéresse qu’à la valeur de chaîne réelle affichée dans la zone de liste déroulante, pas aux autres propriétés de l’object, car il s’agit de la valeur que je dois transmettre à RAS pour établir la connexion VPN. propriété du ConnectionViewModel. Le ComboBox se trouve dans un DataTemplate appliqué à un ItemsControl sur une fenêtre dont le DataContext a été défini sur une instance de ViewModel.

ComboBox affiche la liste des éléments correctement et je peux en sélectionner un dans l’interface utilisateur sans problème. Toutefois, lorsque j’affiche la boîte de message à partir de la commande, la propriété PhonebookEntry contient toujours la valeur initiale, et non la valeur sélectionnée dans le contrôle ComboBox. Les autres instances TextBox sont bien mises à jour et affichées dans MessageBox.

Que manque-t-il à la databinding du ComboBox? J’ai beaucoup cherché et je n’arrive pas à trouver quoi que ce soit que je fasse mal.


C’est le comportement que je constate, mais cela ne fonctionne pas pour une raison quelconque dans mon contexte particulier.

J’ai un MainWindowViewModel qui possède un CollectionView de ConnectionViewModels. Dans le code du fichier MainWindowView.xaml-behind, je définis le DataContext sur MainWindowViewModel. MainWindowView.xaml a un ItemsControl lié à la collection de ConnectionViewModels. J’ai un DataTemplate qui contient le ComboBox ainsi que d’autres TextBox. Les TextBoxes sont directement liées aux propriétés du ConnectionViewModel en utilisant Text="{Binding Path=ConnectionName}" .

 public class ConnectionViewModel : ViewModelBase { public ssortingng Name { get; set; } public ssortingng Password { get; set; } } public class MainWindowViewModel : ViewModelBase { // List... public CollectionView Connections { get; set; } } 

Le code XAML-behind:

 public partial class Window1 { public Window1() { InitializeComponent(); DataContext = new MainWindowViewModel(); } } 

Alors XAML:

        

Les zones de texte se lient toutes correctement et les données se déplacent entre elles et le ViewModel sans problème. Ce n’est que le ComboBox qui ne fonctionne pas.

Vous avez raison dans votre hypothèse concernant la classe PhonebookEntry.

L’hypothèse que je fais est que le DataContext utilisé par mon DataTemplate est automatiquement défini via la hiérarchie de liaison, de sorte que je n’ai pas à le définir explicitement pour chaque élément du ItemsControl . Cela me semblerait un peu idiot.


Voici une implémentation de test qui illustre le problème, basé sur l’exemple ci-dessus.

XAML:

              

Le code-behind :

 namespace WpfApplication7 { ///  /// Interaction logic for Window1.xaml ///  public partial class Window1 : Window { public Window1() { InitializeComponent(); DataContext = new MainWindowViewModel(); } } public class PhoneBookEntry { public ssortingng Name { get; set; } public PhoneBookEntry(ssortingng name) { Name = name; } } public class ConnectionViewModel : INotifyPropertyChanged { private ssortingng _name; public ConnectionViewModel(ssortingng name) { _name = name; IList list = new List { new PhoneBookEntry("test"), new PhoneBookEntry("test2") }; _phonebookEnsortinges = new CollectionView(list); } private readonly CollectionView _phonebookEnsortinges; private ssortingng _phonebookEntry; public CollectionView PhonebookEnsortinges { get { return _phonebookEnsortinges; } } public ssortingng PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } public ssortingng Name { get { return _name; } set { if (_name == value) return; _name = value; OnPropertyChanged("Name"); } } private void OnPropertyChanged(ssortingng propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } public class MainWindowViewModel { private readonly CollectionView _connections; public MainWindowViewModel() { IList connections = new List { new ConnectionViewModel("First"), new ConnectionViewModel("Second"), new ConnectionViewModel("Third") }; _connections = new CollectionView(connections); } public CollectionView Connections { get { return _connections; } } } } 

Si vous exécutez cet exemple, vous obtiendrez le comportement dont je parle. Le TextBox met à jour sa liaison fine lorsque vous le modifiez, mais pas le ComboBox. Très déroutant de voir que la seule chose que j’ai faite est d’introduire un parent ViewModel.

Je travaille actuellement sous l’impression qu’un élément lié à l’enfant d’un DataContext a cet enfant comme DataContext. Je ne trouve aucune documentation qui élimine tout cela d’une manière ou d’une autre.

C’est à dire,

Fenêtre -> DataContext = MainWindowViewModel
..Items -> Bound to DataContext.PhonebookEnsortinges
…. Item -> DataContext = PhonebookEntry (implicitement associé)

Je ne sais pas si cela explique mieux mon hypothèse (?).


Pour confirmer mon hypothèse, modifiez la liaison du TextBox à

  

Et cela montrera la racine de liaison TextBox (que je compare au DataContext) est l’instance ConnectionViewModel.

Vous définissez le DisplayMemberPath et le SelectedValuePath sur “Name”, donc je suppose que vous avez une classe PhoneBookEntry avec une propriété publique Name.

Avez-vous défini le DataContext sur votre object ConnectionViewModel?

J’ai copié votre code et apporté quelques modifications mineures, et cela semble fonctionner correctement. Je peux définir la propriété viewModels PhoneBookEnty et l’élément sélectionné dans la liste déroulante modifiée, et je peux modifier l’élément sélectionné dans la liste déroulante et les propriétés de la vue de la propriété PhoneBookEntry sont définies correctement.

Voici mon contenu XAML:

         

Et voici mon code-behind:

 namespace WpfApplication6 { ///  /// Interaction logic for Window1.xaml ///  public partial class Window1 : Window { public Window1() { InitializeComponent(); ConnectionViewModel vm = new ConnectionViewModel(); DataContext = vm; } private void Button_Click(object sender, RoutedEventArgs e) { ((ConnectionViewModel)DataContext).PhonebookEntry = "test"; } } public class PhoneBookEntry { public ssortingng Name { get; set; } public PhoneBookEntry(ssortingng name) { Name = name; } public override ssortingng ToSsortingng() { return Name; } } public class ConnectionViewModel : INotifyPropertyChanged { public ConnectionViewModel() { IList list = new List(); list.Add(new PhoneBookEntry("test")); list.Add(new PhoneBookEntry("test2")); _phonebookEnsortinges = new CollectionView(list); } private readonly CollectionView _phonebookEnsortinges; private ssortingng _phonebookEntry; public CollectionView PhonebookEnsortinges { get { return _phonebookEnsortinges; } } public ssortingng PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } private void OnPropertyChanged(ssortingng propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } } 

Edit: Le deuxième exemple de Geoff ne semble pas fonctionner, ce qui me semble un peu étrange. Si je modifie la propriété PhonebookEnsortinges sur ConnectionViewModel pour qu’elle soit de type ReadOnlyCollection , la liaison TwoWay de la propriété SelectedValue sur la liste déroulante fonctionne correctement.

Peut-être y a-t-il un problème avec CollectionView? J’ai remarqué un avertissement dans la console de sortie:

System.Windows.Data Avertissement: 50: L’utilisation directe de CollectionView n’est pas entièrement prise en charge. Les fonctionnalités de base fonctionnent, bien qu’avec certaines inefficacités, mais les fonctionnalités avancées peuvent rencontrer des bogues connus. Pensez à utiliser une classe dérivée pour éviter ces problèmes.

Edit2 (.NET 4.5): le contenu de DropDownList peut être basé sur ToSsortingng () et non sur DisplayMemberPath, alors que DisplayMemberPath spécifie uniquement le membre de l’élément sélectionné et affiché.

Pour lier les données à ComboBox

 List ListData = new List(); ListData.Add(new ComboData { Id = "1", Value = "One" }); ListData.Add(new ComboData { Id = "2", Value = "Two" }); ListData.Add(new ComboData { Id = "3", Value = "Three" }); ListData.Add(new ComboData { Id = "4", Value = "Four" }); ListData.Add(new ComboData { Id = "5", Value = "Five" }); cbotest.ItemsSource = ListData; cbotest.DisplayMemberPath = "Value"; cbotest.SelectedValuePath = "Id"; cbotest.SelectedValue = "2"; 

ComboData ressemble à:

 public class ComboData { public int Id { get; set; } public ssortingng Value { get; set; } } 

J’ai eu ce qui au début semblait être un problème identique, mais il s’est avéré être dû à un problème de compatibilité NHibernate / WPF. Le problème est dû à la manière dont WPF vérifie l’égalité des objects. J’ai été capable de faire fonctionner mes fichiers en utilisant la propriété ID de l’object dans les propriétés SelectedValue et SelectedValuePath.

  

Pour plus de détails, consultez l’article du blog Chester, The WPF ComboBox – SelectedItem, SelectedValue et SelectedValuePath avec NHibernate .

J’ai eu un problème similaire où SelectedItem n’a jamais été mis à jour.

Mon problème était que l’élément sélectionné n’était pas la même instance que l’élément contenu dans la liste. J’ai donc simplement dû remplacer la méthode Equals () dans mon object MyCustomObject et comparer les ID de ces deux instances pour indiquer au ComboBox qu’il s’agit du même object.

 public override bool Equals(object obj) { return this.Id == (obj as MyCustomObject).Id; }