c# - 在线程之间传递事件从单独的 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!!!!");
}
这是正确的方法吗?
解决方案
您可以将事件处理程序作为参数传递给构造函数
public event EventHandler BluetoothDeviceDiscovered;
public BluetoothScan(MainForm mainForm, EventHandler bluetoothDeviceDiscovered)
{
System.Diagnostics.Debug.WriteLine("Starting BluetoothScan Class");
BluetoothDeviceDiscovered += bluetoothDeviceDiscovered
Run();
}
就个人而言,我不喜欢在构造函数上调用方法。它可能是错误或性能问题的根源
在基于类的面向对象编程中,构造函数(缩写:ctor)是一种特殊类型的子例程,被调用来创建对象。它准备新对象以供使用,通常接受构造函数用来设置所需成员变量的参数。
您可以将 eventhandler 作为参数传递并稍后调用 Run
推荐阅读
- python - 如何将创建的 EXE 文件转换回 Python?
- python - 使用 discord.py 时异步 def 出现语法错误
- javascript - Vue.Draggable un-choose 的事件是什么?
- ajax - 提交具有多个部分视图的表单
- php - php行尾出错
- azure - 无法使用 WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT 设置横向扩展 azure 函数实例
- elasticsearch - 弹性搜索 - Kibana - Jenkins Pipeline 作业日志
- git - 致命的-“起源”似乎不是 git 存储库
- reactjs - 在 mapStateToProps 中使用三元
- android - How to set width and height of cell item in GridView programmatically in Android