我的应用程序有一个 DataGridView 对象和一个 MousePos 类型的列表。MousePos 是一个自定义类,它保存鼠标 X、Y 坐标(类型为“Point”)和该位置的运行计数。我有一个线程(System.Timers.Timer)每秒引发一次事件,检查鼠标位置,添加和/或更新此列表上的鼠标位置计数。
我想有一个类似的运行线程(再次,我认为 System.Timers.Timer 是一个不错的选择),它会再次引发一个事件,以自动 Refresh() DataGridView 以便用户可以看到数据屏幕更新。(就像 TaskManager 一样。)
不幸的是,调用 DataGridView.Refresh() 方法会导致 VS2005 停止执行并注意到我遇到了跨线程情况。
如果我理解正确,我现在有 3 个线程:
- 主 UI 线程
- MousePos 列表线程(定时器)
- DataGridView 刷新线程(定时器)
为了查看是否可以 Refresh() 主线程上的 DataGridView,我向名为 DataGridView.Refresh() 的表单添加了一个按钮,但这(奇怪的是)没有做任何事情。我发现一个主题似乎表明如果我设置 DataGridView.DataSource = null 并返回到我的列表,它将刷新数据网格。确实这有效,但只能通过按钮(在主线程上处理。)
所以这个问题变成了一个两部分:
- 将 DataGridView.DataSource 设置为 null 并返回到我的 List 是一种可接受的刷新数据网格的方式吗?(对我来说似乎效率低下......)
- 如何在多线程环境中安全地执行此操作?
这是我到目前为止编写的代码(C#/.Net 2.0)
public partial class Form1 : Form
{
private static List<MousePos> mousePositionList = new List<MousePos>();
private static System.Timers.Timer mouseCheck = new System.Timers.Timer(1000);
private static System.Timers.Timer refreshWindow = new System.Timers.Timer(1000);
public Form1()
{
InitializeComponent();
mousePositionList.Add(new MousePos()); // ANSWER! Must have at least 1 entry before binding to DataSource
dataGridView1.DataSource = mousePositionList;
mouseCheck.Elapsed += new System.Timers.ElapsedEventHandler(mouseCheck_Elapsed);
mouseCheck.Start();
refreshWindow.Elapsed += new System.Timers.ElapsedEventHandler(refreshWindow_Elapsed);
refreshWindow.Start();
}
public void mouseCheck_Elapsed(object source, EventArgs e)
{
Point mPnt = Control.MousePosition;
MousePos mPos = mousePositionList.Find(ByPoint(mPnt));
if (mPos == null) { mousePositionList.Add(new MousePos(mPnt)); }
else { mPos.Count++; }
}
public void refreshWindow_Elapsed(object source, EventArgs e)
{
//dataGridView1.DataSource = null; // Old way
//dataGridView1.DataSource = mousePositionList; // Old way
dataGridView1.Invalidate(); // <= ANSWER!!
}
private static Predicate<MousePos> ByPoint(Point pnt)
{
return delegate(MousePos mPos) { return (mPos.Pnt == pnt); };
}
}
public class MousePos
{
private Point position = new Point();
private int count = 1;
public Point Pnt { get { return position; } }
public int X { get { return position.X; } set { position.X = value; } }
public int Y { get { return position.Y; } set { position.Y = value; } }
public int Count { get { return count; } set { count = value; } }
public MousePos() { }
public MousePos(Point mouse) { position = mouse; }
}