none
(有代码)事件和委托跨线程调用Windows控件显示的问题 RRS feed

  • 问题

  • 我有个疑问,谢大侠指教:为什么事件跨线程调用Windows控件会出异常?(委托不会) 以下是事例代码:

    a) 1个form,1个label控件,1个button控件,控件名称全部为默认名称

    b) Visual Studio 2008 - C#

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Threading;

    namespace ThreadException
    {
        public partial class Form1 : Form
        {
            //delegate void SetTextCallback(string text);

            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)
            {
                this.button1.Enabled = false;

                MyThreadClass myClass1 = new MyThreadClass(0, 100);
                myClass1.ThreadEvent += new MyThreadClass.ThreadEventHandler(myClass1_ThreadEvent);
                Thread myThread1 = new Thread(myClass1.ThreadRun);
                myThread1.IsBackground = true;
                myThread1.Start();
            }

            private void SetText(string text)
            {
                this.label1.Text = text;
            }

            private void myClass1_ThreadEvent(object sender, ThreadInfoEventArgs e)
            {
                this.label1.Text = e.Number.ToString();  //此处会抛跨线程调用控件异常,用delegate方式正常,是不是Invoke方法有什么特别的地方???

                //string text = e.Number.ToString();
                //if (this.label1.InvokeRequired)
                //{
                //    SetTextCallback d = new SetTextCallback(SetText);
                //    this.Invoke(d, new object[] { text });
                //}
                //else
                //{
                //    this.label1.Text = text;
                //}
            }
        }


        public class ThreadInfoEventArgs : EventArgs
        {
            private int _number = 0;
            public int Number
            {
                get { return this._number; }
                set { this._number = value; }
            }

            private bool _updateButton = false;
            public bool UpdateButton
            {
                get { return this._updateButton; }
                set { this._updateButton = value; }
            }
        }

        public class MyThreadClass
        {
            public delegate void ThreadEventHandler(object sender,ThreadInfoEventArgs e);
            public event ThreadEventHandler ThreadEvent;

            private int _start = 0;
            public int Start
            {
                get { return this._start; }
            }

            private int _end = 10000;
            public int End
            {
                get { return this._end; }
            }

            public MyThreadClass(int Start, int End)
            {
                this._start = Start;
                this._end = End;
            }

            public void ThreadRun()
            {
                for (int index = this._start; index <= this._end; index++)
                {
                    ThreadInfoEventArgs fe = new ThreadInfoEventArgs();
                    fe.Number = index;
                    if (index == this._end)
                        fe.UpdateButton = true;

                    Thread.Sleep(10);

                    ThreadEvent(this, fe);
                }
            }
        }
    }

    • 已编辑 漂过来的 2010年4月7日 19:04 说明环境版本
    2010年4月7日 19:03

答案

  • 你看你下面代码的结构,ThreadEvent 这个委托是运行在线程中的。

    myClass1.ThreadEvent += new MyThreadClass.ThreadEventHandler(myClass1_ThreadEvent);

    Thread myThread1 = new Thread(myClass1.ThreadRun);
    myThread1.IsBackground = true;
    myThread1.Start();

    public void ThreadRun()

            {
                for (int index = this._start; index <= this._end; index++)
                {
                    ThreadInfoEventArgs fe = new ThreadInfoEventArgs();
                    fe.Number = index;
                    if (index == this._end)
                        fe.UpdateButton = true;

                    Thread.Sleep(10);

                    ThreadEvent(this, fe);
                }
            }


    知识改变命运,奋斗成就人生!
    2010年4月8日 3:42
    版主

全部回复

  •     一个窗口中的所有控件属于同一个 UI 线程。实际中,大部分 Windows 窗体应用程序最终都只有一个线程,所有 UI 活动都发生在这个线程上。

    这个线程通常称为 UI 线程。

        net中不能直接在UI Control的创建线程之外修改UI Control的属性和内容。一个基本办法是通过调用UIControl 的Invoke方法。

         一个线程创建的控件,在另外一个线程中是不能直接访问的。只能用Invoke调用

        跨线程调用界面控件需要做两步工作:
        1.利用委托进入界面线程 2,利用Invoke触发事件


    努力+方法=成功
    2010年4月8日 1:06
  • 你好!

    在其它线程中设置窗体控件(UI线程中创建的控件)属性,要通过使用 Invoke 或 BeginInvoke 实现。如下:

    public delegate void ClientDelegate();

    private void myClass1_ThreadEvent(object sender, ThreadInfoEventArgs e){

        this.BeginInvoke(delegate { this.label1.Text = e.Number.ToString(); });

    }



    知识改变命运,奋斗成就人生!
    2010年4月8日 1:19
    版主
  • 谢,不过上面的示例中,是不是只有2个线程?

    1是主线程(UI线程)

    2是辅助线程(myThread1)

    而在主线程中创建了线程类实例myClass1,并订阅了ThreadEvent ,

    此时处理该事件的方法是在主线程内? 还是在辅助线程内??

    2010年4月8日 3:06
  • 谢,不过上面的示例中,是不是只有2个线程?

    1是主线程(UI线程)

    2是辅助线程(myThread1)

    而在主线程中创建了线程类实例myClass1,并订阅了ThreadEvent ,

    此时处理该事件的方法是在主线程内? 还是在辅助线程内??

    2010年4月8日 3:07
  • 你看你下面代码的结构,ThreadEvent 这个委托是运行在线程中的。

    myClass1.ThreadEvent += new MyThreadClass.ThreadEventHandler(myClass1_ThreadEvent);

    Thread myThread1 = new Thread(myClass1.ThreadRun);
    myThread1.IsBackground = true;
    myThread1.Start();

    public void ThreadRun()

            {
                for (int index = this._start; index <= this._end; index++)
                {
                    ThreadInfoEventArgs fe = new ThreadInfoEventArgs();
                    fe.Number = index;
                    if (index == this._end)
                        fe.UpdateButton = true;

                    Thread.Sleep(10);

                    ThreadEvent(this, fe);
                }
            }


    知识改变命运,奋斗成就人生!
    2010年4月8日 3:42
    版主
  • 您的意思是:ThreadEvent 这个委托是运行在线程中的,所以接受委托的

    方法myClass1_ThreadEvent就运行在线程中的.

    嗯,明白了,谢谢.

    2010年4月8日 4:32