• WinForm 子线程修改主线程(UI线程)Control 【Z】


    原文http://www.cnblogs.com/SkySoot/archive/2012/03/14/2396552.html

    我们先来看一段运行时会抛出 InvalidOperationException 异常的代码段:

    public partial class TestThread : Form
    {
        public TestThread()
        {
            InitializeComponent();
        }
     
        Thread thread;
     
        void SetText(string str)
        {
            this.textBox1.Text = str;
        }
     
        void DoSomething()
        {
            // do......
            SetText("XXX");
        }
     
        private void buttonX1_Click(object sender, EventArgs e)
        {
            thread = new Thread(new ThreadStart(DoSomething));
            thread.IsBackground = true;
            thread.Start();
        }
    }

    image

    在VS2005或者更高版本中,只要不是在控件的创建线程(一般就是指UI主线程)上访问控件的属性就会抛出这个错误,解决方法就是利用控件提供的Invoke和BeginInvoke把调用封送回UI线程,也就是让控件属性修改在UI线程上执行.

    下面给出修改后正确调用的代码:

    比较直接的修改方法像这样:

    public partial class TestThread : Form
    {
        delegate void ChangeTextBoxValue(string str); // 新增委托代理
     
        public TestThread()
        {
            InitializeComponent();
        }
     
        Thread thread;
     
        void SetText(string str)
        {
            this.textBox1.Text = str;
        }
     
        void DoSomething()
        {
            // do......
            this.BeginInvoke(new ChangeTextBoxValue(SetText),"XXX"); // 也可用 this.Invoke调用
        }
     
        private void buttonX1_Click(object sender, EventArgs e)
        {
            thread = new Thread(new ThreadStart(DoSomething));
            thread.IsBackground = true;
            thread.Start();
        }
    }

    不过,要考虑到也许程序中不止子线程会调用 DoSomething()方法,也许主线程或其他模块多处调用这个方法,最漂亮的改法是这样:

    public partial class TestThread : Form
    {
        delegate void ChangeTextBoxValue(string str);
     
        public TestThread()
        {
            InitializeComponent();
        }
     
        Thread thread;
     
        void SetText(string str)
        {
            if (this.InvokeRequired) // 获取一个值指示此次调用是否来自非UI线程
            {
                this.Invoke(new ChangeTextBoxValue(SetText), str);
            }
            else
            {
                this.textBox1.Text = str;
            }            
        }
     
        void DoSomething()
        {
            // do......
            SetText("ABCDEFG");
        }
     
        private void buttonX1_Click(object sender, EventArgs e)
        {
            thread = new Thread(new ThreadStart(DoSomething));
            thread.IsBackground = true;
            thread.Start();
        }
    }

    this.Invoke(new ChangeTextBoxValue(SetText), str) // 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托

    this.BeginInvoke(new ChangeTextBoxValue(SetText), str); // 在创建控件的基础句柄所在线程上,用指定的参数异步执行指定委托。

    无论是同步还是异步,单步跟踪调试代码会发现,这些方法还是会回到UI线程执行,其中通过了代理而已.

    这两个方法向UI线程的消息队列中放入一个消息,当UI线程处理这个消息时,就会在自己的上下文中执行传入的方法,换句话说凡是使用BeginInvoke和Invoke调用的线程都是在UI主线程中执行的,所以如果这些方法里涉及一些静态变量,不用考虑加锁的问题.

  • 相关阅读:
    Dynamics CRM2016 Web API之更新记录
    opencv基础笔记(1)
    HTML5开发移动web应用——SAP UI5篇(6)
    就算你不是电商,你应该为你的电商朋友好好看看这篇文章
    xode5.1.1设置IOS欢迎界面的方法
    Java中的Nested Classes和Inner Classes
    pve之daemon
    对话沈向洋 | 在人工智能最好的时代,寻求可能性比超前顾虑更关键
    .NET Core 2.0 开源Office组件 NPOI
    【转发活动】Hey, 是你吗? | 寻"粉"启示
  • 原文地址:https://www.cnblogs.com/maijin/p/2826146.html
Copyright © 2020-2023  润新知