如何在多线程应用程序中安全地填充数据并 Refresh() DataGridView?

2024-05-23

我的应用程序有一个 DataGridView 对象和一个 MousePos 类型的列表。 MousePos 是一个自定义类,它保存鼠标 X、Y 坐标(类型为“Point”)和该位置的运行计数。我有一个线程(System.Timers.Timer)每秒引发一次事件,检查鼠标位置,添加和/或更新此列表上鼠标位置的计数。

我想要一个类似的运行线程(同样,我认为 System.Timers.Timer 是一个不错的选择),它将再次每秒引发一次事件以自动 Refresh() DataGridView ,以便用户可以看到 DataGridView 上的数据屏幕更新。 (就像任务管理器一样。)

不幸的是,调用 DataGridView.Refresh() 方法会导致 VS2005 停止执行并注意到我遇到了跨线程情况。

如果我理解正确的话,我现在有 3 个线程:

  • 主 UI 线程
  • MousePos 列表线程(计时器)
  • DataGridView刷新线程(定时器)

为了查看是否可以在主线程上 Refresh() DataGridView,我在表单中添加了一个名为 DataGridView.Refresh() 的按钮,但这(奇怪的是)没有执行任何操作。我发现一个主题似乎表明如果我设置 DataGridView.DataSource = null 并返回到我的列表,它将刷新数据网格。事实上,这有效,但只能通过按钮(在主线程上处理)。


所以这个问题变成了两个部分:

  1. 将 DataGridView.DataSource 设置为 null 并返回到我的列表是否是刷新数据网格的可接受方法? (对我来说这似乎效率很低......)
  2. 如何在多线程环境中安全地执行此操作?

这是我到目前为止编写的代码(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; }
}

您必须像所有其他控件一样更新主 UI 线程上的网格。请参阅 control.Invoke 或 Control.BeginInvoke。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在多线程应用程序中安全地填充数据并 Refresh() DataGridView? 的相关文章