线程化和更新 GUI
Threading and updating the GUI
我一直在开发一个 WinForms 实用程序,它从文本文件中获取命令 and/or 数据,并根据该文件的内容通过串行传输或请求更多数据。这一切都很完美,但我正在努力研究如何让 GUI 提供一些正在发生的事情的反馈。在其当前结构中,主窗体加载并且 Load()
事件打开文本文件并处理其内容
tp = new TProcess();
tp.FileOpen();
TProcess 和所有其他涉及通过串口通信的 类 被收集到一个编译为 DLL 的子项目中。反过来,TProcess 将创建我的 SerialDevice 对象的一个新实例(也在我的 DLL 中定义)并将我的数据传递给它,然后告诉它如何处理它,例如
//TProcess
if (File.Exists(filey))
try
{
StreamReader file = new StreamReader(@"filey");
List<clsStuff> stuffList = new List<clsStuff>();
while ((line = file.ReadLine()) != null)
{
//process and add to list
}
var SD = new SerialDevice();
SD.List = stuffList;
SD.Send();
file.Close();
}
catch (Exception e)
{
//Write Error File
}
}
我的 SD Class 定义所有端口参数,发送第一个项目数据包并使用 DataReceived 事件从远程设备接收确认数据包,然后一次发送列表中的项目,直到它已经完成并退出。
因此,我的问题(抱歉啰嗦)是我如何传递某种反馈(可能是每次我收到数据包确认时收到的像 Record X 这样的字符串),因为 GUI 线程没有创建我的 SD例如,如果这是一个更紧凑的程序,并且 GUI 线程调用了 SD,我会使用委托和 BeginInvoke,但我看不出我如何在这里做到这一点,因为 GUI 对 SD 实例一无所知,同样 SD 对 SD 实例一无所知图形用户界面。我觉得这可能可以通过 SynchronisationContext 解决,但我不知道如何实现它。
public void Send()
{
if (!serialP.IsOpen)
{
PortOpen();
serialP.ReceivedBytesThreshold = 4;
}
serialP.Write(CurrentListItem, 0, 11);
}
private void serialP_DataEvent(object sender, SerialDataReceivedEventArgs e)// This triggers when the response is received
{
byte[] Ack = new byte[4];
serialP.Read(Ack, 0, 4);
//snip of course I check these 4 bytes etc here
CurrentListItem++;
//UPDATE THE GUI BUT HOW?
Send()!;//next
}
向您的 TProcess
class 添加您的 UI 可以订阅的活动:
public event EventHandler DataRecieved;
然后在您的 serialP_DataEvent
中,您可以在适当的时候执行:
if (DataRecieved != null)
{
DataRecieved(this,EventArgs.Empty);
}
您的 UI 可以订阅这个:
tp.DataRecieved += (o,e) => { // do something to the UI };
一些注意事项:
1) 如果使用通用 EventHandler<T>
而不是 EventHandler
,则可以在事件中传递信息。其中 T
是您要发送的任何数据的 class
2) 看起来 FileOpen
是同步和阻塞的,所以它会阻止您的 UI 更新。使其异步或至少使用 Task.Run
3) 如果使其异步,则可能会遇到尝试从非 UI 线程更新 UI 的问题。您将需要使用 Invoke
将 UI 更新推回到 UI 线程(您不会说这是 WinForms 还是 WPF,但它们是相似的)。
我一直在开发一个 WinForms 实用程序,它从文本文件中获取命令 and/or 数据,并根据该文件的内容通过串行传输或请求更多数据。这一切都很完美,但我正在努力研究如何让 GUI 提供一些正在发生的事情的反馈。在其当前结构中,主窗体加载并且 Load()
事件打开文本文件并处理其内容
tp = new TProcess();
tp.FileOpen();
TProcess 和所有其他涉及通过串口通信的 类 被收集到一个编译为 DLL 的子项目中。反过来,TProcess 将创建我的 SerialDevice 对象的一个新实例(也在我的 DLL 中定义)并将我的数据传递给它,然后告诉它如何处理它,例如
//TProcess
if (File.Exists(filey))
try
{
StreamReader file = new StreamReader(@"filey");
List<clsStuff> stuffList = new List<clsStuff>();
while ((line = file.ReadLine()) != null)
{
//process and add to list
}
var SD = new SerialDevice();
SD.List = stuffList;
SD.Send();
file.Close();
}
catch (Exception e)
{
//Write Error File
}
}
我的 SD Class 定义所有端口参数,发送第一个项目数据包并使用 DataReceived 事件从远程设备接收确认数据包,然后一次发送列表中的项目,直到它已经完成并退出。 因此,我的问题(抱歉啰嗦)是我如何传递某种反馈(可能是每次我收到数据包确认时收到的像 Record X 这样的字符串),因为 GUI 线程没有创建我的 SD例如,如果这是一个更紧凑的程序,并且 GUI 线程调用了 SD,我会使用委托和 BeginInvoke,但我看不出我如何在这里做到这一点,因为 GUI 对 SD 实例一无所知,同样 SD 对 SD 实例一无所知图形用户界面。我觉得这可能可以通过 SynchronisationContext 解决,但我不知道如何实现它。
public void Send()
{
if (!serialP.IsOpen)
{
PortOpen();
serialP.ReceivedBytesThreshold = 4;
}
serialP.Write(CurrentListItem, 0, 11);
}
private void serialP_DataEvent(object sender, SerialDataReceivedEventArgs e)// This triggers when the response is received
{
byte[] Ack = new byte[4];
serialP.Read(Ack, 0, 4);
//snip of course I check these 4 bytes etc here
CurrentListItem++;
//UPDATE THE GUI BUT HOW?
Send()!;//next
}
向您的 TProcess
class 添加您的 UI 可以订阅的活动:
public event EventHandler DataRecieved;
然后在您的 serialP_DataEvent
中,您可以在适当的时候执行:
if (DataRecieved != null)
{
DataRecieved(this,EventArgs.Empty);
}
您的 UI 可以订阅这个:
tp.DataRecieved += (o,e) => { // do something to the UI };
一些注意事项:
1) 如果使用通用 EventHandler<T>
而不是 EventHandler
,则可以在事件中传递信息。其中 T
是您要发送的任何数据的 class
2) 看起来 FileOpen
是同步和阻塞的,所以它会阻止您的 UI 更新。使其异步或至少使用 Task.Run
3) 如果使其异步,则可能会遇到尝试从非 UI 线程更新 UI 的问题。您将需要使用 Invoke
将 UI 更新推回到 UI 线程(您不会说这是 WinForms 还是 WPF,但它们是相似的)。