DataGridView привязан к словарю
У меня есть Dictionary
, который содержит элементы и цены. Элементы уникальны, но медленно добавляются и обновляются через время жизни приложения (то есть я не знаю строки элемента заранее). Я хотел бы привязать эту структуру к DataGridView, поэтому я могу показывать обновления в моей форме, например:
Dictionary<string, double> _priceData = new Dictionary<string, double>();
BindingSource _bindingSource = new BindingSource();
dataGridView1.DataSource = _bindingSource;
_bindingSource.DataSource = _priceData;
Но не может, поскольку Dictionary
не реализует IList
(или IListSource
, IBindingList
, или IBindingListView
).
Есть ли способ достичь этого? Мне нужно сохранить уникальный список элементов, но также обновить цену для существующего элемента, поэтому Dictionary
- идеальная структура данных, я думаю, но я не могу найти способ отображения данных в моей форме.
Update:
Предложение Marc ниже работает очень хорошо, но я все еще не уверен, как обновить DataGridView во время выполнения.
У меня есть переменная уровня класса:
private DictionaryBindingList<string, decimal> bList;
Затем укажите, что в Main()
:
bList = new DictionaryBindingList<string,decimal>(prices);
dgv.DataSource = bList;
Затем во время выполнения программы, если в словарь добавлена новая запись:
prices.Add("foobar", 234.56M); bList.ResetBindings();
Я думал, что обновит DataGridView. Почему бы и нет?
Ответы
Ответ 1
Есть пара проблем с Dictionary
; первый (как вы уже нашли), не выполняет необходимые IList
/IListSource
. Во-вторых, нет гарантированного порядка для элементов (и даже нет индексатора), что делает невозможным произвольный доступ по индексу (а не по ключу).
Однако... возможно, это возможно с некоторыми дымом и зеркалами; что-то вроде ниже:
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();
}
}
}
}
Обратите внимание, что использование настраиваемого метода расширения является необязательным и может быть удалено в С# 2.0 и т.д., просто используя new DictionaryBindingList<string,decimal>(prices)
.
Ответ 2
Или, в LINQ, это приятно и быстро:
var _priceDataArray = from row in _priceData select new { Item = row.Key, Price = row.Value };
Это должно быть привязано к столбцам "Item" и "Price".
Чтобы использовать его в качестве источника данных в виде сетки, вам просто нужно следовать ему с помощью ToArray()
.
dataGridView1.DataSource = _priceDataArray.ToArray();
Ответ 3
Возможно, это самый простой способ:
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);
}
Если вы используете это, datagridview будет сортироваться.
Ответ 4
Для Dictionary<TKey, TValue>
вы можете использовать эти ключевые слова для привязки: Key
и Value
.
Вот пример привязки ComboBox
, но можно привязать словарь к DataGridView
(установите DataPropertyName
для столбца на Key
или Value
).
ComboBox1.DataSource =
new BindingSource(Pricelevel.GetPricelevels(), null); // GetPricelevels() returns Dictionary<string, string>
ComboBox1.ValueMember = "Key";
ComboBox1.DisplayMember = "Value";
Ответ 5
i, это решит вашу проблему, с которой я столкнулся несколько месяцев назад.
используйте dictionay, поскольку вы хотите обновить цены товара, и только когда вы закончите обновление и хотите показать в datagrid, просто сделайте это. надеюсь, поможет вам
Grd.DataSource=null;
Grd.DataSource = Dictionary.Values.ToList();
Ответ 6
Сделайте такой класс:
class MyRow
{
public string key;
public double value;
public string Key {get {return key;}}
public string Value {get {return value;}}
}
Затем создайте список из них:
List<MyRow> rows = new List<MyRow>();
Затем вставьте их в этот список и привяжите данные к списку.
В стороне, если у вас есть LINQ, я думаю, что существует метод ToArray
, который упростит все это...
Ответ 7
Как расширение предложения Marc, я хотел бы предложить следующее решение, которое также позволит манипулировать словарем во время выполнения:
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);
}
}
Ответ 8
Как расширение Bleiers DictionaryBindingList, я сделал небольшое изменение, чтобы позволить добавлять значения для перезаписывания существующих значений.
Я использую этот метод с веб-узлом WAMP, поэтому он позволяет мне обновлять значения только путем обновления коллекции, а затем мне нужно связать события со значениями.
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));
}