首页 > 解决方案 > 在线程之间传递事件从单独的 C# 类运行

问题描述

我正在努力在与我的主表单不同的类中启动的线程之间传递数据。我相信(我可能是错的)我应该使用一个事件。我遇到的问题是,当我调用 BluetoothScan 类并在订阅事件之前启动线程时,我的订阅者始终为空:

BluetoothScan bluetoothScan = new BluetoothScan(this);            
bluetoothScan.BluetoothDeviceDiscovered += OnBluetoothDeviceDiscovered;

如何在启动线程之前订阅事件?

我有我的主要形式:

using System;

using System.Windows.Forms;

//https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke?view=net-5.0#System_Windows_Forms_Control_Invoke_System_Delegate_System_Object___


namespace YieldMonitor
{
    public partial class MainForm : Form
    {
        public MainForm()
        {           
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {           
            
        }

        private void BtnConnectBT_Click(object sender, EventArgs e)
        {
            //Start looking for the yield monitor device.
            
            BluetoothScan bluetoothScan = new BluetoothScan(this);            
            bluetoothScan.BluetoothDeviceDiscovered += OnBluetoothDeviceDiscovered;
        }

        static void OnBluetoothDeviceDiscovered(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Message recieved from event");
        }

    }

}

我的班级寻找蓝牙设备,如果找到合适的设备,应该触发事件:

using InTheHand.Net.Sockets;
using System;
using System.Linq;


namespace YieldMonitor
{
    class BluetoothScan
    {

        public event EventHandler BluetoothDeviceDiscovered;

        public BluetoothScan(MainForm mainForm)
        {
            System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
            Run();
        }

        public void Run()
        {
            System.Diagnostics.Debug.WriteLine("Running BluetoothScan Class");

            string myDeviceName;
            ulong myDeviceAddress;

            BluetoothClient btClient = new BluetoothClient();
            BluetoothDeviceInfo[] btDevices = btClient.DiscoverDevices().ToArray();
            foreach (BluetoothDeviceInfo d in btDevices)
            {
                System.Diagnostics.Debug.WriteLine(d.DeviceName);
                System.Diagnostics.Debug.WriteLine(d.DeviceAddress);

                //have we found the device we are looking for?
                if (d.DeviceName == "DSD TECH HC-05")
                {
                    myDeviceName = d.DeviceName;
                    myDeviceAddress = d.DeviceAddress;
                    //Send out found adapter to the next stage
                    OnBluetoothScanned(EventArgs.Empty);
                    break;

                }
            }                      
        }

        protected virtual void OnBluetoothScanned(EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Running OnBlueToothScanned");

            EventHandler handler = BluetoothDeviceDiscovered;

            if (handler != null)// we have a subscriber to our event
            {
                System.Diagnostics.Debug.WriteLine("BluetoothScanned is Not empty");
                handler(this, e);
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("BluetoothScanned is Empty");
            }

        }

    }
}

编辑

我使用任务找到了一些不错的解决方案,一旦任务完成,我需要更新标签,即。

bool myDevicePaired = false;
var eventDevicePaired = new Progress<bool>(boDevicePaired => myDevicePaired = boDevicePaired);
await Task.Factory.StartNew(() => BluetoothPair.Run(myDeviceAddress, eventDevicePaired), TaskCreationOptions.LongRunning);

//Register the device is paired with the UI
if (myDevicePaired)
{
    BtnConnectBT.Text = "Disconnect?";
}

这对于我正在等待结束的任务(例如等待蓝牙设备连接)来说效果很好。

但是我开始用System.InvalidOperationException:'跨线程操作无效:控制'tbInfo'从创建它的线程以外的线程访问。尝试更新表单文本框时出错。

例子:

在我的 MainForm 类中:

我创建了一个我称之为事件接收器的东西......

private void BluetoothSocketEventReciever(object sender, EventArgs e)
{
        Debug.WriteLine("Event!!!"); //writes data to debug fine
        tbInfo.AppendText("Event!!!!"); //causing error
}

我创建了一个从设备读取的任务......

private void ReadDataFromDevice(UInt64 myDeviceAddress)
{
         BluetoothSocket bluetoothSocket = new BluetoothSocket(myDeviceAddress);
         bluetoothSocket.BluetoothDataRecieved += BluetoothSocketEventReciever;

         Task.Factory.StartNew(() => bluetoothSocket.Run(), TaskCreationOptions.LongRunning);
}

在我的 BluetoothSocket 类中,我有一个无休止的 while 循环,它将从套接字读取数据(希望如此) 目前它只是创建一个空的 EventArgs 以每秒触发事件:

namespace YieldMonitor
{
    class BluetoothSocket
    {
        ulong myDeviceAddress;
        public event EventHandler BluetoothDataRecieved;

        public BluetoothSocket (ulong deviceAddress)
        {
            myDeviceAddress = deviceAddress;
        }

        public void Run()
        {
            System.Diagnostics.Debug.WriteLine("Were in BluetoothSocket ... Address: " + myDeviceAddress);
            
            while (true)
            {
                Thread.Sleep(1000);
                Debug.WriteLine("In BluetoothSocket - Address = " + myDeviceAddress);
                OnBluetoothDataRecieved(EventArgs.Empty);
            }
        }

        protected virtual void OnBluetoothDataRecieved(EventArgs e)
        {
            EventHandler handler = BluetoothDataRecieved;

            if (handler != null)
            {
                handler(this, e);
            } else
            {
                //No subscribers
            }
        }
    }
}

我确定我在这里遗漏了一些简单的东西,但是如何将数据从无限循环传递到主窗体上的文本框?

编辑

想想我刚刚整理好了。

private void BluetoothSocketEventReciever(object sender, EventArgs e)
        {
            Debug.WriteLine("Event!!!");
            tbInfo.Invoke((Action)delegate
            {
                tbInfo.AppendText("Event!!!");
            });


           //tbInfo.AppendText("Event!!!!");
        }

这是正确的方法吗?

标签: c#events

解决方案


您可以将事件处理程序作为参数传递给构造函数

public event EventHandler BluetoothDeviceDiscovered;

public BluetoothScan(MainForm mainForm, EventHandler bluetoothDeviceDiscovered)
{
     System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
    BluetoothDeviceDiscovered += bluetoothDeviceDiscovered
     Run();
}

就个人而言,我不喜欢在构造函数上调用方法。它可能是错误或性能问题的根源

构造函数

在基于类的面向对象编程中,构造函数(缩写:ctor)是一种特殊类型的子例程,被调用来创建对象。它准备新对象以供使用,通常接受构造函数用来设置所需成员变量的参数。

您可以将 eventhandler 作为参数传递并稍后调用 Run


推荐阅读