访问GUI元素无法按预期工作

时间:2012-12-31 14:33:19

标签: c# wpf multithreading user-interface task

当我运行以下代码时,我们应该在每个ListBox的值中填充DataRow,但它会使ListBox填充DataRow个值。

问题是什么?如何解决?这是一个C#4.0 WPF应用程序。

    Task.Factory.StartNew(() =>
    {
        myThread();
    });

void myThread()
{
    using (DataTable dtTemp = DbConnection.db_Select_DataTable(srQuery))
    {
        foreach (DataRow drw in dtTemp.Rows)
        {   
            this.Dispatcher.BeginInvoke(new Action(delegate()
            {
                listBox1.Items.Add(drw["Value"].ToString());
            }));
        }
    }
}

2 个答案:

答案 0 :(得分:6)

问题是您在匿名方法中捕获drw变量。该变量正在foreach循环中更新,因此,当您在调度程序线程上调用委托时,每次都获得相同的值。在C#5中,这是可以的(这是一个常见的错误,语言已经改变以避免它)但在C#5之前你需要在循环中复制变量:

foreach (DataRow drw in dtTemp.Rows)
{   
    DataRow copy = drw;
    this.Dispatcher.BeginInvoke(new Action(delegate()
    {
        listBox1.Items.Add(copy["Value"].ToString());
    }));
}

甚至更好,在后台线程中进行所有数据访问:

foreach (DataRow drw in dtTemp.Rows)
{   
    string item = drw["Value"].ToString();
    this.Dispatcher.BeginInvoke(new Action(delegate()
    {
        listBox1.Items.Add(item);
    }));
}

请注意,对C#5 的更改会影响foreach - 而不是for循环。

另请注意,使用lambda表达式而不是匿名方法可以略微缩短代码:

foreach (DataRow drw in dtTemp.Rows)
{   
    string item = drw["Value"].ToString();
    Action action = () => listBox1.Items.Add(item);
    this.Dispatcher.BeginInvoke(action);
}

答案 1 :(得分:4)

变量捕获再次发生。 (Google:variable capture

尝试创建一个临时变量并将其传递给线程,如下所示:

void myThread()
{
    using (DataTable dtTemp = DbConnection.db_Select_DataTable(srQuery))
    {
        foreach (DataRow drw in dtTemp.Rows)
        {
            DataRow tmp = drw;
            this.Dispatcher.BeginInvoke(new Action(delegate()
            {
                listBox1.Items.Add(tmp["Value"].ToString());
            }));
        }
    }
}