我有一个对象列表。每个对象都有一个字典作为其属性之一:
public Dictionary<string, string> testArguments = new Dictionary<string, string>();
当我启动我的应用程序时,它会遍历此对象列表并在checkedListBox
中显示对象名称。我想要实现的目标如下:
testArguments
对的列表。经过一番研究后,我发现了DataGridView
班,据说应该可以帮助我解决上述问题。但是我未能达到预期的效果。而且,根据我所读的here,它似乎不可能。是这样吗?如果是的话,最好的办法是什么?
P.S。请注意,我并没有具体询问如何让DataGridView
在这种情况下工作。我正在寻找任何可视化显示/代表类字典数据结构的通用解决方案。它甚至不必是一个合适的Dictionary
。任何提供解决方案的方法都被接受。
答案 0 :(得分:1)
这里的根本问题是Dictionary<TKey, TValue>
类不可绑定。它没有实现正确绑定所需的接口,并且有充分的理由:它是一个固有的无序集合,而使用绑定的UI控件通常假设有序集合。
也就是说,如果你愿意,你仍然可以使用DataGridView
。你只需要手动完成任务:
DataGridView
控件,并使用Add()
对象的Rows
属性上的DataGridView
方法重载之一。ReadOnly
属性设置为true
,以便用户只能编辑值。DataGridView.CellEndEdit
事件以接收用户已编辑单元格的通知。引发事件时,请根据需要进行处理(例如,将值复制回字典对象,根据需要更新数据的任何其他视图等)。据推测,您还可以将AllowUserToAddRows
和AllowUserToDeleteRows
属性设置为false
。特别是添加行会很棘手,因为如果将键列设置为只读,则用户将无法为新行输入键值。所以你可能根本不允许这样做。
您可能还希望继续订购字典数据,同时将其添加到DataGridView
,例如基于键值,用户可以更轻松地导航数据。
您可能会想到并试图将字典对象包装在一些可绑定的实现中。例如。将其包装在IList<KeyValuePair<string, string>>
中,然后将其传递给BindingList<KeyValuePair<string, string>>
,或者自己实际实现整个IBindingList
界面。但这样做可能会涉及到一些令人头痛的事情,再次大多数情况下,字典本身是无序的,因此编写一个可绑定的垫片会涉及强加和维持某种秩序。
一方面,实施IList<T>
比实施IBindingList
更简单。但另一方面,实现IBindingList
可以让您更好地控制字典的确切行为和视图。无论哪种方式,都会很痛苦。直接处理DataGridView
可能更容易。
的附录:强>
对于它的价值,这里有一个简单的例子,说明普通的,手动绑定的实现可能是什么样的:
public partial class Form1 : Form
{
private Dictionary<string, string> _dictionary = new Dictionary<string, string>();
public Form1(Dictionary<string, string> dictionary)
{
InitializeComponent();
_dictionary = dictionary;
_UpdateDictionaryView();
}
private void _UpdateDictionaryView()
{
_UpdateDictionaryListView();
dataGridView1.Rows.Clear();
dataGridView1.Rows.AddRange(_dictionary.OrderBy(kvp => kvp.Key).Select(kvp =>
{
DataGridViewRow row = new DataGridViewRow();
row.CreateCells(dataGridView1, kvp.Key, kvp.Value);
return row;
}).ToArray());
}
private void _UpdateDictionaryListView()
{
listBoxDictionaryView.Items.Clear();
listBoxDictionaryView.Items.AddRange(
_dictionary.OrderBy(kvp => kvp.Key)
.Select(kvp => string.Format("Key: \"{0}\"; Value: \"{1}\"", kvp.Key, kvp.Value))
.ToArray());
}
private void buttonAddKeyValuePair_Click(object sender, EventArgs e)
{
_dictionary.Add(tboxKey.Text, tboxValue.Text);
_UpdateDictionaryView();
}
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
DataGridViewRow row = dataGridView1.Rows[e.RowIndex];
_dictionary[(string)row.Cells[0].Value] = (string)row.Cells[1].Value;
_UpdateDictionaryView();
_SaveDictionaryData();
}
}
Form1.Designer.cs:(为方便起见)
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.buttonAddKeyValuePair = new System.Windows.Forms.Button();
this.tboxValue = new System.Windows.Forms.TextBox();
this.tboxKey = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.listBoxDictionaryView = new System.Windows.Forms.ListBox();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.Key = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Value = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(6, 25);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(36, 17);
this.label1.TabIndex = 0;
this.label1.Text = "Key:";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.buttonAddKeyValuePair);
this.groupBox1.Controls.Add(this.tboxValue);
this.groupBox1.Controls.Add(this.tboxKey);
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Location = new System.Drawing.Point(13, 13);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(182, 113);
this.groupBox1.TabIndex = 1;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "New key-value pair";
//
// buttonAddKeyValuePair
//
this.buttonAddKeyValuePair.Location = new System.Drawing.Point(93, 78);
this.buttonAddKeyValuePair.Name = "buttonAddKeyValuePair";
this.buttonAddKeyValuePair.Size = new System.Drawing.Size(75, 23);
this.buttonAddKeyValuePair.TabIndex = 2;
this.buttonAddKeyValuePair.Text = "Add";
this.buttonAddKeyValuePair.UseVisualStyleBackColor = true;
this.buttonAddKeyValuePair.Click += new System.EventHandler(this.buttonAddKeyValuePair_Click);
//
// tboxValue
//
this.tboxValue.Location = new System.Drawing.Point(68, 50);
this.tboxValue.Name = "tboxValue";
this.tboxValue.Size = new System.Drawing.Size(100, 22);
this.tboxValue.TabIndex = 1;
//
// tboxKey
//
this.tboxKey.Location = new System.Drawing.Point(68, 22);
this.tboxKey.Name = "tboxKey";
this.tboxKey.Size = new System.Drawing.Size(100, 22);
this.tboxKey.TabIndex = 0;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(6, 53);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(48, 17);
this.label2.TabIndex = 0;
this.label2.Text = "Value:";
//
// listBoxDictionaryView
//
this.listBoxDictionaryView.FormattingEnabled = true;
this.listBoxDictionaryView.ItemHeight = 16;
this.listBoxDictionaryView.Location = new System.Drawing.Point(13, 133);
this.listBoxDictionaryView.Name = "listBoxDictionaryView";
this.listBoxDictionaryView.Size = new System.Drawing.Size(182, 180);
this.listBoxDictionaryView.TabIndex = 2;
//
// dataGridView1
//
this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Key,
this.Value});
this.dataGridView1.Location = new System.Drawing.Point(201, 10);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.RowTemplate.Height = 24;
this.dataGridView1.Size = new System.Drawing.Size(382, 303);
this.dataGridView1.TabIndex = 3;
this.dataGridView1.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellEndEdit);
//
// Key
//
this.Key.HeaderText = "Key";
this.Key.Name = "Key";
this.Key.ReadOnly = true;
//
// Value
//
this.Value.HeaderText = "Value";
this.Value.Name = "Value";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(595, 327);
this.Controls.Add(this.dataGridView1);
this.Controls.Add(this.listBoxDictionaryView);
this.Controls.Add(this.groupBox1);
this.Name = "Form1";
this.Text = "Form1";
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Button buttonAddKeyValuePair;
private System.Windows.Forms.TextBox tboxValue;
private System.Windows.Forms.TextBox tboxKey;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ListBox listBoxDictionaryView;
private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.DataGridViewTextBoxColumn Key;
private System.Windows.Forms.DataGridViewTextBoxColumn Value;
}
基本上是我上面提出的确切模板。请注意,代码会在任何更改时从头开始重建视图(ListBox
和DataGridView
)。对于相对较少数量的字典条目(比如多达数百个),我认为这应该没问题。如果您需要支持更多,那么您可能希望更有效地实现视图更新。
对于笑话,我还尝试在IList<T>
实现中包装字典,然后将其传递给BindingSource<T>
,以用作视图的DataSource
,随意强制执行命令通过维护并行列表在字典上。如果有人真的需要一个通用的解决方案,我可以看到这种方法如何最终有用。但是在这个特定的例子中,与上面相比,它有很多额外的代码,并没有真正提供任何附加功能。
我只实现了IList<T>
,但我想如果有人真的想要这种通用解决方案,那么继续实施IBindingList
可能是值得花时间和精力的。这样可以让您更好地控制精确的绑定行为,并且可能比基于IList<T>
/ BindingSource<T>
的{{1}}方法更少(或更少)hacky。