I have created a C# Windows Forms application which I've attempted to make as simple as possible to demonstrate a problem I am running into. I'm trying to use a DataGridView to allow user input in one column while simultaneously getting updates in another column from a background thread.
The problem is that the Input column is effectively un-editable because -- I think -- the updates which are intended for the Output column cause the Input column to be updated with it's current value while the user is attempting to change it.
Is this a bug in DataGridView? Is there a better way to do this sort of thing? Can anyone recommend a good workaround?
The following code demonstrates the problem. The Output column will continuously update and the Input column is virtually uneditable. I've merged the designer code (Form1.designer.cs), and Main (from Program.cs) into the form code (Form1.cs) -- so the following code should work on its own.
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Timers;
public partial class Form1 : Form
{
private System.ComponentModel.IContainer components = null;
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.dataGridView = new System.Windows.Forms.DataGridView();
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
this.SuspendLayout();
//
// dataGridView
//
this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView.Location = new System.Drawing.Point(3, 12);
this.dataGridView.Name = "dataGridView";
this.dataGridView.RowTemplate.Height = 24;
this.dataGridView.Size = new System.Drawing.Size(322, 158);
this.dataGridView.TabIndex = 0;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(328, 174);
this.Controls.Add(this.dataGridView);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.DataGridView dataGridView;
public Form1()
{
InitializeComponent();
}
BindingSource bindingSource = new BindingSource();
BindingList<Item> items = new BindingList<Item>();
private System.Timers.Timer timer;
private void Form1_Load(object sender, EventArgs e)
{
dataGridView.DataSource = bindingSource;
bindingSource.DataSource = items;
items.Add(new Item(dataGridView));
timer = new System.Timers.Timer {Interval = 50};
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
private Random random = new Random();
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
items[0].SetOutput(random.Next(100));
}
}
class Item : INotifyPropertyChanged
{
public int Input { get; set; }
private int output;
public int Output
{
get { return output; }
private set
{
output = value;
OnPropertyChanged("Output");
}
}
public Control control;
public Item(Control control)
{
this.control = control;
}
public void SetOutput(int outputValue)
{
Output = outputValue;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
if(!control.IsDisposed)
control.BeginInvoke(handler, this, new PropertyChangedEventArgs(name));
}
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}