none
串口接收事件处理函数中动态的添加一个usrcontrol,会报出“调用线程必须为 STA……”的错误,不同线程怎么操作ui线程的控件呢? RRS feed

  • 问题

  • 我用串口接收一组数据,然后用串口事件来处理这组数据,根据数据的值会动态生成一个用户控件,把用户控件生成到主窗体的一个grid里,但是程序执行到,创建用户控件时会报错,请问怎么解决,还有我对线程不太了解,msdn上说,一个程序流就算一线程,我能不能理解为,其实这个串口事件处理程序就是为一个辅助线程呢?那么问题就是怎么在辅助线程去给ui线程添加新的控件?下面是我的代码。  

     public  void com_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                byte[] buff=new byte[4];
                int n = com.BytesToRead;
                com.Read(buff, 0, 4);
                if (buff[0] == 0xa5)
                {
                    codenum++;
                   
                    resultxh xh = new resultxh(buff);
                   
                    talnum += xh.cjxs(buff);
                   
                    qwxs qw = new qwxs(buff, (int)gr.ActualHeight);
               
                    usrcnttrol zidan = new usrcnttrol();   //这里创建一个用户控件,会报错
                    zidan.Width = 20;
                    zidan.Height = 20;
                   
                    zidan.Margin = new Thickness(qw.qw().X - 10, qw.qw().Y - 10, 0, 0);
                    gr.Children.Add(zidan);  //把用户控件放到主窗体的grid里
                }
            }


    • 已更改类型 zka 2011年12月31日 4:25 我不会啊
    • 已编辑 zka 2011年12月31日 5:34
    2011年12月31日 3:47

答案

  • 为什么是滥用?

     


    可能是说这里是C#论坛,WPF有专门的论坛:http://social.msdn.microsoft.com/Forums/zh-cn/wpfzhchs/threads

    另外对于WPF而言,WPF的UI线程是STA的,您的事件处理看上去不是一个STA的线程,不过是STA也没用,因为控件是和Dispatcher关联的,只能在创建的那个线程的Dispatcher中更改。所以后面和控件相关的代码都要放到gr那个线程的dispatcher中执行。不然是加不进去的。可以试一下Dispatcher..::.Invoke 方法:http://technet.microsoft.com/zh-cn/magazine/system.windows.threading.dispatcher.invoke.aspx 

    希望对您有所帮助。
    Best Regards,
    Rocky Yue[MSFT]
    MSDN Community Support | Feedback to us
    • 已建议为答案 Lie YouModerator 2012年1月3日 5:02
    • 已标记为答案 zka 2012年1月3日 5:44
    2012年1月2日 5:40
    版主
  • er...

    不得不科普一下,除了STA和MTA的区别,让你的Control创建报错,你还需要注意跨线程界面操作的问题。

    com_DataReceived是一个事件的响应函数,事件本身是在串口对应的监听线程中触发的,所以,这个方法就是运行在监听线程上的,和你的ui线程不同的,ms从.net 2.0开始就规定跨线程安全性检查了,于是,你跨线程不同步直接更新,就会引发一个异常。简单的做法有2个,一个是设置跨线程检查的标记为false,另一个就是用委托的方式包裹一下,并调用界面的invoke/begininvoke来更新界面,这样就与界面同步起来了。

    例如

    this.Invoke((EventHandler)delegate{
    usrcnttrol zidan = new usrcnttrol();   

                    zidan.Width = 20;
                    zidan.Height = 20;
                    
                    zidan.Margin = new Thickness(qw.qw().X - 10, qw.qw().Y - 10, 0, 0);

                    gr.Children.Add(zidan);  //把用户控件放到主窗体的grid里});


    2011 c# mvp China. *George读起来像不像“饺子”?我爱吃饺子,我叫George。
    • 已建议为答案 Lie YouModerator 2012年1月3日 5:02
    • 已标记为答案 zka 2012年1月3日 5:44
    2012年1月2日 9:39
    版主

全部回复

  • 为什么是滥用?

     

    2011年12月31日 4:21
  • 为什么是滥用?

     


    可能是说这里是C#论坛,WPF有专门的论坛:http://social.msdn.microsoft.com/Forums/zh-cn/wpfzhchs/threads

    另外对于WPF而言,WPF的UI线程是STA的,您的事件处理看上去不是一个STA的线程,不过是STA也没用,因为控件是和Dispatcher关联的,只能在创建的那个线程的Dispatcher中更改。所以后面和控件相关的代码都要放到gr那个线程的dispatcher中执行。不然是加不进去的。可以试一下Dispatcher..::.Invoke 方法:http://technet.microsoft.com/zh-cn/magazine/system.windows.threading.dispatcher.invoke.aspx 

    希望对您有所帮助。
    Best Regards,
    Rocky Yue[MSFT]
    MSDN Community Support | Feedback to us
    • 已建议为答案 Lie YouModerator 2012年1月3日 5:02
    • 已标记为答案 zka 2012年1月3日 5:44
    2012年1月2日 5:40
    版主
  • 奥,原来是这样。。我刚来还不清楚分类~~谢谢你,我先看看
    2012年1月2日 8:42
  • 奥,原来是这样。。我刚来还不清楚分类~~谢谢你,我先看看
    2012年1月2日 8:42
  • er...

    不得不科普一下,除了STA和MTA的区别,让你的Control创建报错,你还需要注意跨线程界面操作的问题。

    com_DataReceived是一个事件的响应函数,事件本身是在串口对应的监听线程中触发的,所以,这个方法就是运行在监听线程上的,和你的ui线程不同的,ms从.net 2.0开始就规定跨线程安全性检查了,于是,你跨线程不同步直接更新,就会引发一个异常。简单的做法有2个,一个是设置跨线程检查的标记为false,另一个就是用委托的方式包裹一下,并调用界面的invoke/begininvoke来更新界面,这样就与界面同步起来了。

    例如

    this.Invoke((EventHandler)delegate{
    usrcnttrol zidan = new usrcnttrol();   

                    zidan.Width = 20;
                    zidan.Height = 20;
                    
                    zidan.Margin = new Thickness(qw.qw().X - 10, qw.qw().Y - 10, 0, 0);

                    gr.Children.Add(zidan);  //把用户控件放到主窗体的grid里});


    2011 c# mvp China. *George读起来像不像“饺子”?我爱吃饺子,我叫George。
    • 已建议为答案 Lie YouModerator 2012年1月3日 5:02
    • 已标记为答案 zka 2012年1月3日 5:44
    2012年1月2日 9:39
    版主
  • 谢谢,谢谢,其实我一直不明白这串口接收时间是在哪个线程里面,所以不清楚是不是要跨线程操作,真是谢谢你了,原来他是在串口线程里,明白了,我修改下程序看看,如果还有什么不懂的,请教您~
    2012年1月2日 16:33
  • 我还想问下,我按你的方法改的代码如下

                    this.Dispatcher.Invoke    (System.Windows.Threading.DispatcherPriority.Normal,(EventHandler)delegate
                         {
                            myzd zidan = new myzd();
                            zidan.yanse.Text = Convert.ToString(codenum);
                            zidan.Width = 20;
                            zidan.Height = 20;
                            zidan.Margin = new Thickness(qw.qw().X - 10, qw.qw().Y - 10, 0, 0);
                            gr.Children.Add(zidan);
                         }); 

    可是在调试的时候,总是显示参数不匹配,无处理错误,是怎么回事,我的委托里没有设置参数啊


    • 已编辑 zka 2012年1月4日 7:20
    2012年1月4日 7:09