DataGridView lié à un dictionnaire

j'ai un Dictionary qui contient les articles et les prix. Les éléments sont uniques mais sont lentement ajoutés et mis à jour au cours de la durée de vie de l'application (c'est-à-dire, Je ne sais pas les chaînes d'éléments à l'avance). Je voudrais lier cette structure à un DataGridView, de sorte que je puisse afficher des mises à jour sur mon formulaire, quelque chose comme:

Dictionary<string, double> _priceData = new Dictionary<string, double>();
BindingSource _bindingSource = new BindingSource();
dataGridView1.DataSource = _bindingSource;
_bindingSource.DataSource = _priceData;

mais ne peut pas, depuis Dictionary n'implémente pas IList (ou IListSource,IBindingList, ou IBindingListView).

Est-il un moyen pour y parvenir? J'ai besoin de Gardez une liste unique d'articles, mais aussi mettre à jour le prix d'un article existant, donc un Dictionary est la structure de données idéale je pense, mais je ne peux pas trouver un moyen d'afficher les données sur mon formulaire.


mise à Jour:

la suggestion de Marc ci-dessous fonctionne très bien, mais je ne sais toujours pas comment mettre à jour le DataGridView pendant l'exécution.

j'ai une classe de niveau variable:

private DictionaryBindingList<string, decimal> bList; 

puis instanciez que dans Main():

bList = new DictionaryBindingList<string,decimal>(prices); 
dgv.DataSource = bList; 

Ensuite, pendant l'exécution du programme si une nouvelle entrée est ajoutée au dictionnaire:

prices.Add("foobar", 234.56M); bList.ResetBindings(); 

j'ai pensé que cela rafraîchirait la vue des données. Pourquoi pas?

27
demandé sur Peter Mortensen 2009-05-13 01:29:39

8 réponses

il y a quelques problèmes avec Dictionary; le premier est (comme vous l'avez constaté) qu'il n'a pas mis en œuvre les IList/IListSource. La seconde est qu'il n'y a pas d'ordre garanti aux articles (et, en fait, pas d'indexeur), ce qui rend l'accès aléatoire par index (plutôt que par clé) impossible.

cependant... il est probablement faisable avec un peu de fumée et des miroirs; quelque chose comme ci-dessous:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main()
    {
        Dictionary<string, decimal> prices =
            new Dictionary<string, decimal>();
        prices.Add("foo", 123.45M);
        prices.Add("bar", 678.90M);

        Application.EnableVisualStyles();
        Form form = new Form();
        DataGridView dgv = new DataGridView();
        dgv.Dock = DockStyle.Fill;
        form.Controls.Add(dgv);
        var bl = prices.ToBindingList();
        dgv.DataSource = bl;
        Button btn = new Button();
        btn.Dock = DockStyle.Bottom;
        btn.Click += delegate
        {
            prices.Add(new Random().Next().ToString(), 0.1M);
            bl.Reset();
        };
        form.Controls.Add(btn);
        Application.Run(form);
    }

    public static DictionaryBindingList<TKey, TValue>
        ToBindingList<TKey, TValue>(this IDictionary<TKey, TValue> data)
    {
        return new DictionaryBindingList<TKey, TValue>(data);
    }
    public sealed class Pair<TKey, TValue>
    {
        private readonly TKey key;
        private readonly IDictionary<TKey, TValue> data;
        public Pair(TKey key, IDictionary<TKey, TValue> data)
        {
            this.key = key;
            this.data = data;
        }
        public TKey Key { get { return key; } }
        public TValue Value
        {
            get
            {
                TValue value;
                data.TryGetValue(key, out value);
                return value;
            }
            set { data[key] = value; }
        }
    }
    public class DictionaryBindingList<TKey, TValue>
        : BindingList<Pair<TKey, TValue>>
    {
        private readonly IDictionary<TKey, TValue> data;
        public DictionaryBindingList(IDictionary<TKey, TValue> data)
        {
            this.data = data;
            Reset();
        }
        public void Reset()
        {
            bool oldRaise = RaiseListChangedEvents;
            RaiseListChangedEvents = false;
            try
            {
                Clear();
                foreach (TKey key in data.Keys)
                {
                    Add(new Pair<TKey, TValue>(key, data));
                }
            }
            finally
            {
                RaiseListChangedEvents = oldRaise;
                ResetBindings();
            }
        }

    }
}

Notez que l'utilisation d'une méthode d'extension personnalisée est entièrement facultative et peut être retiré en C# 2.0, etc. en utilisant simplement les new DictionaryBindingList<string,decimal>(prices) à la place.

21
répondu Marc Gravell 2011-08-23 18:12:17

Ou, dans LINQ, c'est agréable et rapide:

var _priceDataArray = from row in _priceData select new { Item = row.Key, Price = row.Value };

qui devrait alors pouvoir être relié aux colonnes "article" et "prix".

Pour l'utiliser comme source de données dans une vue de grille, vous avez juste à suivre avec ToArray().

dataGridView1.DataSource = _priceDataArray.ToArray();
54
répondu Chris 2011-08-23 18:14:01

probablement c'est la façon la plus facile:

Dictionary<char, double> myList = new Dictionary<char, double>();

        dataGridView1.Columns.Add("Key", "KEY");
        dataGridView1.Columns.Add("Values", "VALUES");

        foreach (KeyValuePair<char,double> item in , myList)
        {
            dataGridView1.Rows.Add(item.Key, item.Value);
        }

Si ce datagridview doit être triée.

5
répondu DevQuayle 2015-02-25 07:16:56

Dictionary<TKey, TValue> vous pouvez utiliser ces mots-clés pour la liaison: Key et Value.

voici un exemple pour ComboBox Liaison, mais il est possible de lier dictionnaire DataGridView (set DataPropertyName pour la colonne Key ou Value).

    ComboBox1.DataSource =
        new BindingSource(Pricelevel.GetPricelevels(), null); // GetPricelevels() returns Dictionary<string, string>

    ComboBox1.ValueMember = "Key";
    ComboBox1.DisplayMember = "Value";
2
répondu Vladislav 2010-05-13 09:03:59

je pense que cela va résoudre votre problème auquel j'ai fait face il y a quelques mois.

Utilisez dictionay que vous voulez mettre à jour les prix des articles et juste quand u terminer la mise à jour et que vous voulez montrer dans datagrid faites juste cela. espérons, vous aideront à

Grd.DataSource=null;
Grd.DataSource = Dictionary.Values.ToList();
2
répondu adnan umar 2015-05-19 03:52:32

Faire une classe comme ceci:

class MyRow
{
    public string key;
    public double value;
    public string Key {get {return key;}}
    public string Value {get {return value;}}
}

alors faites une liste d'entre eux:

List<MyRow> rows = new List<MyRow>();

puis insérez - les dans cette liste, et databind à la liste.

à part, si vous avez LINQ, je pense qu'il y a un ToArray méthode qui simplifiera tout cela...

1
répondu Chris 2011-08-23 18:12:56

en prolongement de la suggestion de Marc, j'aimerais proposer la solution suivante qui permettra également de manipuler le dictionnaire en cours d'exécution:

public class DictionaryBindingList<TKey, TValue> : BindingList<KeyValuePair<TKey, TValue>>
{
    public readonly IDictionary<TKey, TValue> Dictionary;
    public DictionaryBindingList()
    {
        Dictionary = new Dictionary<TKey, TValue>();
    }

    public void Add(TKey key, TValue value)
    {
        base.Add(new KeyValuePair<TKey, TValue>(key, value));
    }

    public void Remove(TKey key)
    {
        var item = this.First(x => x.Key.Equals(key));
        base.Remove(item);
    }

    protected override void InsertItem(int index, KeyValuePair<TKey, TValue> item)
    {
        Dictionary.Add(item.Key, item.Value);
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
        Dictionary.Remove(this[index].Key);
        base.RemoveItem(index);
    }

    public int IndexOf(TKey key)
    {
        var item = this.FirstOrDefault(x => x.Key.Equals(key));
        return item.Equals(null) ? -1 : base.IndexOf(item);
    }
}
1
répondu S. Bleier 2016-06-06 02:34:50

comme extension de Bleiers DictionaryBindingList j'ai fait une petite modification pour permettre D'ajouter des valeurs pour écraser les valeurs existantes. J'utilise la méthode avec un websocket WAMP pour garder les valeurs à jour juste en mettant à jour la collection, ensuite je dois lier les événements sur les valeurs.

    public void Add(TKey key, TValue value)
    {
        if (Dictionary.ContainsKey(key))
        {
            int position = IndexOf(key);
            Dictionary.Remove(key);
            Remove(key);
            InsertItem(position, new KeyValuePair<TKey, TValue>(key, value));
            return;
        }
        base.Add(new KeyValuePair<TKey, TValue>(key, value));
    }
0
répondu David Rath 2017-04-10 14:50:40