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 Dictionary
IList
(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?
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.
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();
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.
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();
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";
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 ...
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);
}
}
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));
}