zoukankan      html  css  js  c++  java
  • InvokeHelper,让跨线程访问/修改主界面控件不再麻烦

    事实上,本文内容很简单且浅显,所以取消前戏,直接开始。。

    源代码:在本文最后

    这里是一张动画,演示在多线程(无限循环+Thread.Sleep)情况下主界面操作不受影响。



    多线程是一种提高程序运行效率和性能的常用技术。随着我们学习工作的深入,在编程中或多或少会涉及到需要多线程的情况。多数时候,我们的操作模式是后台线程中处理数据,计算结果,然后在前台界面(GUI)中更新显示。

    在.NET Framework中,为了保证线程安全,避免出现访问竞争等问题,是不允许跨线程访问窗体控件的。如果强行访问,则会引发InvalidOperationException无效操作异常,如下图:

    为了实现跨线程访问控件,.NET Framework为每个控件提供了InvokeRequired属性和Invoke方法。使用这些技巧,就可以实现我们在其他线程中直接修改界面的需要。看起来似乎很简单,但实际每次调用都有不少代码需要编写,还需要自行处理各种异常。下面是典型的调用例子:

    C# code?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void DoWork()  
    {  
        if (control.InvokeRequired)  
        {  
            control.Invoke(DoWork);  
        }  
        else  
        {  
            // do work  
        }  
    }  


    为了便于使用,我封装了实现细节,在这里给出一个InvokeHelper类,使用该类即可方便地实现跨线程调用主界面控件方法、获取/设置控件属性等功能。
    该类实现非常简单,有效代码约150行,主要有以下3个方法:

    1.Invoke

    该方法可以调用主界面控件的某个方法,并返回方法执行结果。用法如下:

    C# code?
    1
    InvokeHelper.Invoke(<控件>, "<方法名称>", <参数>);  


    其中“参数”为参数列表,支持0个或多个参数。

    2.Get

    该方法可以获取主界面控件的某个属性。用法如下:

    C# code?
    1
    InvokeHelper.Get(<控件>, "<属性名称>");  



    3.Set
    该方法可以设置主界面控件的某个属性。用法如下:

    C# code?
    1
    InvokeHelper.Set(<控件>, "<属性名称>", <属性值>);  



    下面是整个类的实现代码。最后是一个演示用的例子。

    C# code?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    /******************************************************************************* 
     * InvokeHelper.cs 
     * A thread-safe control invoker helper class. 
     * ----------------------------------------------------------------------------- 
     * Project:Conmajia.Controls 
     * Author:Conmajia 
     * Url:conmajia@gmail.com 
     * History: 
     *      4th Aug., 2012 
     *      Added support for "Non-control" controls (such as ToolStripItem). 
     *       
     *      4th Aug., 2012 
     *      Initiated. 
     ******************************************************************************/  
    using System;  
    using System.Collections.Generic;  
    using System.Reflection;  
    using System.Text;  
    using System.Windows.Forms;  
       
    namespace InvokerHelperDemo  
    {  
        /// <summary>  
        /// A thread-safe control invoker helper class.  
        /// </summary>  
        public class InvokeHelper  
        {  
            #region delegates  
            private delegate object MethodInvoker(Control control, string methodName, params object[] args);  
       
            private delegate object PropertyGetInvoker(Control control, object noncontrol, string propertyName);  
            private delegate void PropertySetInvoker(Control control, object noncontrol, string propertyName, object value);  
            #endregion  
      
            #region static methods  
            // helpers  
            private static PropertyInfo GetPropertyInfo(Control control, object noncontrol, string propertyName)  
            {  
                if (control != null && !string.IsNullOrEmpty(propertyName))  
                {  
                    PropertyInfo pi = null;  
                    Type t = null;  
       
                    if (noncontrol != null)  
                        t = noncontrol.GetType();  
                    else  
                        t = control.GetType();  
       
                    pi = t.GetProperty(propertyName);  
       
                    if (pi == null)  
                        throw new InvalidOperationException(  
                            string.Format(  
                            "Can't find property {0} in {1}.",  
                            propertyName,  
                            t.ToString()  
                            ));  
       
                    return pi;  
                }  
                else  
                    throw new ArgumentNullException("Invalid argument.");  
            }  
       
            // outlines  
            public static object Invoke(Control control, string methodName, params object[] args)  
            {  
                if (control != null && !string.IsNullOrEmpty(methodName))  
                    if (control.InvokeRequired)  
                        return control.Invoke(  
                            new MethodInvoker(Invoke),  
                            control,  
                            methodName,  
                            args  
                            );  
                    else  
                    {  
                        MethodInfo mi = null;  
       
                        if (args != null && args.Length > 0)  
                        {  
                            Type[] types = new Type[args.Length];  
                            for (int i = 0; i < args.Length; i++)  
                            {  
                                if (args[i] != null)  
                                    types[i] = args[i].GetType();  
                            }  
       
                            mi = control.GetType().GetMethod(methodName, types);  
                        }  
                        else  
                            mi = control.GetType().GetMethod(methodName);  
       
                        // check method info you get  
                        if (mi != null)  
                            return mi.Invoke(control, args);  
                        else  
                            throw new InvalidOperationException("Invalid method.");  
                    }  
                else  
                    throw new ArgumentNullException("Invalid argument.");  
            }  
       
            public static object Get(Control control, string propertyName)  
            {  
                return Get(control, null, propertyName);  
            }  
            public static object Get(Control control, object noncontrol, string propertyName)  
            {  
                if (control != null && !string.IsNullOrEmpty(propertyName))  
                    if (control.InvokeRequired)  
                        return control.Invoke(new PropertyGetInvoker(Get),  
                            control,  
                            noncontrol,  
                            propertyName  
                            );  
                    else  
                    {  
                        PropertyInfo pi = GetPropertyInfo(control, noncontrol, propertyName);  
                        object invokee = (noncontrol == null) ? control : noncontrol;  
       
                        if (pi != null)  
                            if (pi.CanRead)  
                                return pi.GetValue(invokee, null);  
                            else  
                                throw new FieldAccessException(  
                                    string.Format(  
                                    "{0}.{1} is a write-only property.",  
                                    invokee.GetType().ToString(),  
                                    propertyName  
                                    ));  
       
                        return null;  
                    }  
                else  
                    throw new ArgumentNullException("Invalid argument.");  
            }  
       
            public static void Set(Control control, string propertyName, object value)  
            {  
                Set(control, null, propertyName, value);  
            }  
            public static void Set(Control control, object noncontrol, string propertyName, object value)  
            {  
                if (control != null && !string.IsNullOrEmpty(propertyName))  
                    if (control.InvokeRequired)  
                        control.Invoke(new PropertySetInvoker(Set),  
                            control,  
                            noncontrol,  
                            propertyName,  
                            value  
                            );  
                    else  
                    {  
                        PropertyInfo pi = GetPropertyInfo(control, noncontrol, propertyName);  
                        object invokee = (noncontrol == null) ? control : noncontrol;  
       
                        if (pi != null)  
                            if (pi.CanWrite)  
                                pi.SetValue(invokee, value, null);  
                            else  
                                throw new FieldAccessException(  
                                    string.Format(  
                                    "{0}.{1} is a read-only property.",  
                                    invokee.GetType().ToString(),  
                                    propertyName  
                                    ));  
                    }  
                else  
                    throw new ArgumentNullException("Invalid argument.");  
            }  
            #endregion  
        }  
    }  



    下面是一个演示用的例子。在该例子中,创建了一个永久循环的线程,该线程每隔500毫秒修改一次界面显示。主要代码如下:

    C# code?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    Thread t;  
    private void button1_Click(object sender, EventArgs e)  
    {  
        if (t == null)  
        {  
            t = new Thread(multithread);  
            t.Start();  
            label4.Text = string.Format(  
                "Thread state:\n{0}",  
                t.ThreadState.ToString()  
                );  
        }  
    }  
       
    public void DoWork(string msg)  
    {  
        this.label3.Text = string.Format("Invoke method: {0}", msg);  
    }  
       
    int count = 0;  
    void multithread()  
    {  
        while (true)  
        {  
            InvokeHelper.Set(this.label1, "Text"string.Format("Set value: {0}", count));  
            InvokeHelper.Set(this.label1, "Tag", count);  
            string value = InvokeHelper.Get(this.label1, "Tag").ToString();  
            InvokeHelper.Set(this.label2, "Text",  
                string.Format("Get value: {0}", value));  
       
            InvokeHelper.Invoke(this"DoWork", value);  
       
            Thread.Sleep(500);  
            count++;  
        }  
    }  



    详细代码请参阅源代码。运行后效果正常,尽管线程t是无限循环的线程,但主界面并不受其阻塞,操作一切正常。


    源代码:点击下载

  • 相关阅读:
    图片音乐 上传、下载
    表格类型数据,Excel csv导入,导出操作
    逐行读取txt文件,分割,写入txt。。。上传,下载
    性能分析四
    性能分析三
    postman断言
    postman+Newman语法参数
    shell_03
    shell_02
    shell_01
  • 原文地址:https://www.cnblogs.com/wangchuang/p/2919040.html
Copyright © 2011-2022 走看看