2009-05-12 46 views
25

Tôi có một Dictionary có chứa các mặt hàng và giá cả. Các mục này là duy nhất nhưng từ từ được thêm vào và cập nhật thông qua toàn bộ thời gian của ứng dụng (tức là, tôi không biết các chuỗi mục trước). Tôi muốn để ràng buộc cấu trúc này cho một DataGridView, vì vậy tôi có thể hiển thị các cập nhật về Mẫu của tôi, một cái gì đó như:DataGridView ràng buộc với một từ điển

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

Nhưng không thể, kể từ Dictionary không thực hiện IList (hoặc IListSource, IBindingList, hoặc IBindingListView).

Có cách nào để đạt được điều này không? Tôi cần giữ một danh sách các mục duy nhất, nhưng cũng cập nhật giá cho một mục hiện có, do đó, Dictionary là cấu trúc dữ liệu lý tưởng mà tôi nghĩ, nhưng tôi không thể tìm cách hiển thị dữ liệu trên Biểu mẫu của mình.


Cập nhật:

Marc gợi ý dưới đây hoạt động rất độc đáo, nhưng tôi vẫn không chắc chắn làm thế nào để cập nhật DataGridView trong khi thực hiện.

Tôi có một biến lớp cấp:

private DictionaryBindingList<string, decimal> bList; 

Sau đó nhanh chóng rằng trong Main():

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

Sau đó, trong quá trình thực hiện chương trình nếu một mục mới được thêm vào từ điển:

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

Tôi nghĩ rằng sẽ làm mới DataGridView. Tại sao không?

+3

như một sang một bên - cho "giá", bạn thường nên sử dụng số thập phân, chứ không phải gấp đôi –

+0

Cập nhật lại nhận xét của bạn; trừ khi chúng tôi phân loại từ điển <,>, không có cách nào đơn giản để biết rằng bạn đã thêm một mục; thay vì điều này, tôi đã thêm phương thức Reset() (sử dụng ví dụ) để điền lại danh sách. Xem nút ở dưới cùng của biểu mẫu để biết ví dụ. –

Trả lời

21

Có một số vấn đề với Dictionary; đầu tiên là (như bạn đã tìm thấy) nó không thực hiện IList/IListSource cần thiết. Thứ hai là không có thứ tự đảm bảo cho các mục (và thực sự, không có người lập chỉ mục), làm cho truy cập ngẫu nhiên bằng chỉ mục (chứ không phải bằng khóa) là không thể.

Tuy nhiên ... có thể doable với một số khói và gương; một cái gì đó như dưới đây:

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

    } 
} 

Lưu ý rằng việc sử dụng một phương pháp mở rộng thông thường là hoàn toàn tùy chọn, và có thể được loại bỏ trong C# 2.0, vv bằng cách chỉ sử dụng new DictionaryBindingList<string,decimal>(prices) để thay thế.

1

Tạo một lớp như vậy:

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

Sau đó, lập danh sách trong số họ:

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

Sau đó chèn chúng vào danh sách đó, và DataBind vào danh sách.

Là một sang một bên, nếu bạn đã có LINQ, tôi nghĩ rằng có một phương pháp ToArray sẽ đơn giản hóa tất cả điều này ...

+0

Danh sách không dễ dàng thực thi tính duy nhất và không dễ dàng cập nhật giá trị trong cấu trúc danh sách như vậy, đó là lý do tại sao tôi muốn sử dụng Từ điển. Cảm ơn bạn. – WillH

47

Hoặc, trong LINQ, nó đẹp và nhanh chóng:

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

sau đó Đó nên là bindable, để các cột 'mục' và 'Giá'.

Để sử dụng làm nguồn dữ liệu trong chế độ xem lưới, bạn chỉ cần theo dõi nó với ToArray().

dataGridView1.DataSource = _priceDataArray.ToArray(); 
+7

+1 Đơn giản hơn và sạch hơn so với câu trả lời được chấp nhận. – GuiSim

+2

+1 Làm việc cho tôi! Dễ như bánh (không phải pi)! –

+0

Dễ như bánh? Tôi thực sự tìm thấy chiếc bánh khá khó khăn để có được quyền: P –

2

Đối Dictionary<TKey, TValue> bạn có thể sử dụng các từ khóa cho ràng buộc: KeyValue.

Dưới đây là ví dụ cho ComboBox Binding, nhưng nó có thể để ràng buộc từ điển để DataGridView (set DataPropertyName cho cột để Key hoặc Value).

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

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

điều này sẽ giải quyết được sự cố của bạn mà tôi gặp phải vài tháng trước.

sử dụng từ điển khi bạn muốn cập nhật giá mặt hàng và chỉ khi u hoàn thành cập nhật và muốn hiển thị trong datagrid chỉ cần làm điều này. hy vọng sẽ giúp bạn

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

Có lẽ đây là cách đơn giản nhất:

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

Nếu sử dụng này, bạn DataGridView được sắp xếp.

1

Là một phần mở rộng cho đề nghị của Marc, tôi muốn đề xuất các giải pháp sau đó cũng sẽ cho phép thao tác thời gian chạy của từ điển:

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

Là một phần mở rộng của Bleiers DictionaryBindingList tôi đã thực hiện một thay đổi nhỏ để cho phép Thêm giá trị để ghi đè các giá trị hiện tại. Tôi đang sử dụng phương pháp với một webcard WAMP vì vậy nó sẽ cho phép tôi giữ các giá trị được cập nhật chỉ bằng cách cập nhật bộ sưu tập, tiếp theo tôi cần kết hợp các sự kiện vào các giá trị.

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)); 
    } 
Các vấn đề liên quan