为什么Invoke不能在另一个类

时间:2016-12-03 12:19:53

标签: c# multithreading serial-port invoke

我会使用我的电脑和Arduino监控串口上收到的数据。 在arduino上,草图向USB发送了字符串“aabb”evry 300ms。 使用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;
    }

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

2 个答案:

答案 0 :(得分:0)

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

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

实际上,您似乎可以使用SerialPortManager但使用InvokeRequired的{​​{1}} / Invoke,就像您在textLog中所做的一样。< / p>

BTW,AppendTextBox相当无用 - if (initializeConn == true)就足够了。

答案 1 :(得分:0)

在SerialPortManager中,您必须使用委托代替Windows控件。

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;
    }

克里特在你形成简单的方法:

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

将委托设置为PortMan:

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

    }

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

-(void) viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"LoginSuccess"] == YES)
    {
      //button hide 
      profile.hidden = NO;
    }
}