程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> c# winform編程之多線程ui界面資源修改總結篇

c# winform編程之多線程ui界面資源修改總結篇

編輯:C#入門知識

單線程的winfom程序中,設置一個控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一個新線程中這麼做,比如: 
private void btnSet_Click(object sender, EventArgs e) 
{    
    Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue)); 
    //當然也可以用匿名委托寫成Thread t = new Thread(SetTextBoxValue); 
    t.Start("Hello World"); 



void SetTextBoxValue(object obj) 

    this.textBox1.Text = obj.ToString(); 


運行時,會報出一個無情的錯誤: 
線程間操作無效: 從不是創建控件“textBox1”的線程訪問它。 

究其原因,winform中的UI控件不是線程安全的,如果可以隨意在任何線程中改變其值,你創建一個線程,我創建一個線程,大家都來搶著更改"TextBox1"的值,沒有任何秩序的話,天下大亂... 

解決辦法: 
1.掩耳盜鈴法(Control.CheckForIllegalCrossThreadCalls = false;)--僅Winform有效 
using System; 
using System.Threading; 
using System.Windows.Forms; 

namespace ThreadTest 

    public partial class Form1 : Form 
    {        

        public Form1() 
        { 
            InitializeComponent(); 
            Control.CheckForIllegalCrossThreadCalls = false;//這一行是關鍵      
        } 
       

        private void btnSet_Click(object sender, EventArgs e) 
        {           
            Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue)); 
            t.Start("Hello World"); 
        } 


        void SetTextBoxValue(object obj) 
        { 
            this.textBox1.Text = obj.ToString(); 
        }        
    } 


設置Control.CheckForIllegalCrossThreadCalls為false,相當於不檢測線程之間的沖突,允許各路線程隨便亂搞,當然最終TextBox1的值到底是啥難以預料,只有天知道,不過這也是最省力的辦法 

2.利用委托調用--最常見的辦法(僅WinForm有效) 
using System; 
using System.Threading; 
using System.Windows.Forms; 

namespace ThreadTest 

    public partial class Form1 : Form 
    { 
        delegate void D(object obj); 

        public Form1() 
        { 
            InitializeComponent();            
        } 
       

        private void btnSet_Click(object sender, EventArgs e) 
        {           
            Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue)); 
            t.Start("Hello World"); 
        } 


        void SetTextBoxValue(object obj) 
        { 
            if (textBox1.InvokeRequired) 
            { 
                D d = new D(DelegateSetValue); 
                textBox1.Invoke(d,obj); 

            } 
            else 
            { 
                this.textBox1.Text = obj.ToString(); 
            } 
        } 


        void DelegateSetValue(object obj) 
        { 
            this.textBox1.Text = obj.ToString(); 
        } 
    } 


3.利用SynchronizationContext上下文 -- 最神秘的方法(Winform/Silverlight能用) 

之所以說它神秘,是因為msdn官方對它的解釋據說也是不清不楚 
using System; 
using System.Threading; 
using System.Windows.Forms; 

namespace ThreadTest 

    public partial class Form1 : Form 
    { 
        public Form1() 
        { 
            InitializeComponent();            
        }       

        private void btnSet_Click(object sender, EventArgs e) 
        { 
            Thread t = new Thread(new ParameterizedThreadStart(Run)); 
            MyPram _p = new MyPram() { context = SynchronizationContext.Current, parm = "Hello World" }; 
            t.Start(_p); 
        } 

        void Run(object obj) 
        { 
            MyPram p = obj as MyPram; 
            p.context.Post(SetTextValue, p.parm); 
        } 


        void SetTextValue(object obj) 
        { 
            this.textBox1.Text = obj.ToString(); 
        } 
    } 


    public class MyPram 
    { 
        public SynchronizationContext context { set; get; } 
        public object parm { set; get; } 
    } 


4.利用BackgroundWorker --最偷懶的辦法(Winform/Silverlight通用) 

BackgroundWorker會在主線程之外,另開一個後台線程,我們可以把一些處理放在後台線程中處理,完成之後,後台線程會把結果傳遞給主線程,同時結束自己。 
using System; 
using System.ComponentModel; 
using System.Windows.Forms; 

namespace ThreadTest 

    public partial class Form1 : Form 
    { 
        public Form1() 
        { 
            InitializeComponent();            
        }       

        private void btnSet_Click(object sender, EventArgs e) 
        { 
            //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); 
            using (BackgroundWorker bw = new BackgroundWorker()) 
            { 
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
                bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
                bw.RunWorkerAsync("Hello World"); 
            } 
        } 

        void bw_DoWork(object sender, DoWorkEventArgs e) 
        { 
            //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); 
            e.Result = e.Argument;//這裡只是簡單的把參數當做結果返回,當然您也可以在這裡做復雜的處理後,再返回自己想要的結果(這裡的操作是在另一個線程上完成的) 
        } 

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
        { 
            //這時後台線程已經完成,並返回了主線程,所以可以直接使用UI控件了 
            this.textBox1.Text = e.Result.ToString(); 
            //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); 
        }       
    }    


5.Dispatcher.BeginInvoke--Silverlight的獨門秘籍 
代碼 
using System.Threading; 
using System.Windows.Controls; 
using System.Windows.Input; 

namespace ThreadTest 

    public partial class MainPage : UserControl 
    { 
        public MainPage() 
        { 
            InitializeComponent(); 
        } 

        private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
        { 
            Thread t = new Thread(SetTextValue); 
            t.Start("Hello World"); 
        } 

        void SetTextValue(object text) 
        { 
            this.Dispatcher.BeginInvoke(() => { this.txt.Text = text.ToString(); });            
        } 
    } 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved