如何在WinForms应用程序中表示/可视化数据对

时间:2015-02-10 05:50:15

标签: c# winforms dictionary

我有一个对象列表。每个对象都有一个字典作为其属性之一:

public Dictionary<string, string> testArguments = new Dictionary<string, string>();

当我启动我的应用程序时,它会遍历此对象列表并在checkedListBox中显示对象名称。我想要实现的目标如下:

  • 当我选择(点击)列表中的对象名称时,我想获取并显示其testArguments对的列表。
  • 我希望这些对的值(而不是键)是可编辑的。在稍后阶段,我想使用序列化来启用这些值的保存/加载。

经过一番研究后,我发现了DataGridView班,据说应该可以帮助我解决上述问题。但是我未能达到预期的效果。而且,根据我所读的here,它似乎不可能。是这样吗?如果是的话,最好的办法是什么?

P.S。请注意,我并没有具体询问如何让DataGridView在这种情况下工作。我正在寻找任何可视化显示/代表类字典数据结构的通用解决方案。它甚至不必是一个合适的Dictionary。任何提供解决方案的方法都被接受。

1 个答案:

答案 0 :(得分:1)

这里的根本问题是Dictionary<TKey, TValue>类不可绑定。它没有实现正确绑定所需的接口,并且有充分的理由:它是一个固有的无序集合,而使用绑定的UI控件通常假设有序集合。

也就是说,如果你愿意,你仍然可以使用DataGridView。你只需要手动完成任务:

  1. 通过枚举字典填充DataGridView控件,并使用Add()对象的Rows属性上的DataGridView方法重载之一。
  2. 将包含键的列的ReadOnly属性设置为true,以便用户只能编辑值。
  3. 处理DataGridView.CellEndEdit事件以接收用户已编辑单元格的通知。引发事件时,请根据需要进行处理(例如,将值复制回字典对象,根据需要更新数据的任何其他视图等)。
  4. 据推测,您还可以将AllowUserToAddRowsAllowUserToDeleteRows属性设置为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;
    }
    

    基本上是我上面提出的确切模板。请注意,代码会在任何更改时从头开始重建视图(ListBoxDataGridView)。对于相对较少数量的字典条目(比如多达数百个),我认为这应该没问题。如果您需要支持更多,那么您可能希望更有效地实现视图更新。

    对于笑话,我还尝试在IList<T>实现中包装字典,然后将其传递给BindingSource<T>,以用作视图的DataSource,随意强制执行命令通过维护并行列表在字典上。如果有人真的需要一个通用的解决方案,我可以看到这种方法如何最终有用。但是在这个特定的例子中,与上面相比,它有很多额外的代码,并没有真正提供任何附加功能。

    我只实现了IList<T>,但我想如果有人真的想要这种通用解决方案,那么继续实施IBindingList可能是值得花时间和精力的。这样可以让您更好地控制精确的绑定行为,并且可能比基于IList<T> / BindingSource<T>的{​​{1}}方法更少(或更少)hacky。