c# Passing parameters on event from another class to main class - c#

I have a main (Form1) Class, and a second class which handles all the Serial Communication (ComPort)
When a new data is received through the Serial port (Event), I'd like to pass it to the Form1 and do some manipulation on this data.
Little Example: Creating long string from received data
Form1.cs
public partial class Form1 : Form
{
//Creating instance of SerialCommunication.
....
....
string receivedString = ""; //Here I will store all data
public void addNewDataMethod(string newDataFromSerial)
{
receivedString = receivedString + newDataFromSerial;
MessageBox.Show(receivedString);
}
}
SerialCommunication.cs
public partial class SerialCommunicationPort
{
constructor()
{
.... //open serial port with the relevant parameters
ComPort.DataReceived += new SerialDataReceivedEventHandler(ComPortBufferRead);//Create Handler
}
public void ComPortBufferRead(object sender, SerialDataReceivedEventArgs e)
{
//create array to store buffer data
byte[] inputData = new byte[ComPort.BytesToRead];
//read the data from buffer
ComPort.Read(inputData, 0, ComPort.BytesToRead);
//***** What should I write here in order to pass "inputData" to Form1.addNewDataMethod ?
}
}
I Tried the following:
Form1 Form;
Form1.addNewDataMethod(inputData.ToString());
The above code will generate an error: use of unassigned local variable "Form"
Form1 Form1 = new Form1();
Form1.addNewDataMethod(inputData.ToString());
the above will create a new instance of Form1, and will not contain the previous received data.
Any Suggestions ?

Create an event in SerialCommunication class which will be raised when data is arrived:
public partial class SerialCommunicationPort
{
public event Action<byte[]> DataReceived;
public void ComPortBufferRead(object sender, SerialDataReceivedEventArgs e)
{
byte[] inputData = new byte[ComPort.BytesToRead];
ComPort.Read(inputData, 0, ComPort.BytesToRead);
if (DataReceived != null)
DataReceived(inputData);
}
}
Then subscribe to this event in your form.
public partial class Form1 : Form
{
SerialCommunication serialCommunication;
public Form1()
{
InitializeComponent();
serialCommunication = new SerialCommunication();
serialCommunication.DataReceived += SerialCommunication_DataReceived;
}
private void SerialCommunication_DataReceived(byte[] data)
{
// get string from byte array
// and call addNewDataMethod
}
}
If you want to follow Microsoft coding guidelines for WinForms, then instead of Action<byte[]> delegate you should use EventHandler<DataReceivedEventArgs> delegate, where DataReceivedEventArgs is class inherited from EventArgs.

probably the best way would be to use events. Define an event which would an EventArgs parameter that would take a string. In Form1 subscribe to the event and in your serial communication class just trigger the event passing the string you want to the eventargs.
The way you wanted should work as well, but something like this:
Form1 Form = new Form1();
Form.Show(); // to display it...
/// to other stuff...
Form.addNewDataMethod(inputData); // call the method on the INSTANCE which you opened!
similar to usage of methods, you could define a property in Form1 :
private string _myString = "";
public string MyString {
private get {
}
set {
string _myString= value;
// to with the string what you want
}
}
and then similar to the method call use
Form.MyString = "abc";
Hope this help!

Related

Add delegate to Windows Form

Problem: I want to write the same message to a textbox control that I am writing to a log file.
I have a windows form (Form1.cs) that calls a crosscutting class of static methods. In each of the crosscutting methods, they call WriteLogEntry to update a log file of what they are doing. I'd like to send back an event to Form1 so I can write the same log message to a control on the form.
I have looked at events but do not understand enough to make sense of the examples and have not found a simple enough example to do what I want. Can someone show me a simiple example of how to add an event to my code to accomplish this?
namespace MainForm
{
public delegate void MyDel(string str);
public partial class Form1 : Form
{
public event MyDel MyEvent;
public Form1()
{
InitializeComponent();
MyEvent += new MyDel(WriteSomething);
Crosscutting.DoSomething();
}
public void WriteSomething(string message)
{
Console.WriteLine(message);
}
}
//Crosscutting.cs
public class Crosscutting
{
private static void WriteLogEntry(string message)
{
// Code to write message to log file.
}
public static void DoSomething()
{
WriteSomething obj = new WriteSomething();
// Code to do something.
WriteLogEntry("I'm doing something");
}
}
}
After not being able to figure out how to use a delegate to get back to the form, I tried another way. By creating an instance of Form1 on "MyClass", I was able to use a public method to write back to the form. Not the way I wanted, but it is a way to get it done for now. If anyone can explain how to do this a better way, please do so.
public partial class Form1 : Form
{
private string message = string.Empty;
public static Form1 form;
public Form1()
{
InitializeComponent();
form = this;
}
public void UpdateTextBox(string message)
{
textBox1.Text += message + Environment.NewLine;
this.Update();
}
private void button1_Click(object sender, EventArgs e)
{
var myClass = new MyClass();
myClass.DoSomething();
}
}
public class MyClass
{
public void DoSomething()
{
Log("I did something");
}
private void Log(string message)
{
Console.WriteLine(message);
Form1.form.UpdateTextBox(message);
}
}

How to know if it is a reader in a delegate event callback?

I have a question about callback.
I have two forms(VS2010).
I have made a class that raise an event when the ''valueIN'' change.
I set a delegate in my class so that any Form can get the ValueIN when it changes.
The problem
I create the Form2 object and link a callback to it so it is able to get ''valueIN''. but if the form2 object is not instantiated the run time tells me that there is no instantiation of the object.
So I would like to know how do I know that Form2 exists in my WorkingStation.
This line: SetValueINValCallback(value_received);
should looks like something (In workstationlooking at Form2): if(SetValINCallbackFn.exists) SetValueINValCallback(value_received);
Form2
namespace DelegateTest
{
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
using System.IO;
public delegate void DEL_SetValIN(Single value);//test event
public partial class Form1 : Form
{
IBAAdsServer _adsServer;
WorkingStation WorkStation;
public Form1()
{
InitializeComponent();
WorkingStation WorkStation = new WorkingStation(_adsServer);
}
private void btn_Frm2Open_Click(object sender, EventArgs e)
{
Form2 Form2Test = new Form2();
WorkStation.SetValueINValCallback += new DEL_SetValIN(Form2Test.SetValINCallbackFn);
Form2Test.Show();
}
}
}
namespace DelegateTest
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public void SetValINCallbackFn(Single Value_received)//refresh valueIN
{
label1.Text = Value_received.ToString("F1");
}
}
}
WorkStation
namespace DelegateTest
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;//msgbox
using Beckhoff.App.Ads.Core;
using Beckhoff.App.Ads.Core.Plc;
using TwinCAT.Ads;
public class WorkingStation
{//working station class
public DEL_SetValIN SetValueINValCallback;
private static IBAAdsCncClient _cncClient;
private static IBAAdsPlcClient _plcClient;
public static List<IBAAdsNotification> _notifications;
//no need public event System.EventHandler e_RefreshValueIN;//Input value has changed
public WorkingStation(IBAAdsServer _adsServer) //constructor
{
try
{
_cncClient = _adsServer.GetAdsClient<IBAAdsCncClient>("CNC");
_plcClient = _adsServer.GetAdsClient<IBAAdsPlcClient>("PLC");
_notifications = new List<IBAAdsNotification>();
var _refreshValueIN = new OnChangeDeviceNotification("GlobalVar.PLC.valueInput", ValINHasChanged);//event handler value
_plcClient.AddPlcDeviceNotification(_refreshValueIN);
_notifications.Add(_refreshValueIN);
}
catch (Exception Except)
{ MessageBox.Show("Error init object:" + Except.Message); }
}
~WorkingStation()//destructor
{
foreach (var notify in _notifications)
{
_plcClient.RemovePlcDeviceNotification(notify);
}
}
protected virtual void ValINHasChanged(object sender, BAAdsNotificationItem notification)//get Input value
{//event method
Single value_received = 0;
try
{
value_received = _plcClient.ReadSymbol<Single>("valueInput");
SetValueINValCallback(value_received);
//no need EventHandler E_VIChange = e_RefreshValueIN;
//no need if (E_VIChange != null)
//no need E_VIChange(this, EventArgs.Empty);
}
catch (Exception except)
{
MessageBox.Show("bad event (valueIN): " + except.Message);
}
}
}
}
or does it exist another way to pass event from a class to multiple Forms?
I need those values to draw a chart in Form2 for an example.
Thanks in advance for your help!
Sorry because I didn't see that I was raising an event.
If I've made a delegate is to subscribe to the event, so I don't need to had a raising event, I just need to subscribe.
The error raised because if you code an event like:
protected virtual void ValINHasChanged(object sender, BAAdsNotificationItem notification)//get Input value
{//event method
{
EventHandler E_VIChange = e_RefreshValueIN;
if (E_VIChange != null)
E_VIChange(this, EventArgs.Empty);
}
You need to have a subscriber like
subscriber += new EventHandler... and so on
And you don^t need it anymore because you have the delegate that make the job. so you just have to subscribe to the delegate callback to have your info.
Sorry for the wasting of time.
Best regards

Update a winform from an Interface callback from another class and thread

I have an winform and an interface callback that continuously sends updates. I want to be able to update a label1.Text from the callback interface. However since the intrface runs on an seperate thread I do not think i can do it directly so I was trying to use a delegate and invoke.
However I am running into an error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created - at
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
Here is the full code.
public partial class Form1 : Form
{
MyCallBack callback;
public delegate void UpdateDelegate(string myString);
public UpdateDelegate myDelegate;
public Form1()
{
InitializeComponent();
myDelegate = new UpdateDelegate(UpdateDelegateMethod);
callback = new MyCallBack(this);
CallBackInterfaceClass.SetCallBack(callback);
callback.OnUpdate();
}
public void UpdateDelegateMethod (String str)
{
label1.Text = str;
}
}
class MyTestCallBack : Callback
{
public Form1 form1;
public SomeObject so;
public MyTestCallBack(Form1 form)
{
this.form1 = form;
}
public void OnUpdate(SomeObject someobj)
{
so = someobj;
OnUpdate();
}
public void OnUpdate()
{
form1.Invoke(form1.myDelegate, new Object[] { so.getString() });
}
}
Two questions.
Can anyone explain what I am doing wrong?
Is this actually best method to do this?
Here is the answer based on the reply by bokibeg (see below) with a couple of modifications to make it work:
public partial class Form1 : Form {
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action(() =>
{
callback_MyTestCallBackEvent(sender, e);
}));
return;
}
label1.Text = e.SomeObject.GetDisplayString();
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(this, e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
} }
Here's what I'd do:
public partial class Form1 : Form
{
MyTestCallBack _callback;
public Form1()
{
InitializeComponent();
_callback = new MyTestCallBack();
_callback.MyTestCallBackEvent += callback_MyTestCallBackEvent;
_callback.OnUpdate();
}
private callback_MyTestCallBackEvent(MyTestCallBackEventArgs e)
{
// TODO: Invoke code here with e.g. label1.Text = e.SomeObj.ToString()...
}
}
class MyTestCallBackEventArgs : EventArgs
{
public SomeObject SomeObj { get; set; }
}
class MyTestCallBack : Callback
{
public event EventHandler<MyTestCallBackEventArgs> MyTestCallBackEvent;
private SomeObject _so;
public MyTestCallBack()
{
//
}
protected virtual void OnMyTestCallBackEvent(MyTestCallBackEventArgs e)
{
if (MyTestCallBackEvent != null)
MyTestCallBackEvent(e);
}
public void OnUpdate(SomeObject someobj)
{
_so = someobj;
OnMyTestCallBackEvent(new MyTestCallBackEventArgs { SomeObject = _so });
}
}
It separates the GUI logic from whatever that thread is doing. It fires an event and it's Form's duty to do whatever it pleases with it.
I'm not sure if this compiles, I wrote it in text pad. Tell me if you have questions.
You probably just learned about delegates and got carried away with it, this is similar as it uses an event but the event is properly placed in the "back end" logic - form may or may not use it. Also notice how form's code is a lot better, it doesn't have so much boilerplate code just to implement some background service class.
Note however that MyTestCallBackEvent event may keep firing even after you close the form so make sure you unsubscribe from it when the form closes or disposes (or whenever you feel like form doesn't need it anymore).
Oh and I almost forgot: the original error you were getting is because you called Invoke when one wasn't required and Form definitely wasn't ready for it. Read this question to see how to safely invoke controls.

How to know when dll events fired

I created a dll contain this event handler:
public void tcp1_Data(object sender, Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
response = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
tcp.Close();
}
this will fire when server write some thing in socket connection. so by this, I can read the data on socket.
I used this dll in another project. I want to know in my project (that used dll) exactly when server is writing data on socket connection. as you see in tcp1_Data event, I set result into response variable and in main project (that used dll), I checked this variable polling (if response is not null, it means that this event fired). but Its not what I want. I dont want check this variable all the time.
is there any other way?
I tried this as #ThorstenDittmar said:
my dll project (its name is ClientSample) contain:
TheClassInDLL Class
public class TheClassInDLL
{
public event EventHandler<MyEventArgs> DataEventCalled;
public void tcp1_Data(object sender, Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
// Note: LOCAL variable
string myresponse = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
// Call the new event here
if (DataEventCalled != null)
DataEventCalled(this, new MyEventArgs(myresponse));
tcp.Close();
}
// We use this class to pass arguments to the event handler
public class MyEventArgs : EventArgs
{
public MyEventArgs(string response)
{
this.Response = response;
}
public string Response
{
get;
private set;
}
}
}
TCPSample class
public class TCPSample
{
Tcp tcp = new Tcp();
tcp.Data += new System.EventHandler
and in another project that I used above dll:
public partial class Form1 : Form
{
private TheClassInDLL myClass;
ClientSample.TCPSample t = new ClientSample.TCPSample();
public Form1()
{
InitializeComponent();
myClass = new TheClassInDLL();
myClass.DataEventCalled += DataEvent;
}
private void button1_Click(object sender, EventArgs e)
{
t.newTCP();
}
private void DataEvent(object sender, TheClassInDLL.MyEventArgs e)
{
MessageBox.Show(e.Response);
}
}
but It didnt work, DataEvent never happend.
Thanks for any helping...
What you wrote here is an event handler that is called when something happens. There must be a class containing this event handler. Instead of writing a global response variable, declare and invoke another event you can subscribe to from outside that class like this:
public class <TheClassInDLL>
{
public event EventHandler<MyEventArgs> DataEventCalled;
// SNIP: All the other code that leads to tcp1_Data being called
...
// The event handler that's called by some code in the class
public void tcp1_Data(object sender, Dart.Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
// Note: LOCAL variable
string myresponse = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
// Call the new event here
if (DataEventCalled != null)
DataEventCalled(this, new MyEventArgs(myresponse));
tcp.Close();
}
// We use this class to pass arguments to the event handler
public class MyEventArgs : EventArgs
{
public MyEventArgs(string response)
{
this.Response = response;
}
public string Response
{
get;
private set;
}
}
}
From the caller, you use it like this:
public class <TheCallingClassOutsideDLL>
{
private <TheClassInDLL> myClass;
public TheCallingClassOutsideDLL()
{
myClass = new TheClassInDLL();
myClass.DataEventCalled += DataEvent;
}
private void DataEvent(object sender, <TheClassInDLL>.MyEventArgs e)
{
Console.WriteLine(e.Response);
}
}
Of course you need to replace <TheClassInDLL> and <TheCallingClassOutsideDLL> with the real class names! Creating additional classes of course doesn't work!
For that you got to define your own event and raise it when needed...
for Example -> In the class where you set the "response" variable define an event
//your custom event
public event EventHandler<CustomEventArgs> MyCustomEvent;
//This will raise your event and notify all who registered
private void RaiseMyCustomEvent(CustomEventArgs e)
{
var handler = MyCustomEvent;
if (handler != null)
{
handler(this, e);
}
}
Maybe you will also need CustomEventArgs (used in the example above)
public class CustomEventArgs : EventArgs
{
public String Message {get;private set;}
public CustomEventArgs(String message){
this.Message = message;
}
}
The class which is using the dll and that wants to get notified needs to register for this event
YourDllClassInstance.MyCustomEvent += OnMyCustomEvent;
public void OnMyCustomEvent(object sender, CustomEventArgs e){
Console.WriteLine("Event received");
}
That means in your dll class you got to do something like the following when you want to raise the event
response = "blablabla";
RaiseMyCustomEvent(new CustomEventArgs(response);
Is that what you where asking for?

Access form component from another class

I hope that the title and this simple example says everything.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdateLabel(string str)
{
label1.Text = str;
MessageBox.Show("Hello");
}
private void buttonIn_Click(object sender, EventArgs e)
{
UpdateLabel("inside");
}
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod();
}
}
public class MyClass
{
public void MyMethod()
{
Form1 MyForm1 = new Form1();
MyForm1.UpdateLabel("outside");
}
}
When I'm trying to change lable1 from MyClass it does nothing. But I can get to the UpdateLable method from outside, it says Hello to me, it just doesn't change the label.
Use a delegate for setting your label
public class MyClass {
Action<String> labelSetter;
public MyClass(Action<String> labelSetter) {
this.labelSetter = labelSetter;
}
public void MyMethod() {
labelSetter("outside");
}
}
.
public void buttonOut_Click(object sender, EventArgs e) {
var outside = new MyClass(UpdateLabel);
outside.MyMethod();
}
a bit unsure because the example actually leaves some bits unclear... but here is a try:
public class MyClass
{
public void MyMethod(Form1 F)
{
F.UpdateLabel("outside");
}
}
this works as long as MyClass is NOT running on a different thread - otherwise the call to UpdataLabel must be synchronized with the UI thread...
EDIT:
private void buttonOut_Click(object sender, EventArgs e)
{
MyClass Outside = new MyClass();
Outside.MyMethod(this);
}
Either go with Yahia's way (it has been updated and will work correctly) or try the following (probably overkill for what you're trying to do... whatever that is).
UPDATE:
Based on your comment in the question, you are also doing the work in MyClass on a different thread. Code change is below.
public partial class Form1 : Form
{
// keep a reference to a MyClass object for your Form's lifetime
private MyClass _myClass;
public Form1()
{
InitializeComponent();
// Intstantiate your MyClass object so you can use it.
_myClass = new MyClass();
// Register to the MyClass event called UpdateLabel.
// Anytime MyClass raises the event, your form will respond
// by running the UpdateLabelFromMyClass method.
_myClass.UpdateLabel += UpdateLabelFromMyClass;
}
private void button1_Click(object sender, EventArgs e)
{
// Call MyMethod in your MyClass object. It will raise
// the UpdateLabel event.
// update, you are calling this on a background thread?
_myClass.MyMethod();
}
void UpdateLabelFromMyClass(string message)
{
// Update your label with whatever message is passed in
// from the MyClass.UpdateLabel event.
// UPDATE: If called from a background thread you'll need this:
this.BeginInvoke( (Action) (()=>
{
label1.Text = message;
}));
}
}
public class MyClass
{
// An event that can be raised, allowing other classes to
// subscribe to it and do what they like with the message.
public event Action<string> UpdateLabel;
public void MyMethod()
{
// Raise the UpdateLabel event, passing "Outside" as
// the message.
UpdateLabel("Outside");
}
}
After wasting a ton of time on what should be a simple task and trying every answer on stack overflow, I said, if C# wants to make it stupid hard to change the text of a simple label, I am going to come up with a stupid fix.
Here is what you do:
In Form1 or whatever form has the label you want add:
public void setStatus()
{
lblStatus.Text = status;
}
public static string status;
Now, add a timer to Form1 and have it run "setStatus();" on every tick
Now, in any class, just write:
Form1.status = "Change label to this text";
you need to make both the method MyMethod and the label in question static. But if you do then you cannot access MyMethod through a new instance of the form instead you have to access it directly like Form1.MyMethod(). But if you do make the label the static visual studio will make it non-static one you access the label from the designer so you will have to keep making it static from form1.designer.cs. Also if you do make the label static change every line that refers to any of its properties so if it says this.label1.Text change it to label1.Text. This should give you the desired effect

Categories