为啥 Invoke 在另一个类中不起作用

Posted

技术标签:

【中文标题】为啥 Invoke 在另一个类中不起作用【英文标题】:why Invoke doesn't work in another class为什么 Invoke 在另一个类中不起作用 【发布时间】:2017-04-18 06:38:30 【问题描述】:

我会用我的电脑和 Arduino 监控在串行端口上接收到的数据。 在 arduino 上,草图每 300 毫秒向 USB 发送字符串“aabb”。 使用 pc 我想听,并在控件中实时打印字符串 (Textbox)。为此,我创建了一个新线程,它在循环中侦听到达串行端口的内容,当它发生时,它通过调用文本框中的字符串写入。如果我在表单的类中部署,则这些过程有效,但如果我使用外部类,则不会。为了更好地解释这个问题,我粘贴了类的代码

class SerialPortManager

    public SerialPort Serial = new SerialPort();
    private Thread thr;
    private string Log;
    public TextBox textLog;
    public string LastString;
    public bool thrIsAlive;
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [IODescriptionAttribute("ControlInvokeRequiredDescr")]
    public bool InvokeRequired  get; private set; 

    //DISPOSE
    public void Dispose()
    
        this.Dispose();
    

    //SET Textobox LOG
    public void SetLogTxtB (TextBox txt)
    
        textLog = txt;
    

    //PORTE DISPONIBILI 
    public string[] Available_Ports()
    
        return SerialPort.GetPortNames();
    

    //COSTRUTTORI
    public SerialPortManager(string portname, int baudrate,bool InitializeConn)
    
        Serial.BaudRate = baudrate;
        Serial.PortName = portname;
        if (InitializeConn == true) Serial.Open();
    
    public SerialPortManager()
    

    

    //SETTA I PARAMETRI E INIZIALIZZA LA CONNESSIONE
    public void SetConnectionParam(string portname, int baudrate, bool initializeConn)
    
        Serial.Close();
        Serial.Dispose();
        Serial = new SerialPort();
        Serial.BaudRate = baudrate;
        Serial.PortName = portname;
        if (initializeConn == true) Serial.Open();
    

    //ASYNC LISTENER
    public void AsyncListener()
    
        thrIsAlive = true;
        thr = new Thread(ThreadReader);
        thr.Start();
    
    //PROCEDURA PER APPEND
    public void AppendTextBox(string value)
    
        if (InvokeRequired)
        
            this.Invoke(new Action<string>(AppendTextBox), new object[]  value );
            return;

        
        textLog.Text += value;
    

    private void Invoke(Action<string> action, params object[] v)
    
        throw new NotImplementedException();
    

    void ThreadReader()
    
        while (thrIsAlive)
        
            string temp = Serial.ReadLine();
            LastString = temp;
            Log += LastString + "\n";
            AppendTextBox(LastString + "\n");
        

    

表格中我写了三行

SerialPortManager PortMan = new Driver_Arduin.SerialPortManager("COM3", 9600,true);
        PortMan.SetLogTxtB(textBox1);
        PortMan.AsyncListener();

如果我尝试运行程序,它会返回错误“不允许跨线程操作”。现在,当我发布这个问题时,我决定做最后一次尝试并将 AppendTextBox 方法更改为:

public void AppendTextBox(string value)
    
        if (textLog.InvokeRequired)
        
            try
            
                textLog.Invoke(new Action<string>(AppendTextBox), new object[]  value );
                return;
            
            catch (ObjectDisposedException)
            
                thrIsAlive = false;
            
        
        textLog.Text += value;
    

它终于奏效了。现在确定了在发布之前解决问题的 *** 的强大功能,我会知道为什么我的代码有效。谢谢你

【问题讨论】:

【参考方案1】:

在 SerialPortManager 中,您必须使用委托而不是窗口控件。

    class SerialPortManager
    
        public SerialPort Serial = new SerialPort();
        private Thread thr;
        private string Log;
        //public TextBox textLog;
        public Action<string> textLog;
.....

Crete 在你形成简单的方法:

public void SetTextBoxText(string value)
    
        if (textBox1.InvokeRequired)
        
            try
            
                textBox1.Invoke(new Action<string>(AppendTextBox), new object[]  value );
                return;
            
            catch (ObjectDisposedException)
            
                thrIsAlive = false;
            
        
        textBox1.Text += value;
    

将委托设置为 PortMan:

SerialPortManager PortMan = new Driver_Arduin.SerialPortManager("COM3", 9600,true);
        PortMan.SetLogTxtB=new Action<string>(SetTextBoxText);
        PortMan.AsyncListener();

如果需要输出日志到 PortMan 的 TextBox 调用 textLog 代理。

void ThreadReader()
    
        while (thrIsAlive)
        
            string temp = Serial.ReadLine();
            LastString = temp;
            Log += LastString + "\n";
            //AppendTextBox(LastString + "\n");
            textLog(LastString + "\n");
        

    

【讨论】:

【参考方案2】:

除了SerialPortManager 中的Invoke 方法应该抛出NotImplementedException 之外,问题是你定义了自己的InvokeRequired/Invoke

您需要使用 WinForms 控件提供的这些方法,以便它知道您的代码是否在创建控件的线程(UI 线程)内运行,以及它如何将上下文更改为该线程。

实际上,您似乎可以使用您的SerialPortManager,但要使用textLogInvokeRequired/Invoke,就像您已经在AppendTextBox 中所做的那样。

顺便说一句,if (initializeConn == true) 相当没用 - if (initializeConn) 就足够了。

【讨论】:

以上是关于为啥 Invoke 在另一个类中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为啥 setter 的 'this._obj['a']=2' 在 Dart 类中不起作用

为啥我的 javascript 箭头函数在 Edge/IE 中不起作用?

为啥 iframe 高度 100% 在 XHTML 页面中不起作用? [复制]

为啥方法重命名在 PHP 特征中不起作用?

为啥 UITableViewCell 初始化在 initWithCoder 中不起作用

为啥这个显式 P/Invoke 不起作用?