C# DataGridView 绑定到字典

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/854953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:20:18  来源:igfitidea点击:

DataGridView bound to a Dictionary

c#.netwinformsdatagridviewdictionary

提问by WillH

I have a Dictionarythat contains items and prices. The items are unique but slowly get added and updated through the lifetime of the application (that is, I don't know the item strings in advance). I would like to bind this structure to a DataGridView, so I can show updates on my Form, something like:

我有一个Dictionary包含项目和价格的。这些项目是独一无二的,但会在应用程序的整个生命周期中慢慢添加和更新(也就是说,我事先不知道项目字符串)。我想将此结构绑定到 DataGridView,以便我可以在我的表单上显示更新,例如:

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

But cannot, since Dictionarydoes not implement IList(or IListSource, IBindingList, or IBindingListView).

但不能,因为Dictionary没有实现IList(or IListSource, IBindingList, or IBindingListView)。

Is there a way to achieve this? I need to keep a unique list of items, but also update the price for an existing item, so a Dictionaryis the ideal data structure I think, but I cannot find a way to display the data on my Form.

有没有办法实现这一目标?我需要保留一个唯一的项目列表,还要更新现有项目的价格,所以 aDictionary是我认为的理想数据结构,但我找不到在我的表单上显示数据的方法。



Update:

更新:

Marc's suggestion below works very nicely, but I'm still not sure how to update the DataGridView during execution.

下面 Marc 的建议非常有效,但我仍然不确定如何在执行期间更新 DataGridView。

I have a class-level variable:

我有一个类级变量:

private DictionaryBindingList<string, decimal> bList; 

Then instantiate that in Main():

然后在Main()

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

Then during program execution if a new entry is added to the dictionary:

然后在程序执行期间,如果新条目添加到字典中:

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

I thought that would refresh the DataGridView. Why not?

我认为这会刷新 DataGridView。为什么不?

采纳答案by Marc Gravell

There are a couple of issues with Dictionary; the first is (as you've found) it doesn't implement the necessary IList/IListSource. The second is that there is no guaranteed order to the items (and indeed, no indexer), making random access by index (rather than by key) impossible.

有几个问题Dictionary;第一个是(如您所见)它没有实现必要的IList/ IListSource。第二个是项目没有保证的顺序(实际上,没有索引器),使得通过索引(而不是通过键)进行随机访问是不可能的。

However... it is probably doable with some some smoke and mirrors; something like below:

然而......它可能是可行的一些烟雾和镜子; 像下面这样:

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

    }
}

Note that the use of a custom extension method is entirely optional, and can be removed in C# 2.0, etc. by just using new DictionaryBindingList<string,decimal>(prices)instead.

请注意,自定义扩展方法的使用完全是可选的,并且可以在 C# 2.0 等中删除,只需使用new DictionaryBindingList<string,decimal>(prices)代替即可。

回答by Chris

Make a class like so:

像这样创建一个类:

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

Then make a list of them:

然后列出它们:

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

Then insert them into that list, and databind to the list.

然后将它们插入该列表,并将数据绑定到该列表。

As an aside, if you've got LINQ, I think there's a ToArraymethod that'll simplify all this...

顺便说一句,如果你有 LINQ,我认为有一种ToArray方法可以简化这一切......

回答by Chris

Or, in LINQ, it's nice and quick:

或者,在 LINQ 中,它又好又快:

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

That should then be bindable, to the columns 'Item' and 'Price'.

然后应该可以绑定到“Item”和“Price”列。

To use it as a data source in a grid view, you just have to follow it with ToArray().

要将其用作网格视图中的数据源,您只需在它后面加上ToArray().

dataGridView1.DataSource = _priceDataArray.ToArray();

回答by Vladislav

For Dictionary<TKey, TValue>you can use these keywords for binding: Keyand Value.

因为Dictionary<TKey, TValue>您可以使用这些关键字进行绑定:KeyValue

Here is example for ComboBoxBinding, but it's possible to bind dictionary to DataGridView(set DataPropertyNamefor column to Keyor Value).

这是ComboBox绑定的示例,但可以将字典绑定到DataGridView(将DataPropertyName列设置为KeyValue)。

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

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

回答by adnan umar

i thing this will resolve your problem which i faced few months ago.

我认为这将解决您几个月前遇到的问题。

use dictionay as you want to update item prices and just when u finish updation and want to show in datagrid just do this. hope will help you

使用 dictionay,因为您想更新商品价格,并且当您完成更新并希望在数据网格中显示时,只需执行此操作。希望能帮到你

Grd.DataSource=null;
Grd.DataSource = Dictionary.Values.ToList();

回答by DevQuayle

Probably this is easiest way:

可能这是最简单的方法:

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

If use this you datagridview shall be sortable.

如果使用这个,你的 datagridview 应该是可排序的。

回答by S. Bleier

As an extension to Marc's suggestion, I would like to propose the following solution that will also allow run-time manipulation of the dictionary:

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

回答by David Rath

As an extension of Bleiers DictionaryBindingList I made a small alteration to allow Add values to overwrite existing values. I'm using the method with a WAMP websocket so it would allow me to keep values updated just by updating the collection, next I need to tie events onto the values.

作为 Bleiers DictionaryBindingList 的扩展,我做了一个小改动,允许添加值覆盖现有值。我将该方法与 WAMP websocket 一起使用,这样我就可以通过更新集合来保持值的更新,接下来我需要将事件绑定到值上。

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