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

来源:互联网 发布:模拟炒汇软件 编辑:程序博客网 时间:2024/05/16 16:20

在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.privatevoidbtnStart_Click(objectsender, EventArgs e)
02.{
03.progressBar1.Minimum = 0;
04.progressBar1.Maximum = 100;
05.
06.System.Threading.Thread t1 =newSystem.Threading.Thread(startProgress);
07.t1.Start();
08.}
09.voidstartProgress()
10.{
11.for(inti = 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.delegatevoidSetControlValueCallback(Control oControl,stringpropName, objectpropValue);
02.privatevoidSetControlPropertyValue(Control oControl,stringpropName, objectpropValue)
03.{
04.if(oControl.InvokeRequired)
05.{
06.SetControlValueCallback d =newSetControlValueCallback(SetControlPropertyValue);
07.oControl.Invoke(d,newobject[] { oControl, propName, propValue });
08.}
09.else
10.{
11.Type t = oControl.GetType();
12.PropertyInfo[] props = t.GetProperties();
13.foreach(PropertyInfo pin 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 delegateand 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 tostring.

 

 

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

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

原创粉丝点击