当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在多线程应用程序中安全地填充数据和Refresh()DataGridView?》经验,为你挑选了2个好方法。

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

我想有一个类似的运行线程(再次,我认为System.Timers.Timer是一个不错的选择),这将再次引发一次事件一次自动Refresh()DataGridView,以便用户可以看到数据屏幕更新.(就像TaskManager一样.)

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

如果我理解正确,我现在有3个主题:

主UI线程

MousePos List线程(Timer)

DataGridView刷新线程(计时器)

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


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

    将DataGridView.DataSource设置为null并返回到我的List是一种可接受的刷新数据网格的方法吗?(对我来说似乎效率低下......)

    如何在多线程环境中安全地执行此操作?


这是我到目前为止编写的代码(C#/.Net 2.0)

public partial class Form1 : Form
{
    private static List mousePositionList = new List();
    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 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; }
}

Grzenio.. 5

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



1> Grzenio..:

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



2> Pretzel..:

UPDATE!- 我部分找到了"Pro .NET 2.0 Windows窗体和C#中的客户控件"一书中第1部分的答案

我原先以为Refresh()没有做任何事情,我需要调用Invalidate()方法,告诉Windows重新调整我的控件.(通常是马上,但是如果您需要保证现在重新绘制它,那么请立即调用Update()方法.)

    dataGridView1.Invalidate();

但是,事实证明,Refresh()方法仅仅是一个别名:

    dataGridView1.Invalidate(true);
    dataGridView1.Update();             // <== forces immediate redraw

我发现的唯一一个问题是,如果dataGridView中没有数据,则无效的数量会刷新控件.我不得不重新分配数据源.然后它在那之后运作良好.但仅限于行数(或列表中的项目) - 如果添加了新项目,dataGridView将不会意识到还有更多行要显示.

因此,似乎在将数据源(List或Table)绑定到Datasource时,dataGridView会对项(行)进行计数,然后在内部进行设置,并且永远不会检查是否删除了新的行/项或行/项.这就是之前重复绑定数据源的原因.

现在要弄清楚如何在不必重新绑定数据源的情况下更新要在dataGridView中显示的行数...有趣,有趣,有趣!:-)


在做了一些挖掘之后,我想我的答案是我的问题的第2部分(又名安全的多线程):

而不是使用System.Timers.Timer,我发现我应该使用System.Windows.Forms.Timer.

发生这样的事件,使得回调中使用的方法自动发生在主线程上.没有跨线程问题!

声明如下:

private static System.Windows.Forms.Timer refreshWindow2;
refreshWindow2 = new Timer();
refreshWindow2.Interval = 1000;
refreshWindow2.Tick += new EventHandler(refreshWindow2_Tick);
refreshWindow2.Start();

方法是这样的:

private void refreshWindow2_Tick(object sender, EventArgs e)
{
    dataGridView1.Invalidate();
}


重置DataSource的一种更优雅的方法是使用BindingContext并改为刷新它.这意味着您不必每次都将其重置为null.在进行自动更新方面,我认为您需要一个实现INotifyPropertyChanged的数据源.
推荐阅读
黄晓敏3023
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有