C# 多线程中更新窗体控件

在C#中用到多线程处理一些功能,同时希望界面同步显示更新,如果直接写代码就会出现一些问题:

问题代码:

Thread ScanFileThread;

ScanFileThread = new Thread(WorkScanFileThread);
 this.ScanFileThread.Start();

 

WorkScanFileThread线程体代码:

private void WorkScanFileThread()
        {

            for (nDriveIndex = 0; nDriveIndex < DriveList.Count; nDriveIndex++)
            {
                this.listView3.Items[nDriveIndex].SubItems[2].Text =  "Scaning...";
                ...

                //指定的操作,操作结束后设置控件文本为完成

                ...

                SetListViewControlPropertyValue(this.listView3, nDriveIndex, 2, "Finished");
            }
        }

直接这样调用,会在运行时抛出异常,下面是异常信息:

未处理 System.InvalidOperationException
  Message="Cross-thread operation not valid: Control 'listView3' accessed from a thread other than the thread it was created on."
  Source="System.Windows.Forms"
  StackTrace:
       at System.Windows.Forms.Control.get_Handle()
       at System.Windows.Forms.ListView.ListViewNativeItemCollection.DisplayIndexToID(Int32 displayIndex)
       at System.Windows.Forms.ListView.ListViewNativeItemCollection.get_Item(Int32 displayIndex)
       at System.Windows.Forms.ListView.ListViewItemCollection.get_Item(Int32 index)
       at Undelete_UI.Scan.WorkScanFileThread() in D:\C# Project\Undelete_UI - Copy\Undelete_UI\Scan.cs:line 123
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

下面给出MSDN给出的解决方案:

如何跨线程调用Windows窗体控件(http://msdn.microsoft.com/en-us/library/ms171728(VS.90).aspx

摘取C#相关代码如下:

 

// This event handler creates a background thread that 
// attempts to set a Windows Forms control property 
// directly.
private void setTextUnsafeBtn_Click
    (object sender, EventArgs e)
{
    // Create a background thread and start it.
    this.demoThread =
        new Thread(new ThreadStart(this.ThreadProcUnsafe));
    this.demoThread.Start();

    // Continue in the main thread.  Set a textbox value that
    // would be overwritten by demoThread if it succeeded.
    // This value will appear immediately, then two seconds 
    // later the background thread will try to make its
    // change to the textbox.
    textBox1.Text = "Written by the main thread.";
}

// This method is executed on the worker thread. It attempts
// to access the TextBox control directly, which is not safe.
private void ThreadProcUnsafe()
{
    // Wait two seconds to simulate some background work
    // being done.
    Thread.Sleep(2000);

    this.textBox1.Text = 
        "Written unsafely by the background thread.";
}


上述代码并不能解决这个问题,在网上搜了一下,找到了这个方法,很好用:

先给出链接:http://www.shabdar.org/c-sharp/102-cross-thread-operation-not-valid.html

摘抄部分原文:

Solution


 
01. privatevoid btnStart_Click(objectsender, EventArgs e)
02. {
03. progressBar1.Minimum = 0;
04. progressBar1.Maximum = 100;
05.
06. System.Threading.Thread t1 =new System.Threading.Thread(startProgress);
07. t1.Start();
08. }
09. voidstartProgress()
10. {
11. for(int i = 0; i             {
12. SetControlPropertyValue(progressBar1,"value", i); //This is a thread safe method
13. System.Threading.Thread.Sleep(100);
14. }
15. }

Note how SetControlpropertyValue function is used above. Following is it's definition.
 
01. delegatevoid SetControlValueCallback(Control oControl,string propName, objectpropValue);
02. privatevoid SetControlPropertyValue(Control oControl,string propName, objectpropValue)
03. {
04. if(oControl.InvokeRequired)
05. {
06. SetControlValueCallback d =new SetControlValueCallback(SetControlPropertyValue);
07. oControl.Invoke(d,new object[] { oControl, propName, propValue });
08. }
09. else
10. {
11. Type t = oControl.GetType();
12. PropertyInfo[] props = t.GetProperties();
13. foreach(PropertyInfo p in props)
14. {
15. if(p.Name.ToUpper() == propName.ToUpper())
16. {
17. p.SetValue(oControl, propValue,null);
18. }
19. }
20. }
21. }

You can apply same solution to any windows control. All you have to do is, copySetControlValueCallback delegate and SetControlPropertyValue function from above code. For example if you want to set property of a label, useSetControlPropertyValue function.


SetControlPropertyValue(Label1, "Text", i.ToString());

Make sure you supply property value in correct type. In above example Text is a string property. This is why I am converting variablei to string.

 

 

自己根据上面的代码对程序进行了修改,修改后的代码如下:

delegate void SetControlValueCallback(Control oControl, string propName, object propValue);

        private void SetControlPropertyValue(Control oControl, string propName, object propValue)
        {

            if (oControl.InvokeRequired)
            {

                SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);

                oControl.Invoke(d, new object[] { oControl, propName, propValue });

            }

            else
            {

                Type t = oControl.GetType();

                PropertyInfo[] props = t.GetProperties();

                foreach (PropertyInfo p in props)
                {

                    if (p.Name.ToUpper() == propName.ToUpper())
                    {

                        p.SetValue(oControl, propValue, null);

                    }

                }

            }

        }

        //这是根据需要改写的,用于给ListView指定列赋值的
        delegate void SetListViewControlValueCallback(ListView oControl, int nItem, int nSubItem, string propValue);

        private void SetListViewControlPropertyValue(ListView oControl, int nItem, int nSubItem, string propValue)
        {

            if (oControl.InvokeRequired)
            {

                SetListViewControlValueCallback d = new SetListViewControlValueCallback(SetListViewControlPropertyValue);

                oControl.Invoke(d, new object[] { oControl,nItem,nSubItem, propValue });

            }

            else
            {
                oControl.Items[nItem].SubItems[nSubItem].Text = propValue;

            }

        }
        private void WorkScanFileThread()
        {

            for (nDriveIndex = 0; nDriveIndex < DriveList.Count; nDriveIndex++)
            {
                SetControlPropertyValue(this.progressBar1, "value", 0);
                SetListViewControlPropertyValue(this.listView3, nDriveIndex,2, "Scaning...");
                //执行的功能操作
                SetListViewControlPropertyValue(this.listView3, nDriveIndex, 2, "Finished");
            }
        }


这样问题就得到的完美的解决。

 

产生这个问题的主要原因是由于,控件不是线程安全性的,在线程中访问非线程安全性的操作会被抛出异常,提醒编程开发人员进行代码修改。

再给出两个相关的链接

http://blog.sina.com.cn/s/blog_4cb80898010009de.html

http://www.cnblogs.com/ini_always/archive/2011/04/26/2029409.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

daiafei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值