DataGridView привязан к словарю - PullRequest
30 голосов
/ 13 мая 2009

У меня есть 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 - это идеальная структура данных, но я не могу найти способ отобразить данные в моей форме.


Обновление:

Предложение Марка, приведенное ниже, работает очень хорошо, но я все еще не уверен, как обновить DataGridView во время выполнения.

У меня есть переменная уровня класса:

private DictionaryBindingList<string, decimal> bList; 

Затем создайте экземпляр этого в Main():

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

Затем во время выполнения программы, если в словарь добавляется новая запись:

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

Я думал, что это обновит DataGridView. Почему нет?

Ответы [ 8 ]

60 голосов
/ 13 мая 2009

Или, в LINQ, это приятно и быстро:

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

Это должно быть привязано к столбцам «Товар» и «Цена».

Чтобы использовать его в качестве источника данных в виде сетки, вам просто нужно следовать за ним с ToArray().

dataGridView1.DataSource = _priceDataArray.ToArray();
23 голосов
/ 13 мая 2009

Есть пара проблем с 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();
            }
        }

    }
}

Обратите внимание, что использование пользовательского метода расширения не является обязательным и может быть удалено в C # 2.0 и т. Д., Просто взамен new DictionaryBindingList<string,decimal>(prices).

5 голосов
/ 25 февраля 2015

Наверное, это самый простой способ:

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

Если вы используете это, ваша таблица данных должна сортироваться.

3 голосов
/ 19 февраля 2014

Я думаю, это решит вашу проблему, с которой я столкнулся несколько месяцев назад.

используйте dictionay, если хотите обновить цены на товары, а когда закончите обновление и хотите показать в сетке данных, просто сделайте это. надежда поможет вам

Grd.DataSource=null;
Grd.DataSource = Dictionary.Values.ToList();
2 голосов
/ 13 мая 2010

Для Dictionary<TKey, TValue> вы можете использовать следующие ключевые слова для привязки: Key и Value.

Вот пример для ComboBox Binding, но можно связать словарь с DataGridView (установите DataPropertyName для столбца на Key или Value).

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

    ComboBox1.ValueMember = "Key";
    ComboBox1.DisplayMember = "Value";
1 голос
/ 06 июня 2016

В качестве дополнения к предложению Марка я хотел бы предложить следующее решение, которое также позволит манипулировать словарем во время выполнения:

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 голос
/ 13 мая 2009

Создайте класс так:

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, который упростит все это ...

0 голосов
/ 10 апреля 2017

В качестве расширения Bleiers DictionaryBindingList я внес небольшое изменение, чтобы позволить значениям Add перезаписывать существующие значения. Я использую метод с веб-сокетом 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));
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...