how to access Custom EventArgs class in Button click event? - c#

As a follow up to:
access values within custom eventargs class
How do I access public variables within custom Eventargs class, using button click or any other method?
Example Custom Event Args class:
public class TraderEventArgs: EventArgs
{
private int _shares;
private string _symbol;
public TraderEventArgs(int shs, string sym)
{
this._shares = shs;
this._symbol = sym;
}
public decimal Price
{
get {return _prices;}
}
public int Shares
{
get { return _shares; }
}
}
Code behind button_click event:
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
// run trader app
myApp.Main();
// try to access TraderEventArgs class
TraderEventArgs: EventArgs ev = TraderEventArgs: EventArgs(); // Invalid
TraderEventArgs ev = new TraderEventArgs(); // this needs argument variables that are unassigned... ?
TraderEventArgs ev = (TraderEventArgs)e; // Unable to cast object of type 'System.EventArgs' to type TraderEventArgs'.
string sym = ev.Symbol.ToString();
string sharws = ev.Shares.ToString();
// do something with data
}
thanks for help.

When the button click event is raised, it's creating the EventArgs that gets passed into "e". That object was not created by you, but rather by the framework itself, and is of type EventArgs. This prevents you from being able to cast it to a different type.
If you want to have an event that raises TraderEventArgs, you need to create an event, preferably of type EventHandler<TraderEventArgs>, and raise this event somewhere that is in your control. This allows you to generate the class of the correct type, then handle it (in a separate event handler) directly.

You can't do that. The EventArgs of the Click event is always of type EventArgs; you can't expect it to be of type TraderEventArgs, because the event args is created by the button, and the button doesn't know anything about TraderEventArgs. The only way it could work is if you create your own Button control that raises the event with a TraderEventArgs instead of an EventArgs

Solution: Pass custom event args through a custom delegate and event that returns the data (Datatable, Array, etc)...using whatever button click or other event necessary.
So as a function of the delegate the correct data is returned.. I can not post the complete code here, but it is a modification of this very excellent example on event delegate usage...
Core C# and .NET
3.7. Delegates and Events
http://flylib.com/books/en/4.253.1.38/1/

Related

How do I change the value of form controls without triggering any event?

I want to change the value that is assign to control of a form in c# (visual studio 2010), while the form is loaded.
I want my form should display to the end user, but at the same time as I get the data from server, I want it to reflect the same data onto the controls. (without any using timer, thread or any event).
Example : textBox1.text ="abc";
if server is sending "xyz" than while form is already loaded testbox's value should automatically change to xyz.
without any click or any kind of event.
You have to look at how Properties in c# work:
If we decompile a simple class on sharplab.io
public class C {
public int foo
{get;set;}
}
You will see that the compile will always generate backing fields and a getter and setter method.
So if you don't want to trigger an event you will have to bypass these methods as most likely the event will be triggered in there.
This should be doable with an reflection which is normally pretty easy to do.
BUT Textbox doesn't seem to have a backing field which is easily accessible for its Text-Property. Most likely it is set by its private StringSource field. Which is from the internal type StringSource. So first we have to get the type. Get a reference to the constructor then call this and set the private field.
This is how far i've come:
private int number = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
number++;
this.textBox1.Text = number.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
number++;
Type cTorType = typeof(string[]);
string[] cTorParams = new string[] { number.ToString() };
Type type = this.textBox1.GetType().GetRuntimeFields().ElementAt(11).FieldType;
ConstructorInfo ctor = type.GetConstructor(new[] { cTorType });
object stringSourceInstance = ctor.Invoke(new[] { cTorParams });
this.textBox1.GetType().GetRuntimeFields().ElementAt(11).SetValue(this.textBox1, stringSourceInstance);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show("Changed!");
}
I'd recommend digging a bit more into reflection and see what you can find in the TextBox class by using typeof(TextBox).GetFields / .GetProperties because somewhere there must be a field or property which you can change to bypass your setter method triggering the event.
Hope this helps.

How do I pass objects in EventArgs

I have a usercontrol that raises an event after communicating with a web service. The parent handles this event when raised. What I thought would be the proper approach would be to pass the object returned from the webservice to the parent as eventargs???
If this is the proper way I can't seem to find the instructions on how to do so.
UserControl
public event EventHandler LoginCompleted;
then later after the service returns biz object:
if (this.LoginCompleted != null)
{
this.LoginCompleted(this, new EventArgs() //this is where I would attach / pass my biz object no?);
}
Parent
ctrl_Login.LoginCompleted += ctrl_Login_LoginCompleted;
....snip....
void ctrl_Login_LoginCompleted(object sender, EventArgs e)
{
//get my object returned by login
}
So my question is what would be the "approved" method for getting the user object back to the parent? Create a property class that everything can access and put it there?
You would have to declare your event using EventHandler<T> where T is your class that derives from EventArgs:
public event EventHandler<LoginCompletedEventArgs> LoginCompleted;
LoginCompletedEventArgs could look like this:
public class LoginCompletedEventArgs : EventArgs
{
private readonly YourBusinessObject _businessObject;
public LoginCompletedEventArgs(YourBusinessObject businessObject)
{
_businessObject = businessObject;
}
public YourBusinessObject BusinessObject
{
get { return _businessObject; }
}
}
Usage would be like this:
private void RaiseLoginCompleted(YourBusinessObject businessObject)
{
var handler = LoginCompleted;
if(handler == null)
return;
handler(this, new LoginCompletedEventArgs(businessObject));
}
Please notice how I implemented RaiseLoginCompleted. This is a thread-safe version of raising the event. I eliminates a possible NullReferenceException that can occur in a race condition scenario where one thread wants to raise the event and another thread un-subscribes the last handler after the if check but before actually invoking the handler.
Well, you could do all that or you could define a delegate as your EventHandler and define your properties in its signature.
Such as:
public delegate void MyEventEventHandler(int prop1, string prop2, object prop3...);
public event MyEventEventHandler MyEvent;
I recommend use named tuples with EventHandler<TEventArgs>.
I like olddog's answer. Microsoft already has this delegate EventHandler< TEventArgs >.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
You don't need to inherits from EventArgs.
Declare your event handler with named tuples.
public event EventHandler<(int id, string message, object LoginObject)> LoginCompleted;
In your client code, assign method to the LoginCompleted event handler
option 1: use lambda
LoginCompleted += (o, e) =>
{
Console.WriteLine($"Hello, sender is {o.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
};
option 2: call a local method
LoginCompleted += onLoginCompleted;
private static void onLoginCompleted (object sender, (int id, string message, object LoginObject) e)
{
Console.WriteLine($"Hello, sender is {sender.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
}
I just wrote an example, please refer to my repo
I personally like Toni Petrina's approach (see https://coderwall.com/p/wkzizq/generic-eventargs-class). It differs from the accepted answer in that you don't have to create a special EventHandler class (e.g. LoginCompletedEventArgs).
(Note: I am using VS 2015 and C# v6. In older versions of Visual Studio and C#, you may have to add using System.Linq;)
Create a generic EventArgs<T> class that inherits from EventArgs...
class EventArgs<T> : EventArgs {
public T Value { get; private set; }
public EventArgs(T val) {
Value = val;
}
}
Declare your event handler...
public event EventHandler<EventArgs<object>> LoginCompleted;
Assuming you have declared and assigned an object named loginObject, add code to raise you event...
private void RaiseLoginCompleted() {
if (LoginCompleted != null)
LoginCompleted(this, new EventArgs<object>(loginObject));
}
In your client code, add the LoginCompleted event handler (uses Linq and calls a local method)...
LoginCompleted += (o, e) => onLoginCompleted(e.Value); // calls a local method
void onLoginCompleted(LoginObject obj) {
// add your code here
}
sometimes it sucks to create a class for merely passing a bool as a derived EventArgs! so you can simply use Action instead of EventHandler. you can pass any type and how many parameters you like (Action supports Up to 16).
class Raiser
{
public event Action<Raiser, bool,DateTimeOffset> OnCreate;
public void Create()
{
OnCreate?.Invoke(this, true,DateTimeOffset.Now);
}
}
class Listener
{
Raiser raiser;
public Listener()
{
raiser = new Raiser();
raiser.OnCreate += Raiser_OnCreate;
}
private void Raiser_OnCreate(Raiser arg1, bool arg2,DateTimeOffset dateTimeOffset)
{
throw new NotImplementedException();//Do Your works here
}
}
generally using Action and 'Func' are easier than Delegate.

how do I subscribe to an event of a usercontrol

I have a group of usercontrols that I use multiple instances of through out my form.
The usercontrols have contain either a textbox, combobox, or checkbox and a get value method to return the value of it's repective control. Usually I have a button on the form whose clicked event calls the usercontrols getValue function, but now I need for something to happen on the form whenever the usercontrols controls changed event happens. Something like the following.
In form1.cs
form1.Controls.Add(UserControl1);
form1.Controls.Add(UserContorl2);
// gets called every time the combobox on UserControl1 has it's
// ValueChanged event raised
private void UserControl1_Changed(object Sender, EventArgs e)
{
form1.property1 = UserControl1.getValue();
}
// gets called everytime the textbox on UserControl2 has it's
// textChanged event raised
private void UserControl2_Changed(object Sender, EventArgs e)
{
form1.property2 = UserControl2.getValue();
}
I can't figure out how to throw/catch that event in form. I'm using VS 2005.
here is the code in one of my usercontrols. txtValue is a textbox
public partial class StringParameterControl : BaseParameterControl
{
public StringParameterControl(string aName, string aValue)
: base(aName)
{
InitializeComponent();
txtValue.Text = aValue;
}
public StringParameterControl(string aName)
: base(aName)
{
InitializeComponent();
}
public StringParameterControl()
: base()
{
InitializeComponent();
}
public void SetValue(string aValue)
{
txtValue.Text = aValue;
}
public override object GetValue()
{
return txtValue.Text;
}
}
UserControl1.Changed += UserControl1_Changed;
Update your control to include the following:
// A delegate type for hooking up change notifications.
// This is _what kind_ of event you want. It sets the signature your event handler methods must have.
public delegate void ChangedEventHandler(object sender, EventArgs e);
//the actual event
public event ChangedEventHandler Changed;
// Method to raise/fire the Changed event. Call this whenever something changes
protected virtual void OnChanged(EventArgs e)
{
ChangedEventHandler handler = Changed;
if (handler != null) handler(this, e);
}
//and update your existing SetValue() function like so:
public void SetValue(string aValue)
{
txtValue.Text = aValue;
OnChanged(EventArgs.Empty);
}
You can change your event signature to pass any information you want — for example the old or new value of the property (or both). I just used the standard event arguments for the example.
And speaking or properties, don't write separate Get/Set methods in C# like you just did. If you find yourself doing that, you probably want to use a property instead, which will enforce the correct get/set semantics automatically:
public string Value
{
get { return txtValue.Text;}
set {txtValue.Text = value; OnChanged(EventArgs.Emtpy); }
}
As far as I understand the usercontrols you are using do not fire events whenever their value changes, so you can't just subscribe to some "ValueChanged" event.
A possible solution might be to find the control you are interested in (Combobox, Textbox, etc.) in the usercontrols' "Controls" collection and directly subscribe to its appropriate events.
Or you can do with type inference style.
UserControl.Changed = (sender, e) => this.controlFired = true; //or whatever
The Changed is the public event you expose through a property in your control with the type of the delegate (void(object sender, EventArges e)). You can look up how to publish the event on msdn - there is plenty of articles on that.

How to add an event to a UserControl in C#?

I have a UserControl which contains 3 labels. I want to add an event for it, which occurs when the text of one of the labels changed.
I am using Visual Studio 2010
First, you need to declare the event within your class (alongside your methods and constructors):
public event EventHandler LabelsTextChanged;
Then you need to create a method to handle the individual labels' TextChanged events.
private void HandleLabelTextChanged(object sender, EventArgs e)
{
// we'll explain this in a minute
this.OnLabelsTextChanged(EventArgs.Empty);
}
Somewhere, probably in your control's constructor, you need to subscribe to the label's TextChanged events.
myLabel1.TextChanged += this.HandleLabelTextChanged;
myLabel2.TextChanged += this.HandleLabelTextChanged;
myLabel3.TextChanged += this.HandleLabelTextChanged;
Now for the HandleLabelsTextChanged method. We could raise LabelsTextChanged directly; however, the .NET framework design guidelines say that is it a best practice to create an OnEventName protected virtual method to raise the event for us. That way, inheriting classes can "handle" the event by overriding the OnEventName method, which turns out to have a little better performance than subscribing to the event. Even if you think you will never override the OnEventName method, it is a good idea to get in the habit of doing it anyway, as it simplifies the event raising process.
Here's our OnLabelsTextChanged:
protected virtual void OnLabelsTextChanged(EventArgs e)
{
EventHandler handler = this.LabelsTextChanged;
if (handler != null)
{
handler(this, e);
}
}
We have to check for null because an event without subscribers is null. If we attempted to raise a null event, we would get a NullReferenceException. Note that we copy the event's EventHandler to a local variable before checking it for null and raising the event. If we had instead done it like this:
if (this.LabelsTextChanged != null)
{
this.LabelsTextChanged(this, e);
}
We would have a race condition between the nullity check and the event raising. If it just so happened that the subscribers to the event unsubscribed themselves just before we raised the event but after we checked for null, an exception would be thrown. You won't normally encounter this issue, but it is best to get in the habit of writing it the safe way.
Edit: Here is how the public event EventHandler LabelsTextChanged; line should be placed:
namespace YourNamespace
{
class MyUserControl : UserControl
{
// it needs to be here:
public event EventHandler LabelsTextChanged;
...
}
}
Here are the framework design guidelines on event design for further reading.
First you should declare an event in your usercontrol for example:
public event EventHandler TextOfLabelChanged;
then you have to call the call back function that is bound to your event(if there's any) in runtime.You can do this by handling the TextChanged event of a label like this:
public void LabelTextChanged(object sender,EventArgs e)
{
if(TextOfLabelChanged!=null)
TextOfLabelChanged(sender,e);
}
You can have your own EventArgs object if you like.
somewhere in your code you should bound your label TextChanged event to this method like this:
_myLabel.TextChanged+=LabelTextChanged;
public delegate void TextChangedEventHandler(object sender, EventArgs e);
public event TextChangedEventHandler LabelTextChanged;
// ...
protected void MyTextBox_TextChanged(object sender, EventArgs e)
{
if (LabelTextChanged != null) {
LabelTextChanged(this, e);
}
}
compile error, which says: "Expected class, delegate, enum, interface, or struct" on the second line it seems to have a problem with "event...
These 2 lines need to be INSIDE the class declaration.
public delegate void TextChangedEventHandler(object sender, EventArgs e);
public event TextChangedEventHandler LabelTextChanged;
There is a very simple way to do that!
On the UserControl Form :
change properties to public to access everywhere
on the main form , where you are using UserControl:
.5: in the using region add using userControl1=UserControl.userControl1
1.Add 'Laod' event to your UserControl :
this.userControl1.Load += new System.EventHandler(this.userControl1_Load);
2.In the userControl1_Load :
private void userControl1_Load(object sender, EventArgs e)
{
(sender as UserControl1).label1.TextChanged += label1_TextChanged;
//add a 'TextChanged' event to the label1 of UserControl1
OR use direct cast:
((UserControl1) sender).label1.TextChanged += label1_TextChanged;
}
3.In th label1_TextChanged:
private void label1_TextChanged(object sender, EventArgs e)
{
//do whatever you want
}
You must be declaring the event and delegate within the Namespace. Try to bring the code within the class Scope. It will run fine.

c# How to send an array with an event?

I have this array with information
string[] aInfo; // Contains information about this person
I have a clickevent on a menuitem that goes like this:
// Event for adding contact
mContact.Click += new EventHandler(addContactMenuClick);
Then further into the project i have the eventhandler:
// Eventhandler for adding a contact
private void addContactMenuClick(object sender, System.EventArgs e)
{
string sNumber = ((MenuItem)sender).Text;
MessageBox.Show(sNumber);
}
How can i send the array with the event?
All this is in a foreach loop, so the array will never be the same.
Thanks in advance
Define your own class that derives from EventArgs and add a field for your array there.
class MyEventArgs : EventArgs
{
public string[] Info { get; private set; }
public MyEventArgs(string[] info)
{
this.Info = info;
}
}
EDIT (thanks to the comments):
OF course, you cannot change the information that the Click-Event sends, because you can'T change to code that fires the event.
One possible solution would be to derive your own MenuItem and add the information array as a field to this derived class. Then provide a property for this field, so you can access it via the sender parameter of your event. Example:
class MyMenuItem : MenuItem
{
public string[] Info { get; private set; }
public MyMenuItem(string[] strInfo)
{
this.Info = strInfo;
}
}
Access:
protected void mnu_Click(object sender, EventArgs e)
{
MyMenuItem obj = sender as MyMenuItem;
if (obj != null)
{
//Access
}
}
each control has a Tag property that is of the "object" datatype. you could assign your array to this property. Then in the event, cast it back to an array, and use it.
string[] aInfo; // Contains information about this person
....
....
// Event for adding contact
mContact.Click += new EventHandler(addContactMenuClick);
mContact.Tag = aInfo;
// Eventhandler for adding a contact
private void addContactMenuClick(object sender, System.EventArgs e)
{
string sNumber = ((MenuItem)sender).Text;
string[] aInfo = (string[])((MenuItem)sender).Tag;
MessageBox.Show(sNumber);
}
Just put it in a custom Event Args object! Just create an object (ContactMenuClickedEventArgs) or something that inherits from Event Args.
Wrap mContact in another object, and override the 'click' functionality. Have it create the custom EventArgs, and it will get passed in as 'e'!
You can't really add the information to an existing event type. But you can derive your own EventArgs class (ContactAddedEventArgs) that can hold any information you like, and set up a new event (ContactAdded) that accepts this new data.
If you then wish to also Add contacts when you get a click, then just make the click event handler create the user info array (ContactAddedEventArgs) and then raise your ContactAdded event with it.
The key is in the comment to this line:
string[] aInfo; // Contains information about this person
"this person". So aInfo is a member of some class, correct? And when you click your addContact MenuItem you presumably want to use some currently selected object on the screen as your "contact-to-add". So you need to know how to do that part (find the currently selected object on the screen). This object will likely have some relationship to the type containing your aInfo member, and if it doesn't you need to fix that.

Categories

Resources