I'm trying to create a custom control and need to raise an event from it. The idea is to raise an event at the end of the click event (OnAfterClick). I found one or two tutorials on doing this, but am clearly missing a step somewhere; I have the following.
In the control:
public class AfterClickEventArgs : EventArgs
{
...
}
public partial class MyButton : CommandButton
{
public delegate void AfterClickEvnt(object sender, AfterClickEventArgs e);
public event AfterClickUpdatedEvnt AfterClick;
}
protected override void OnClick(EventArgs e)
{
...
Processing here
...
AfterClickEventArgs myArgs = new AfterClickEventArgs();
AfterClick(this, myArgs);
}
In the program using the control:
In InitializeComponent():
this.MyButton.AfterClick += new System.EventHandler(this.cmdMyButton_Click);
This line is giving me a compile error (cmdMyButton_Click does exist). It tells me:
Cannot implicitly convert type 'System.EventHandler' to 'Namespace.MyButton.AfterClick'
Can anyone tell me what I'm missing, or misunderstanding about this, please?
Your event is declared to be of type AfterClickEvnt, but you're trying to subscribe to it using an EventHandler. You can't convert between the two.
Options:
Explicitly state the right type:
this.MyButton.AfterClick += new AfterClickEvnt(this.cmdMyButton_Click);
Use an implicit method group conversion:
this.MyButton.AfterClick += cmdMyButton_Click;
By the way, I suggest you remove your custom delegate type and instead use the generic EventHandler<T> delegate:
public event EventHandler<AfterClickEventArgs> AfterClick;
There is some discrepancies in your code, so it's hard to say what is going on. (myArgs became newArgs, and AfterClickUpdateEvnt became AfterClickEvnt). You also attempt to use an EventHandler in place of your delegate.
Also, it's better to use EventHandler<T> so that you don't have to bother with a delegate:
public event EventHandler<AfterClickEventArgs> AfterClick;
Related
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/
I'm currently developing a tiny technical Framework that is independant of any applications. Business code just refers to this Framework.
According this article : http://msdn.microsoft.com/en-us/library/5z57dxz2.aspx (exemple 2), we need to provide a delegate for the custom event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code and that isn't logical for me, so what is the best way to raise a custom Event with a delegate that is only "internal" and not "public" ?
Thanks for help.
I am not sure if I get it right or not. I think that you feel like if you provide a public Delegate type for your custom event, anyone will be able to Raise that event.
Well, that is not true. Only the class that defines that custom event can raise it. If this is your issue, don't worry.
Not true. It's not allowed to invoke an event outside the class which the event belongs to. Others can only use += and -= operators to your event. Only in the class, you can invoke the event. That is a difference between an event and a normal delegate. That is:
public Data
{
public event EventHandler OnSave
public EventHandler OnLoad;
private void Load()
{
if (OnLoad!=null) OnLoad();
//other operations
}
private void Save()
{
if (OnSave!=null) OnSave();
//other operations
}
}
And outside the class:
Data data = new Data();
data.OnLoad += (s,e) => {};
data.OnSave += (s,e) => {};
data.OnLoad = (s,e)=>{};
//data.OnSave = (s,e)=>{}; //invalid
data.OnLoad();
//data.OnSave(); //invalid
The delegate is just a type declaration describing the "signature" of your event. This has to be public. To actually invoke your event you often implement a method named OnEvent (where you substitute Event with Click or Closed or whatever describes your event). This method should be private (or protected) in your class.
You cannot declare an event using a delegate that is less "visible" than the event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code
That isn't true. You declare an event as follows:
public event FooEventHandler Foo;
The only thing that external code can do with the event is subscribe to it (+=), or unsubscribe from it (-=). It can't access the actual delegate, which is generated by the compiler as a private field. In other words, this code would be invalid :
SomeClass x = new SomeClass();
x.Foo(x, new FooEventArgs()); // compilation error here
Don't forget that an event is actually a pair of methods (add and remove). The compiler rewrites the event declaration to something along those lines:
private FooEventHandler _foo;
public event FooEventHandler Foo
{
add { _foo += value; }
remove { _foo -= value; }
}
(the generated code is actually a bit more complex, with some locking to ensure thread safety)
As you can see, the _foo field is private, so client code can't access it. Only the event's add and remove accessors are accessible.
One way of doing it:
Instead of public event, create a method that will manually subscribe your desired delegates, and store them in `private List _delegates' field.
Then, from the 'inside', call each of them when you desire.
public class Framework
{
public delegate void Method();
public void AttachEvent(Method M)
{
_methods.Add(M);
}
private List<Method> _methods;
private FireMethods()
{
_methods.Foreach(x=>x.Invoke());
}
}
Or, you can embrace 'by design' feature of the events that they aren't publicly Invoke()-able.
:)
I've read that one benefit to the MyEventHandler/MyEventArgs model is that it allows standard event handlers to handle a variety of events. It sounds good, but perhaps I'm understanding how this is supposed to work. I have the following code:
public delegate void DataArrivalEventHandler
(object sender, DataArrivalEventArgs e);
public class DataArrivalEventArgs : EventArgs
{
public DateTime Arrival { get; protected set; }
public DataArrivalEventArgs()
{
Arrival = DateTime.Now;
}
public DataArrivalEventArgs(DateTime arrival)
{
Arrival = arrival;
}
}
...
_pipeReader.DataArrival += new EventHandler(Pipe_DataArrival);
...
private void Pipe_DataArrival(object sender, EventArgs e)
{
...
}
The code throws an error when I'm trying to add the event handler, however, saying that it cannot implicity cast DataArrivalEventHandler to EventHandler. Changing DataArrivalEventHandler(Pipe_DataArrival) to EventHandler(Pipe_DataArrival) fixes the problem, so I feel like you should be able to add generic event handlers to more specific events (I understand why you can't do it the other way around.)
Is how I have it the best way to do it, or is there a better convention?
The way you've got it is right.
There's no conversion from a more specific delegate type to a more general one - but there is a conversion from a method group with a more specific set of parameters to a delegate type with a more general set of parameters. (It works the other way for return types.)
Now C# 4 changes this slightly, as there is a conversion from (say) Action<string> to Action<object> - but this only happens for generic delegates, in terms of their type parameters.
I think delegate contravariance (assigning EventHandler delegate to DataArrivalEventHandler) does not work in .NET 1.1. It is available since .NET 2.0
(Your code looks like .NET 1.1 code)
Naming
There are a naming convention for events. In your case the event should be named DataArrived. Read more here: http://msdn.microsoft.com/en-us/library/h0eyck3s(v=VS.71).aspx
Delegates
You got two delegates for events that should be used: EventHandler and EventHandler<T>
Multihtreading
If you are using multithreading, init your event in the following way.
public event EventHandler<MyEventArgs> = delegate {};
In this way you don't have to worry about multithreading issues.
Assuming we declare a class DerivedEventArgs:
public class DerivedEventArgs : EventArgs { ... }
then EventHandler delegate is able to accept methods with the following signature:
public static void Some_Method(object o, DerivedEventArgs e) { ... }
But if we try to subscribe a method with the above signature to the event implementing EventHandler delegate:
public event EventHandler MyEvent;
, then we get an error. Why is that?
thanx
Suppose the code raising the event specified a value which wasn't a DerivedEventArgs - what would you expect it to do? Basically you'd lose type safety.
EDIT Note that you can do it the other way round - you can subscribe to an event with a more specific parameter type using a method with a less specific parameter type - because the event is still guaranteeing that it will call the handler with something compatible. Here's an example:
using System;
class Test
{
public class DerivedEventArgs : EventArgs { }
public EventHandler<DerivedEventArgs> SpecialistEvent;
static void Main()
{
Test t = new Test();
t.SpecialistEvent += GeneralHandler;
}
static void GeneralHandler(object sender, EventArgs e)
{
}
}
MyEvent(this, EventArgs.Empty)
would try to pass a regular EventArgs to DerivedEventArgs in
Some_Method(object o, DerivedEventArgs e)
and then if Some_Method tried to do an
e.PropertyInDerivedClass
it'd fail.
try using an EventHandler<T> for your event, where T is your DerivedEventArgs class.
From MSDN EventHandler documentation...
EventHandler is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must supply your own custom event data type and either create a delegate where the type of the second parameter is your custom type, or use the generic EventHandler< TEventArgs > delegate class and substitute your custom type for the generic type parameter.
The EventHandler Delegate was not made to do what you're trying to do. It's a simple convenience delegate for use in cases where event data is unimportant. The event that utilizes this delegate type may send a EventArgs with the least derived event argument type possible. That means your delegate should be ready to receive anything.
But in your example, you're specifying a more derived event argument type in your handler. Hence, if allowed by the compiler, your code can actually receive another EventArgs type that's not what you expect but defined as your derived type; breaking type safety as mentioned earlier.
If I have ClassA that has a public event, SomeEvent, and ClassC that has method, addListener, that accepts an EventHandler reference, why can't ClassB have a line that says c.addListener(ref a.SomeEvent)? If I try I get a compiler error that says: "The event 'ClassA.SomeEvent' can only appear on the left hand side of += or -= (except when used from within the type 'ClassA').
Why does this restriction exist? And how can I get around it while staying reasonably close to my structure?
I'm a C# newbie; any help would be appreciated. Thanks!
class ClassA {
public event EventHandler SomeEvent;
}
ClassB{
public ClassB() {
ClassA a = new ClassA();
ClassC c = new ClassC();
c.addListener(ref a.SomeEvent); //Compile error
}
}
class ClassC {
public void addListener(ref EventHandler handler) {
handler += onEvent;
}
private void onEvent(object sender, EventArgs e) {
//do stuff
}
}
Outside of the class, you only have access to the add and remove accessors - that is the point of an event you can neither see other subscribers, nor change them (for example, setting the event to null). It would be better to handle the event normally, and cause whatever consequences you need.
Imagine you could do what you suggest. For example, suppose you subscribe to a button click, and some other code uses that info to hook you into a "tick" event - you're code isn't going to work as it expected to = bug.
To make that explict; an event isn't an EventHandler, in the same way that a property isn't an int - the event/property defines accessor methods.
Re your scenario, either make OnEvent public and use a.SomeEvent += c.OnEvent;, or have some similar method and use an anon-method:
a.SomeEvent += delegate { c.DoSomethingCool(); };
The event keyword creates an accessor for a private delegate object. The exact same thing a property does, it restricts access to a private field. Your code snippet fails with a similar kind of error when you use a property instead of an event:
class ClassA {
public int Property { get; set; }
}
class ClassB {
public ClassB() {
ClassA a = new ClassA();
ClassC c = new ClassC();
c.setValue(ref a.Property); // CS0206
}
}
class ClassC {
public void setValue(ref int value) {
value = 42;
}
}
It is easier to see now, there is no way for the compiler to ensure that the setValue() method uses the property setter. Nor could it know that the "value" argument is a property with a setter or a plain field.
It is less clear for an event because there is so much syntax sugar at work. This declaration
public event EventHandler SomeEvent;
actually generates this code:
private EventHandler _SomeEvent;
public event SomeEvent {
add { _SomeEvent += new EventHandler(value); }
remove { _SomeEvent -= new EventHandler(value); }
}
The add and remove accessors are equivalent to the get and set accessors of a property, they prevent code from messing with the private _SomeEvent field. By convention, the add accessor is invoked when you use +=, remove is invoked with -=. Compare this with the earlier example I gave for a property. Same problem, you can't use the ref keyword and ClassC.addListener() would have no way to know that the handler is actually an event instead of a delegate object. If the compiler would pass _SomeEvent instead, the point of using the accessors is lost.
You can restructure the code to solve this problem:
class ClassC {
public EventHandler getListener() {
return new EventHandler(onEvent);
}
private void onEvent(object sender, EventArgs e) { }
}
...
a.SomeEvent += c.getListener();
One final note: the symmetry between an event and a property is a bit lost, the C# compiler automatically generates the add/remove accessors if you don't write them explicitly. It doesn't do this for a property. It would have made automatic properties a lot easier:
property int Property;
But that would have required adding a new keyword to the language, something the C# team really dislikes. Other languages like VB.NET and C++/CLI do have that keyword.
How can I get around it while staying reasonably close to my structure?
Use a.SomeEvent += handler instead.
Why does this restriction exist?
See Marc Gravell's answer.