First of all, the duplicate is a duplicate. You can't modify the UI from another thread. Before .NET 4.5, people would use Invoke
or BeginInvoke
on a control to marshal a delegate to the UI thread and run it there. The question's code though calls Invoke()
by itself, essentially running the delegate on the thread it's currently on.
In short this:
Invoke(new MethodInvoker(delegate () { listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1); }));
Is essentially the same as this, as far as threading is concerned :
listBox1.Items.Add(fc1); listBox1.Items.Add(StartAddress1); listBox1.Items.Add(NumOfPoints1);
With .NET 4.5 and later, you can use Progress to report progress from any thread or task, as shown in Enabling Progress and Cancellation in Async APIs. Given that the earliest supported .NET version is 4.5.2, you can assume that class is available everywhere.
By using the Progress<T>
and the IProgress<T>
interface you can decouple the event from the UI, which means you can handle the data any way you want, even on different forms. You could move the Modbus class to a different class or library to keep it separate from the UI.
In the simplest case, you can instantiate the Progress<T>
class in your form's constructor and call it through the IProgress<T>
interface from the event handler, eg :
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());
}
And report progress from the event, no matter which thread it's raised on :
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);
}
If you decide to move the Modbus classes to another class, all you have to do is pass an IProgress<ModbusData>
instance to them before you start using them.
For example :
class MyModbusController
{
IProgress<ModbusData> _modbusProgress;
public MyModbusController(IProgress<ModbusData> progress)
{
_modbusProgress=progress;
}
public void Modbus_Request_Event(...)
}