多線程委派 转

一般我們在實作多線程存取主線程控制項屬性時,必須以委派方式來存取,否則就有可能會出現 InvalidOperationException 例外, 一般初學者總會犯這個錯誤,大都不知道需要委派的動作,總是在子線程用一行 textBox1.Text = "aaaaa" 就想改變控制項的屬性,卻 換來 InvalidOperationException 的錯誤,而在一般情況下委派的動作總不會 像 textBox1.Text = "aaaaa" 短短一行就這麼直覺的解決問題,總覺得委派代碼實在麻煩。如果你運作的環境 是 .net framework 3.5(含以上),又覺得委派的代碼總是惹人厭的話,下面的代碼或許幫得上你解決惱人的委派問題,至少幫了我很多,分 享給大家,以下是實例。

首先定義一個類別,來當然是 Control Class 擴充方法類別。

public static class ExtensionControl
{                                               
    public static object GetPropertySafe(this Control control, string propertyName)
    {
        object returnValue = null;
        Action func = () =>
        {
            Type type = control.GetType();
            returnValue = type.InvokeMember(propertyName, BindingFlags.GetProperty, null, control, null);
        };
        if (control.InvokeRequired)
        {
            control.Invoke(func);
        }
        else
        {
            func();
        }
        return returnValue;
    }

    public static object SetPropertySafe(this Control control, string propertyName, object value)
    {
        object returnValue = null;
        Action func = () =>
        {
            Type type = control.GetType();
            returnValue = type.InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { value });
        };
        if (control.InvokeRequired)
        {
            control.Invoke(func);
        }
        else
        {
            func();
        }
        return returnValue;
    }

    public static object GetPropertySafe(this ToolStripMenuItem control, string propertyName)
    {
        object returnValue = null;
        Control owner = control.Owner;
        Action func = () =>
        {
            Type type = control.GetType();
            returnValue = type.InvokeMember(propertyName, BindingFlags.GetProperty, null, control, null);
        };
        if (owner.InvokeRequired)
        {
            owner.Invoke(func);
        }
        else
        {
            func();
        }
        return returnValue;
    }

    public static object SetPropertySafe(this ToolStripMenuItem control, string propertyName, object value)
    {
        object returnValue = null;
        Control owner = control.Owner;
        Action func = () =>
        {
            Type type = control.GetType();
            returnValue = type.InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { value });                
        };
        if (owner.InvokeRequired)
        {
            owner.Invoke(func);
        }
        else
        {
            func();
        }
        return returnValue;
    }        

    public static object InvokeMethodSafe(this Control control, string methodName, params object[] args)
    {
        object returnValue = null;
        if (args == null)
        {
            args = new object[1];
            args[0] = null;
        }
        else if (args != null && args.Length == 0)
        {
            args = null;
        }
        Action func = () =>
        {
            Type type = control.GetType();                
            returnValue = type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, control, args);
        };
        if (control.InvokeRequired)
        {
            control.Invoke(func);
        }
        else
        {
            func();
        }
        return returnValue;
    }

    public static object InvokeMethodSafe(this ToolStripMenuItem control, string methodName, params object[] args)
    {
        object returnValue = null;
        if (args == null)
        {
            args = new object[1];
            args[0] = null;
        }
        else if (args != null && args.Length == 0)
        {
            args = null;
        }
        Control owner = control.Owner;
        Action func = () =>
        {
            Type type = control.GetType();
            returnValue = type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, control, args);
        };
        if (owner.InvokeRequired)
        {
            owner.Invoke(func);
        }
        else
        {
            func();
        }
        return returnValue;
    }
}    

類別中分別定義 SetPropertySafe, GetPropertySafe, InvokeMethodSafe 方法,當作 是 Control 的擴充方法,在一個 WindowsFormsApplication 加入該代碼或者自己做成 dll 引用都可以,以下是多線程 實作委派的示例。

namespace WindowsFormsApplication1
{
    public partial class MainForm : Form
    {        
        public MainForm()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(
                ThreadStart =>
                {
                    //this.textBox1.Text = "Thread Edit";
                    //將上方會產生 InvalidOperationException 的代碼註解,改以下方代碼                    
                    this.textBox1.SetPropertySafe("Text", "ThreadEdit");
                }
            );
            thread.IsBackground = true;
            thread.Start();
        }
    }
}

這樣就徹底解決多線程存取主線程控制項委派的麻煩代碼,同樣一行程式碼做相同的事,接著就可以舉一反三再擴充 Control 方法,技術上並沒有多深,只是一個比較方便的代碼,希望能夠讓一些人在實作多線程委派時,能夠少走彎路,一同為程式加油。

 
posted on 2013-04-26 10:19  武胜-阿伟  阅读(209)  评论(0编辑  收藏  举报