c# - 跨线程操作无效:控件“listBox1”从创建它的线程以外的线程访问
问题描述
我想将项目添加到 Modbus_request_event 中的 listBox1。我已经完成了为这个问题提供的解决方案并使用 MethodInvoker Delegate 修改了代码,但它仍然不会将项目添加到 listBox1。这是我的代码
public void Modbus_Request_Event(object sender, ModbusSlaveRequestEventArgs e)
{
//disassemble packet from master
byte fc = e.Message.FunctionCode;
byte[] data = e.Message.MessageFrame;
byte[] byteStartAddress = new byte[] { data[3], data[2] };
byte[] byteNum = new byte[] { data[5], data[4] };
short StartAddress = BitConverter.ToInt16(byteStartAddress, 0);
short NumOfPoint = BitConverter.ToInt16(byteNum, 0);
string fc1 = Convert.ToString(fc);
string StartAddress1 = Convert.ToString(StartAddress);
string NumOfPoints1 = Convert.ToString(NumOfPoint);
/*Adds the items to listBox1*/
Invoke(new MethodInvoker(delegate () { listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1); }));
//it runs infinitely not able to add to listbox//
}
谁能帮我解决这个问题?
解决方案
首先,副本是副本。您不能从另一个线程修改 UI。在 .NET 4.5 之前,人们会使用Invoke
或BeginInvoke
在控件上将委托编组到 UI 线程并在那里运行它。问题的代码虽然自己调用Invoke()
,但本质上是在它当前所在的线程上运行委托。
简而言之:
Invoke(new MethodInvoker(delegate () { listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1); }));
就线程而言,本质上与此相同:
listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1);
在 .NET 4.5 及更高版本中,您可以使用 Progress 来报告任何线程或任务的进度,如在异步 API 中启用进度和取消中所示。鉴于最早受支持的 .NET 版本是 4.5.2,您可以假设该类随处可用。
通过使用Progress<T>
和IProgress<T>
界面,您可以将事件与 UI分离,这意味着您可以以任何您想要的方式处理数据,即使是在不同的表单上。您可以将 Modbus 类移动到不同的类或库,以使其与 UI 分开。
Progress<T>
在最简单的情况下,您可以在表单的构造函数中实例化该类,并通过IProgress<T>
事件处理程序的接口调用它,例如:
public class ModbusData
{
public byte Fc {get; set;}
public short StartAddress {get; set;}
public short NumOfPoints {get; set;}
}
public class MyForm : ...
{
IProgress<ModbusData> _modbusProgress;
public MyForm()
{
__modbusProgress=new Progress<ModbusData>(ReportProgress);
}
public void ReportProgress(ModbusData data)
{
listBox1.Items.Add(data.fc1.ToString());
listBox1.Items.Add(dta.StartAddress1.ToString());
listBox1.Items.Add(data.NumOfPoints1.ToString());
}
并报告事件的进展,无论它是在哪个线程上提出的:
public void Modbus_Request_Event(object sender, ModbusSlaveRequestEventArgs e)
{
//disassemble packet from master
byte fc = e.Message.FunctionCode;
byte[] data = e.Message.MessageFrame;
byte[] byteStartAddress = new byte[] { data[3], data[2] };
byte[] byteNum = new byte[] { data[5], data[4] };
short StartAddress = BitConverter.ToInt16(byteStartAddress, 0);
short NumOfPoint = BitConverter.ToInt16(byteNum, 0);
var modData = new ModbusData {
Fc = fc,
StartAddress = StartAddress,
NumOfPoints = NumOfPoint
};
_progress.Report(modData);
}
如果您决定将 Modbus 类移动到另一个类,您所要做的就是IProgress<ModbusData>
在开始使用它们之前将一个实例传递给它们。
例如 :
class MyModbusController
{
IProgress<ModbusData> _modbusProgress;
public MyModbusController(IProgress<ModbusData> progress)
{
_modbusProgress=progress;
}
public void Modbus_Request_Event(...)
}
推荐阅读
- google-cloud-load-balancer - 后端利用率 - 谷歌云平台负载均衡器 - 500%
- vb.net - WebRequest 在某些网站上出现“请求被中止:无法创建 SSL/TLS 安全通道”错误。(并非在所有网站上)
- cqrs - 如何将“EventStore”中的聚合/读取模型保存在数据库中?
- mysql - 使用 SQL“sum”和“over”语法的 Totals 和 Runningtotals
- python-3.x - Gtk_widget.set_sensitive() 无法正常工作
- asp.net-core - 构建错误 - 命名空间“Microsoft”中不存在类型或命名空间名称“AspNetCore”(您是否缺少程序集引用?
- javascript - 如何更新从函数返回的变量?
- sql - 是否有任何方法或其他方法可以通过带有字符串值的数据表进行排序
- class - Xamarin - 如何将变量传递给视图模型
- javascript - 如何从字符串计算数组元素的计数