Liaison de données WinForms ComboBox Gotcha
-
08-06-2019 - |
Question
Supposons que vous faites quelque chose comme ce qui suit
List<string> myitems = new List<string>
{
"Item 1",
"Item 2",
"Item 3"
};
ComboBox box = new ComboBox();
box.DataSource = myitems;
ComboBox box2 = new ComboBox();
box2.DataSource = myitems
Nous avons donc maintenant 2 zones de liste déroulante liées à ce tableau, et tout fonctionne bien.Mais lorsque vous modifiez la valeur d’une zone de liste déroulante, les DEUX zones de liste déroulante sont remplacées par celle que vous venez de sélectionner.
Maintenant, je sais que les tableaux sont toujours passés par référence (j'ai appris cela quand j'ai appris C :D), mais pourquoi diable les zones de liste déroulante changeraient-elles ensemble ?Je ne pense pas que le contrôle de la zone de liste déroulante modifie du tout la collection.
En guise de solution de contournement, cela ne permettrait pas d'obtenir la fonctionnalité attendue/souhaitée.
ComboBox box = new ComboBox();
box.DataSource = myitems.ToArray();
La solution
Cela a à voir avec la façon dont les liaisons de données sont configurées dans le framework dotnet, en particulier le BindingContext
.À un niveau élevé, cela signifie que si vous n'avez pas spécifié le contraire, chaque formulaire et tous les contrôles du formulaire partagent le même BindingContext
.Lorsque vous réglez le DataSource
propriété le ComboBox
utilisera le BindingContext
pour obtenir un ConcurrenyMangager
cela enveloppe la liste.Le ConcurrenyManager
garde une trace de choses telles que la position actuellement sélectionnée dans la liste.
Lorsque vous définissez le DataSource
de la seconde ComboBox
il utilisera la même chose BindingContext
(les formulaires) qui donneront une référence au même ConcurrencyManager
comme ci-dessus utilisé pour configurer les liaisons de données.
Pour obtenir une explication plus détaillée, voir Contexte de liaison.
Autres conseils
Une meilleure solution (en fonction de la taille de la source de données) consiste à déclarer deux BindingSource
les objets (nouveautés à partir de la version 2.00) lient la collection à ceux-ci, puis les lient aux listes déroulantes.
Je joins un exemple complet.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
private BindingSource source1 = new BindingSource();
private BindingSource source2 = new BindingSource();
public Form1()
{
InitializeComponent();
Load += new EventHandler(Form1Load);
}
void Form1Load(object sender, EventArgs e)
{
List<string> myitems = new List<string>
{
"Item 1",
"Item 2",
"Item 3"
};
ComboBox box = new ComboBox();
box.Bounds = new Rectangle(10, 10, 100, 50);
source1.DataSource = myitems;
box.DataSource = source1;
ComboBox box2 = new ComboBox();
box2.Bounds = new Rectangle(10, 80, 100, 50);
source2.DataSource = myitems;
box2.DataSource = source2;
Controls.Add(box);
Controls.Add(box2);
}
}
}
Si vous voulez vous embrouiller encore plus, essayez de toujours déclarer les liaisons dans le constructeur.Cela peut entraîner certains vraiment bugs curieux, c'est pourquoi je me lie toujours à l'événement Load.