首页 > 解决方案 > 跨线程操作无效:控件“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//


        }

谁能帮我解决这个问题?

标签: c#winformsmodbus-tcp

解决方案


首先,副本副本。您不能从另一个线程修改 UI。在 .NET 4.5 之前,人们会使用InvokeBeginInvoke控件上将委托编组到 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(...)
}

推荐阅读