我有一个应用程序,通过按钮点击通过PC串口发送指令给微型。然后微控制器回送数据,该数据触发数据接收事件处理程序。这被捕获成一个字符串。
此时我希望使用字符串的数据并填充我的listview框。我可以使用invoke,delegate来执行此操作,因为我仍然在数据接收线程中。
有什么方法可以在线程退出后调用事件处理程序或简单例程来执行此操作,因此我不需要使用invoke,delegate?如果它由按钮触发,那么构建例程就可以了,但我希望以编程方式调用它来完成任务。
希望它足够清楚,这是我的第一篇文章。
修改:以下是一些示例代码 -
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//use 28591 or("ISO-8859-1") to cover all hex bytes from 0 - 255
serialPort1.Encoding = Encoding.GetEncoding(28591);
//wait for download to complete by monitoring cts line
if (HoldData == true)
{
while (serialPort1.CtsHolding == true) ;
HoldData = false;
}
else
Thread.Sleep(50);
string text = serialPort1.ReadExisting();
switch (text[0])
{
case '?': MemLabelUpdate(); break;
case '>': WriteConfig(text); break;
case '=': SealTest(text); break;
case '<': CurrentNumber(text); break;
default: DataDownload(text); break;
}
}
字符串文本的第一个字节是关于内容的标识符。这反过来调用一个函数,该函数使用调用委托方法在主表单上填充标签,因为它在数据接收线程中运行。对下载数据功能的默认调用会传递文本并将其排序,因为这是一大堆事件。然后将结果传递到我的列表视图框中的相关列。我想摆脱使用invoke委托方法。我需要退出port_datareceived线程来执行此操作,退出后,输入我的函数以更新列表,如下所示。我如何以编程方式触发此类事件。
private void btnDisplayData_Click(object sender, EventArgs e)
{
int SectionStart = 10;
int SectionEnd = 8;
listView1.Items.Clear();
listView1.View = View.Details;
listView1.GridLines = true;
//Add columns to listview
listView1.Columns.Add("Event", 80, HorizontalAlignment.Center);
listView1.Columns.Add("Time", 80, HorizontalAlignment.Center);
listView1.Columns.Add("Date", 80, HorizontalAlignment.Center);
//Print results to listview box
ListViewItem ListItem;
for (int i = 0; i < 10; i++)
{
ListItem = listView1.Items.Add(DownloadedData.Substring(SectionStart, SectionEnd));
SectionStart += 8;
ListItem.SubItems.Add(DownloadedData.Substring(SectionStart, SectionEnd));
SectionStart += 8;
ListItem.SubItems.Add(DownloadedData.Substring(SectionStart, SectionEnd));
SectionStart += 8;
}
foreach (ColumnHeader column in listView1.Columns)
{
column.Width = -2;
}
}
答案 0 :(得分:0)
您真正感兴趣的事件似乎是PinChange
。所以处理它,检查CTS,然后使用BeginInvoke
将一条消息发送回UI线程,这可以清空串口缓冲区(无需等待),解析数据并更新控件。
另外请注意,DataReceived
事件在Microsoft提供的System.IO.Ports.SerialPort
类中实际上是无用的。如果您有兴趣在数据到达时立即对数据执行某些操作,请使用BeginRead
进行回调。
答案 1 :(得分:0)
您仍未提供有关线程问题的任何详细信息。 SerialPort是在UI线程,工作线程还是BackgroundWorker上创建的? SynchronizationContext很重要,如果SerialPort是在不同的线程上创建的,那么你必须使用某种机制来调用或将数据提升回UI线程来显示。
最简单的解决方案可能只是调用DownloadData方法(假设该方法更新UI或调用另一种方法来更新UI):
switch (text[0])
{
case '?': MemLabelUpdate(); break;
case '>': WriteConfig(text); break;
case '=': SealTest(text); break;
case '<': CurrentNumber(text); break;
default:
if (InvokeRequired)
Invoke((MethodInvoker)delegate { DataDownload(text); });
else
DataDownload(text);
break;
}
另一种技术是为收到的数据案例定义自己的事件。 YouOne可以为一个或所有案例定义事件。您可能只需要为将访问UI的代码定义事件,但为所有情况添加事件可能是一个好主意,以便您可以更好地组件化协议并将串行代码移动到单独的类中: / p>
// Create an EventArgs based class for the data
public class DataDownloadEventArgs : EventArgs
{
public string Data { get; set; }
public DataDownloadEventArgs(string data)
{
Data = data;
}
}
public partial class Form1 : Form
{
// Event handler when data is downloaded
public event EventHandler<DataDownloadEventArgs> DataDownloaded;
// Virtual method to raise event to observers
protected virtual void OnDataDownloaded(DataDownloadEventArgs e)
{
var handler = DataDownloaded;
if (handler != null)
{
handler(this, e);
}
}
// Form constructor
public Form1()
{
InitializeComponent();
// Add handler for the download event
DataDownloaded += new EventHandler<DataDownloadEventArgs>(DisplayData);
}
// Serial port receive event
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// snip...
switch (text[0])
{
case '?': MemLabelUpdate(); break;
case '>': WriteConfig(text); break;
case '=': SealTest(text); break;
case '<': CurrentNumber(text); break;
default:
DataDownload(text);
OnDataDownloaded(new DataDownloadEventArgs(text));
break;
}
// snip...
}
// Change btnDisplayData_Click to the following:
private void DisplayData(object sender, DataDownloadEventArgs e)
{
// insert remaining code from btnDisplayData_Click
}
}
如果您仍然遇到UI线程和事件的问题,您可以使用扩展将事件提升到UI线程,如this。这篇文章可能也有帮助,Cross-Thread Events。