La définition dynamique de DataGridViewComboBoxCell de la source de données à DataView filtrée à partir d'autres cellules de la sélection
J'ai été à la recherche haute et basse pour un moyen de le faire, mais en vain. Je suis venu avec une solution qui fonctionne, mais je me demande si il ya une meilleure façon de les gérer.
Le Problème:
Je suis à l'aide d'un DataGridView qui a deux DataGridViewComboBoxColumn, col1 et col2.
col1 a eu sa source de données définie à une DataTable. En fonction de la sélection à partir d'une cellule donnée dans col1, je voudrais avoir respectifs col2 cellule de la même ligne avoir sa source de données défini pour être un DataView filtrée de col2 de la source de données.
La version abrégée du code à partir de mon actuel de la mise en œuvre pourrait mieux aider à décrire ce que je suis en train de faire:
DataGridView dg = new DataGridView();
dg.DataSource = new BindingSource() { DataSource = _myDataTable }; //DataTable with Foreign Keys for ID1 and ID2
dg.CellValueChanged += new DataGridViewCellEventHandler(dg_CellValueChanged);
DataGridViewComboBoxColumn col1 = new DataGridViewComboBoxColumn();
col1.DataPropertyName = "ID1";
col1.DisplayMember = "Display1";
col1.ValueMember = "ID1";
col1.DataSource = dataTable1;
col1.ValueType = typeof(Int32);
DataGridViewComboBoxColumn col2 = new DataGridViewComboBoxColumn();
col2.DataPropertyName = "ID2";
col2.DisplayMember = "Display2";
col2.ValueMember = "ID2";
col2.DataSource = dataTable2;
col2.ValueType = typeof(Int32);
dg.Columns.Add(col1);
dg.Columns.Add(col2);
Puis-je définir le gestionnaire d'événement:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
if (dgv == null)
return;
int selectedID;
if (!int.TryParse(dgv[e.ColumnIndex, e.RowIndex].Value.ToString(), out selectedID))
return;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
var col = dgv.Columns[e.ColumnIndex + 1] as DataGridViewComboBoxColumn;
if(col == null)
return;
var dt = col.DataSource as DataTable; //The macro-DataTable containing all possible values
if(dt == null)
return;
DataView dv = new DataView(dt, "ID1 = " + selectedID, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
//This is the part that I am wondering if there is a better way of handling
cell.DataSource = dt; //Set the data source to the macro-DataTable so that when we set the Value to something in the new DataView it will not throw an exception
cell.DisplayMember = "Display2"; //Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.Value = dv[0]["ID2"]; //Set the value to the first option in the new DataView to avoid an exception being thrown when setting the dv as the DataSource
cell.DataSource = dv;
}
}
La dernière partie qui est ma préoccupation.
Lors de la commutation dynamique de la source de données de cellule2, si cellule2 actuel de sélection n'apparaît pas dans le nouveau DataView, une exception sera levée:
Système.ArgumentException: DataGridViewComboBoxCell valeur n'est pas valide.
Pour éviter cela, je suis en train de la source de données à la macro-DataTable (contenant toutes les valeurs que peut être illustré à partir d'cell1 de sélection), la modification de la valeur sélectionnée de cellule2 pour être le premier résultat dans la fenêtre données et de définir ensuite cellule2 de la source de données pour être dit DataView. Tout cela garantit que la cellule est jamais une sélection non valide et qu'il fonctionne comme prévu.
Ma question est, est-il mieux/plus simple façon de le faire? Pour mon utilisation, ce code est activé uniquement lors de la création de nouvelles lignes, de sorte qu'il n'est pas fait plus de quelques fois dans la forme donnée. Cependant, si il ya une meilleure façon de le faire, ou quelques suggestions sur ce qu'elle soit mieux, je vous en serais reconnaissant!
(première question ici, donc je suis également ouvert à toutes les suggestions pour l'affichage ... excuses pour les erreurs commises)
MODIFIER (en fournissant à la structure de la table - aussi changé "BoundID" être "ID1" pour éviter toute confusion):
DataGrid de la structure de la table serait:
MainTableID int
ID1 int --C'est une clé étrangère vers col1
ID2 int --C'est une clé étrangère vers col2
La colonne 1 du tableau de la structure:
ID1 int
Display1 varchar(50)
La colonne 2 du tableau de la structure:
ID2 int
Display2 varchar(50)
ID1 int --C'est une clé étrangère vers col1
Mise à jour:
J'ai redéfini la CellValueChanged gestionnaire d'événement par Mohsen suggestions:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
DataView dv = new DataView( ((DataTable)((DataGridViewComboBoxColumn)dgv.Columns[e.ColumnIndex + 1]).DataSource), "ID1 = " + dgv.CurrentCell.Value, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
cell.DisplayMember = "Display2"; //Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.DataSource = dv;
}
}
Et j'ai ajouté dans un gestionnaire d'événement pour la DataError comme il le suggère:
void dg_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
if(e.ColumnIndex != 1)
{
//Alert the user for any other DataError's outside of the column I care about
MessageBox.Show("The following exception was encountered: " + e.Exception);
}
}
Cela semble fonctionner parfaitement.
OriginalL'auteur Smitty | 2012-10-26
Vous devez vous connecter pour publier un commentaire.
Votre code peut être simplifié à ce, sans exception, lançant:
Mis à jour
Lorsque vous modifiez un déjà des éléments de jeu dans la première zone de liste modifiable depuis le dataview filtrée dans la deuxième zone de liste déroulante ont différentes
ID1
, une exception est levée avec "DataGridViewComboBoxCell valeur n'est pas valide.". Afin d'attraper cette exception s'inscrire datagridviewDataError
de l'événement avec pas de code dans les enregistrements de la méthode. Ensuite, lorsque vous modifiez la déjà mis en combbox, la zone de liste déroulante correspondante sera remplie avec les éléments corrects.Pouvez-vous fournir le schéma de votre base de données les tables? Aussi, ne définissez pas DataPropertyName pour les colonnes
Toutes mes excuses, je n'avais pas précédemment indiquent que le DataGridView a l'aide d'un BindingSource comme source de données (c'est pourquoi le DataPropertyName est utilisé). J'ai ajouté de la structure de la table selon votre demande.
Mise à jour de réponse. Si vous rencontrez une erreur encore, temporairement désactiver la source de données de datagridview dans designer et essayez de nouveau. Si le problème résiste, veuillez fournir un instantané de votre datagridview
Ajout de la gestion des événements pour DataError à ma solution existante ne faire l'affaire. Cependant, je n'arrive toujours pas votre solution fonctionne avec réglage de la cellule.Source de données à un DataRow sur[] pour afficher autre chose que Système.Les données.DataRow dans les menus déroulants. Je ne suis pas sûr de savoir comment procéder, que je voudrais vous donner un crédit pour votre mise à Jour réponse, mais pas pour le code qui ne fonctionne pas (dans mon cas).
OriginalL'auteur Mohsen Afshin
C'est l'arrêt rapide de la boutique pour la solution qui a fonctionné pour moi, mais je voulais quand même donner Mohsen le crédit. Dupliqué ce dans la question d'origine.
J'ai redéfini la CellValueChanged gestionnaire d'événement par Mohsen suggestions:
Et j'ai ajouté dans un gestionnaire d'événement pour la DataError comme il le suggère:
Cela semble fonctionner parfaitement.
OriginalL'auteur Smitty