C#GUI刷新和异步串口通信

我正在尝试创建一个通过串口与硬件通信的应用程序,并将结果报告给gui。

目前通过GUI移动是由KeyEvents引起的,它触发了GUI的下一个“页面”的绘制。 然而,在一步(按下键后)我需要绘制新页面并通过串口发送一些命令。

命令发送通过以下方式完成:

port.Write(data, 0, data.Length); 

然后我等待DataReceivedHandler触发等待答案 – 它只是确定存在等待的数据并且正在以另一种方法处理数据。

起初我只是将“发送和接收”命令放在“绘制部件”之后绘制页面的function中,但它使其卡住 – 数据被转移,但页面未被绘制 – 它被冻结。

然后我做了一个异步方法:

 private async void SendData() { await Task.Run(() => serialClass.SendAndReceive(command)); // process reply etc. } 

使用的是这样的:

 public void LoadPage() { image = Image.FromFile(path); //do some stuff on image using Graphics, adding texts etc. picturebox1.Image = image; SendData(); } 

它工作正常,但我需要“重新加载”页面(再次调用LoadPage)。 如果我在async方法中这样做:

 private async void SendData() { await Task.Run(() => serialClass.SendAndReceive(command)); // process reply etc. LoadPage(); } 

然后显然图像不会刷新,但数据将通过串口发送。 有可能以某种方式检查异步function是否完成并触发我可以重新加载页面的事件?

到目前为止,我已尝试使用BackGroundWorker工作完成和属性更改。 数据再次发送,但图像未重新加载。 知道如何实现这一目标吗?

在此先感谢您的帮助,祝您好运

您需要使用状态机和委托来实现您要执行的操作。 请参阅下面的代码,我建议在除Main之外的单独线程中完成所有这些操作。 您可以跟踪您所处的状态,当您收到响应时,您可以使用正确的回调函数对其进行解析,如果它是您所期望的,则转到下一个发送命令状态。

 private delegate void CallbackFunction(String Response); //our generic Delegate private CallbackFunction CallbackResponse; //instantiate our delegate private StateMachine currentState = ATRHBPCalStateMachine.Waiting; SerialPort sp; //our serial port private enum StateMachine { Waiting, SendCmd1, Cmd1Response, SendCmd2, Cmd2Response, Error } private void do_State_Machine() { switch (StateMachine) { case StateMachine.Waiting: //do nothing break; case StateMachine.SendCmd1: CallbackResponse = Cmd1Response; //set our delegate to the first response sp.Write("Send first command1"); //send our command through the serial port currentState = StateMachine.Cmd1Response; //change to cmd1 response state break; case StateMachine.Cmd1Response: //waiting for a response....you can put a timeout here break; case StateMachine.SendCmd2: CallbackResponse = Cmd2Response; //set our delegate to the second response sp.Write("Send command2"); //send our command through the serial port currentState = StateMachine.Cmd2Response; //change to cmd1 response state break; case StateMachine.Cmd2Response: //waiting for a response....you can put a timeout here break; case StateMachine.Error: //error occurred do something break; } } private void Cmd1Response(string s) { //Parse the string, make sure its what you expect //if it is, then set the next state to run the next command if(s.contains("expected")) { currentState = StateMachine.SendCmd2; } else { currentState = StateMachine.Error; } } private void Cmd2Response(string s) { //Parse the string, make sure its what you expect //if it is, then set the next state to run the next command if(s.contains("expected")) { currentState = StateMachine.Waiting; backgroundWorker1.CancelAsync(); } else { currentState = StateMachine.Error; } } //In my case, I build a string builder until I get a carriage return or a colon character. This tells me //I got all the characters I want for the response. Now we call my delegate which calls the correct response //function. The datareceived event can fire mid response, so you need someway to know when you have the whole //message. private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { string CurrentLine = ""; string Data = serialPortSensor.ReadExisting(); Data.Replace("\n", ""); foreach (char c in Data) { if (c == '\r' || c == ':') { sb.Append(c); CurrentLine = sb.ToString(); sb.Clear(); CallbackResponse(CurrentLine); //calls our correct response function depending on the current delegate assigned } else { sb.Append(c); } } } 

我会把它放在后台工作器中,当你按下按钮或其他东西时,你可以将当前状态设置为SendCmd1

按下按钮

 private void buttonStart_Click(object sender, EventArgs e) { if(!backgroundWorker1.IsBusy) { currentState = StateMachine.SendCmd1; backgroundWorker1.RunWorkerAsync(); } } 

背景工作者做工作事件

 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { while (true) { if (backgroundWorker1.CancellationPending) break; do_State_Machine(); Thread.Sleep(100); } } 

编辑:您可以使用invoke从后台工作线程更新GUI。

 this.Invoke((MethodInvoker)delegate { image = Image.FromFile(path); //do some stuff on image using Graphics, adding texts etc. picturebox1.Image = image; });