it-swarm.com.de

DataGridView, das an ein Wörterbuch gebunden ist

Ich habe eine Dictionary, die Artikel und Preise enthält. Die Elemente sind eindeutig, werden jedoch im Laufe der Lebensdauer der Anwendung langsam hinzugefügt und aktualisiert (dh ich kenne die Elementzeichenfolgen nicht im Voraus). Ich möchte diese Struktur an eine DataGridView binden, damit ich Aktualisierungen in meinem Formular anzeigen kann, etwa:

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

Kann jedoch nicht, da DictionaryIList (oder IListSource, IBindingList oder IBindingListView) nicht implementiert.

Gibt es einen Weg, dies zu erreichen? Ich muss eine eindeutige Liste von Artikeln führen, aber auch den Preis für einen vorhandenen Artikel aktualisieren. Daher ist Dictionary die ideale Datenstruktur, denke ich, aber ich finde keine Möglichkeit, die Daten in meinem Formular anzuzeigen.


Aktualisieren:

Marc 's Vorschlag unten funktioniert sehr gut, aber ich bin mir immer noch nicht sicher, wie ich DataGridView während der Ausführung aktualisieren sollte.

Ich habe eine Variable auf Klassenebene: 

private DictionaryBindingList<string, decimal> bList; 

Dann instanziieren Sie das in Main():

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

Dann während der Programmausführung, wenn ein neuer Eintrag zum Wörterbuch hinzugefügt wird: 

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

Ich dachte, das würde die DataGridView aktualisieren. Warum nicht?

27
WillH

Es gibt einige Probleme mit Dictionary; Die erste ist (wie Sie herausgefunden haben) nicht die notwendige IList/IListSource. Die zweite ist, dass es keine garantierte Reihenfolge für die Elemente gibt (und auch keinen Indexer), was den wahlfreien Zugriff nach Index (und nicht nach Schlüssel) unmöglich macht.

Allerdings ... ist es wahrscheinlich mit etwas Rauch und Spiegeln machbar; etwas wie unten:

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();
            }
        }

    }
}

Beachten Sie, dass die Verwendung einer benutzerdefinierten Erweiterungsmethode völlig optional ist und in C # 2.0 usw. entfernt werden kann, indem Sie stattdessen new DictionaryBindingList<string,decimal>(prices) verwenden.

22
Marc Gravell

Oder in LINQ ist es schön und schnell:

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

Das sollte dann an die Spalten 'Artikel' und 'Preis' gebunden werden können.

Um es als Datenquelle in einer Rasteransicht zu verwenden, müssen Sie es nur mit ToArray() folgen.

dataGridView1.DataSource = _priceDataArray.ToArray();
55
Chris

Wahrscheinlich ist dies der einfachste Weg:

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);
        }

Wenn Sie dies verwenden, muss Ihr Datagridview sortierbar sein.

5
DevQuayle

ich denke, das wird Ihr Problem lösen, vor dem ich vor einigen Monaten stand.

verwenden Sie dictionay, da Sie die Artikelpreise aktualisieren möchten. Wenn Sie die Aktualisierung abgeschlossen haben und in datagrid angezeigt werden möchten, tun Sie dies einfach. hoffe es wird dir helfen

Grd.DataSource=null;
Grd.DataSource = Dictionary.Values.ToList();
3
adnan umar

Für Dictionary<TKey, TValue> können Sie diese Schlüsselwörter für die Bindung verwenden: Key und Value.

Hier ist ein Beispiel für ComboBox Binding, aber Sie können das Wörterbuch an DataGridView binden (setzen Sie DataPropertyName für die Spalte auf Key oder Value).

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

    ComboBox1.ValueMember = "Key";
    ComboBox1.DisplayMember = "Value";
2
Vladislav

Machen Sie eine Klasse wie folgt:

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

Dann machen Sie eine Liste von ihnen:

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

Dann fügen Sie sie in diese Liste ein und databind in die Liste.

Nebenbei gesagt, wenn Sie LINQ haben, denke ich, gibt es eine ToArray-Methode, die all dies vereinfacht ...

1
Chris

Als Erweiterung zu Marcs Vorschlag möchte ich folgende Lösung vorschlagen, die auch eine Laufzeitmanipulation des Wörterbuchs ermöglicht:

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
S. Bleier

Als Erweiterung von Bleiers DictionaryBindingList habe ich eine kleine Änderung vorgenommen, um das Hinzufügen von Werten zum Überschreiben vorhandener Werte zuzulassen .. Ich verwende die Methode mit einem WAMP-Websocket, sodass ich Werte aktualisieren kann, indem ich die Auflistung als nächstes aktualisiere müssen Ereignisse an die Werte binden.

    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
David Rath