0

我有一个应用程序,通过单击按钮通过 PC 串行端口向微型计算机发送指令。然后,微流回触发数据接收事件处理程序的数据。这被捕获到一个字符串中。

此时我希望使用字符串的数据并填充我的列表视图框。我可以使用调用、委托来执行此操作,因为我仍在接收数据的线程中。

有什么方法可以在线程退出后调用事件处理程序或简单例程来执行此操作,因此我不需要使用调用、委托?如果它由按钮触发,则构建例程可以正常工作,但我希望以编程方式调用它来完成任务。

希望它足够清楚,这是我的第一篇文章。

编辑:这是一些示例代码 -

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;

    }
}

字符串文本的第一个字节是输入内容的标识符。这反过来调用一个函数,该函数使用调用委托方法填充主窗体上的标签,因为它在接收数据的线程中运行。对下载数据函数的默认调用传递文本并对其进行分类,因为这是大量事件。然后将结果传递到我的列表视图框到相关列中。我想摆脱使用调用委托方法。我需要退出 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;
    }

}
4

2 回答 2

0

看起来您真正感兴趣的事件是PinChange。所以处理它,检查 CTS,然后使用BeginInvoke一条消息发送回您的 UI 线程,这可以清空串行端口缓冲区(无需等待)、解析数据并更新控件。


作为进一步说明,该DataReceived事件实际上在 Microsoft 提供的System.IO.Ports.SerialPort类上是无用的。如果您有兴趣在数据到达后立即对其进行处理,请BeginRead与回调一起使用。

于 2013-05-02T00:27:23.717 回答
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 的代码定义一个事件,但为所有情况添加事件以允许您更好地组件化协议并将串行代码移动到单独的类中可能是个好主意:

// 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 线程,如下所示。这是一篇可能也有帮助的文章,Cross-Thread Events

于 2013-05-02T17:03:43.897 回答