web-dev-qa-db-fra.com

Comment insérer un champ 'vide' dans la zone de liste déroulante liée à DataTable

J'ai une liste déroulante sur une application WinForms dans laquelle un élément peut être sélectionné, mais ce n'est pas obligatoire. J'ai donc besoin d'un premier élément 'Vide' pour indiquer qu'aucune valeur n'a été définie.

La liste déroulante est liée à un DataTable renvoyé par une procédure stockée (je ne présente pas d'excuses pour la notation hongroise sur mes contrôles d'interface utilisateur: p):

 DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP
 cmbHierarchies.DataSource = hierarchies;
 cmbHierarchies.ValueMember = "guid";
 cmbHierarchies.DisplayMember = "ObjectLogicalName";

Comment puis-je insérer un tel article vide?

J'ai effectivement accès à un changement de SP, mais je préférerais vraiment ne pas le "polluer" avec la logique de l'interface utilisateur.

Mise à jour: C’est le DataTable.NewRow () que j’avais masqué, merci. Je vous ai tous mis à jour (les 3 réponses jusqu'à présent de toute façon). J'essaie de faire fonctionner le motif Iterator avant de décider d'une "réponse"

Mise à jour: Je pense que cette modification m’a placé dans le champ Communauté Wiki, j’ai décidé de ne pas spécifier une seule réponse, car elles ont toutes du mérite dans le contexte de leurs domaines. Merci pour votre contribution collective.

17
johnc

Il y a deux choses que tu peux faire:

  1. Ajoutez une ligne vide à la DataTable renvoyée à partir de la procédure stockée.

    DataRow emptyRow = hierarchies.NewRow();
    emptyRow["guid"] = "";
    emptyRow["ObjectLogicalName"] = "";
    hierarchies.Rows.Add(emptyRow);
    

    Créez un DataView et triez-le à l'aide de la colonne ObjectLogicalName. Cela fera de la ligne nouvellement ajoutée la première ligne de DataView.

    DataView newView =           
         new DataView(hierarchies,       // source table
         "",                             // filter
         "ObjectLogicalName",            // sort by column
         DataViewRowState.CurrentRows);  // rows with state to display
    

    Définissez ensuite la vue en tant que DataSource de la ComboBox.

  2. Si vous ne voulez vraiment pas ajouter une nouvelle ligne comme mentionné ci-dessus. Vous pouvez autoriser l'utilisateur à définir la valeur ComboBox sur null en gérant simplement l'événement de suppression "Supprimer". Lorsqu'un utilisateur appuie sur la touche Suppr, définissez la variable SelectedIndex sur -1. Vous devez également définir ComboBox.DropDownStyle sur DropDownList. Cela empêchera l'utilisateur de modifier les valeurs dans la variable ComboBox.

24
Vivek
cmbHierarchies.SelectedIndex = -1;
14
Andrei Nemes

Je crée généralement un itérateur pour ce type de chose. Cela évite de polluer vos données et fonctionne bien avec la liaison de données:

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP
cmbHierarchies.DataSource = GetDisplayTable(hierarchies);
cmbHierarchies.ValueMember = "guid";
cmbHierarchies.DisplayMember = "ObjectLogicalName";

...

private IEnumerable GetDisplayTable(DataTable tbl)
{
    yield return new { ObjectLogicalName = string.Empty, guid = Guid.Empty };

    foreach (DataRow row in tbl.Rows)
        yield return new { ObjectLogicalName = row["ObjectLogicalName"].ToString(), guid = (Guid)row["guid"] };
}

Disclaimer: Je n'ai pas compilé ce code, mais j'ai utilisé ce modèle plusieurs fois.

Remarque: Je suis dans les pays WPF et ASP.Net depuis deux ans. Apparemment, la liste déroulante Winforms veut un IList, pas un IEnumerable. Une opération plus coûteuse consisterait à créer une liste. Ce code est vraiment très concis et je ne l’ai vraiment pas vraiment compilé:

DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();
List<KeyValuePair<string, Guid>> list = new List<KeyValuePair<string, Guid>>(hierarchies.Rows.Cast<DataRow>().Select(row => new KeyValuePair<string, Guid>(row["Name"].ToString(), (Guid)row["Guid"])));
list.Insert(0, new KeyValuePair<string,Guid>(string.Empty, Guid.Empty));
cmbHierarchies.DataSource = list;
cmbHierarchies.ValueMember = "Value";
cmbHierarchies.DisplayMember = "Key";
8
Jason Jackson

insérez une ligne vide dans votre datatable et vérifiez-la dans validation/update/create

3
Steven A. Lowe

J'ai trouvé une autre solution:

Juste après la création de votre table de données (avant d'utiliser le remplissage), ajoutez une nouvelle ligne et utilisez la méthode AcceptChanges pour la table. Le nouvel enregistrement obtiendrait RowState = Unchanged et ne serait pas ajouté à la base de données, mais serait visible dans votre liste de données et votre liste déroulante.

   DataTable dt = new DataTable();
    dt.Rows.Add();
    dt.AcceptChanges();
    ...
    dt.Fill("your query");
2
Ray

Vous ne pouvez pas ajouter un nouveau DataRow au DataTable avant de le lier à votre source de données?

Vous pouvez utiliser la fonction NewRow du DataTable pour réaliser ceci:

http://msdn.Microsoft.com/en-us/library/system.data.datatable.newrow.aspx

2
Mark

J'ai écrit cette méthode sur la base des suggestions de Jason Jackson: 

private IEnumerable<KeyValuePair<object,object>> GetDisplayTable(DataTable dataTable,  DataColumn ValueMember, string sep,params DataColumn[] DisplayMembers)
{
    yield return new KeyValuePair<object,object>("<ALL>",null);

    if (DisplayMembers.Length < 1)
        throw new ArgumentException("At least 1 DisplayMember column is required");

    foreach (DataRow r in dataTable.Rows)
    {
        StringBuilder sbDisplayMember = new StringBuilder();
        foreach(DataColumn col in DisplayMembers)
        {
            if (sbDisplayMember.Length > 0) sbDisplayMember.Append(sep);
            sbDisplayMember.Append(r[col]);
        }
        yield return new KeyValuePair<object, object>(sbDisplayMember.ToString(), r[ValueMember]);
    }
}

Usage:

bindingSource1.DataSource = GetDisplayTable(
            /*DataTable*/typedDataTable, 
            /*ValueMember*/typedDataTable.IDColumn, 
            /*DisplayColumn Seperator*/" - ",
            /*List of Display Columns*/
            typedDataTable.DB_CODEColumn,
            typedDataTable.DB_NAMEColumn);

comboBox1.DataSource = bindingSource1;
comboBox1.DisplayMember = "Key";
comboBox1.ValueMember = "Value";

//another example without multiple display data columns:
bindingSource2.DataSource = GetDisplayTable(
            /*DataTable*/typedDataTable, 
            /*ValueMember*/typedDataTable.IDColumn, 
            /*DisplayColumn Seperator*/null,
            /*List of Display Columns*/
                typedDataTable.DESCColumn );

plus bas, où la valeur sélectionnée est consommée:

if (comboBox1.SelectedValue != null)
     // Do Something with SelectedValue   
else 
     // All was selected (all is my 'empty')

Cela permettra d’afficher plusieurs colonnes concaténées dans la zone de liste déroulante, tout en conservant le membre Value avec le même identifiant + il utilise le bloc itérateur avec BindingSource, BindingSource peut être surchargé en fonction de votre situation.

Les commentaires et suggestions sont les bienvenus.

1
Vincent De Smet

J'ai trouvé ça:

    DataTable hierarchies = new DataTable(); 

    cmbHierarchies.BeginUpdate();
    cmbHierarchies.ValueMember = this.Value;
    cmbHierarchies.DisplayMember = this.Display;
    hierarchies = DataView.ToTable();
    cmbHierarchies.DataSource = table;
    cmbHierarchies.EndUpdate();

    //Add empty row
    DataRow row = table.NewRow();
    table.Rows.InsertAt(row, 0);
    cmbHierarchies.SelectedIndex = 0;
0
Zen

Euh, ne pouvez-vous pas simplement ajouter un élément par défaut à la ComboBox après la liaison de données?

0
flipdoubt

J'ai eu un défi similaire. Dans le cadre de l'événement de chargement de formulaire, j'ai défini le SelectedIndex du contrôle sur -1. 

c'est à dire

private void Form1_Load(object sender, EventArgs e)
{         
    this.TableAdapter.Fill(this.dsListOfCampaigns.EvolveCampaignTargetListMasterInfo);
    this.comboCampaignID.SelectedIndex = -1;
}

Effectivement, la liste déroulante est remplie et le premier élément est sélectionné. Ensuite, l'élément est désélectionné. Peut ne pas être une solution viable pour tous les cas.

0
Trevor

Au lieu d’ajouter une nouvelle ligne à votre variable de données, liez simplement les données à la liste déroulante et, au chargement, définissez SelectedIndex sur -1. Cela entraînera une sélection nulle jusqu'à ce que l'utilisateur sélectionne un élément. 

Je l'ai extrait d'un de mes projets en cours. 

        Attorney_List_CB.DataSource = DA_Attorney_List.BS.DataSource;
        Attorney_List_CB.DisplayMember = "Attorney Name";
        Attorney_List_CB.SelectedIndex = -1;      

Afin d'effacer la sélection, j'insère généralement un bouton qui redéfinit le SelectedIndex sur -1. 

    private void Clear_Selection_BTN_Click(object sender, EventArgs e)
    {
        Attorney_List_CB.SelectedIndex = -1;  // Clears user selection
    }

Enfin, une fois que j'ai validé les données de mon formulaire, si le SelectedIndex de n'importe quelle liste déroulante est -1, il est ignoré, ou je générerai un type de valeur par défaut tel que "N/A" ou tout ce dont j'ai besoin dans les circonstances. 

0
joshman1019

this.recieptDateTimePicker.SelectedIndex = -1; this.dateCompoBox.SelectedIndex = -1;

0

Je voudrais lier les données puis insérer un élément vierge à la position 0 en utilisant la méthode ComboxBox.Items.Insert. Semblable à ce que suggère flipdoubt, mais ajoute l'élément au sommet.

0
Arry