I use the article here to implement the weak referenced handler pattern.
The code is really perfect before I found that the event handler in WPF is not the instance of EventHandler. Such as PropertyChangedEventHandler:
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
Although I think they have the same signature as EventHandler<PropertyChangedEventArgs>, where EventHandler is :
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
I try to convert it directly but the C# said "I cannot do it".
So my question is it possible to smoothly convert these two kind of delegate?
What's the different between them?
I have some delegates with type parameters, and I convert them like this:
public void AddHandler(EventHandlerUntyped<TKey> EventHandlerMethod)
{
SomeEventTakingTypedEventHandler += new TypedEventHandler<TKey, TValue>(EventHandlerMethod);
}
These are the delegates:
public delegate void TypedEventHandler<TKey, TValue>(object sender, TypedEventArgs<TKey, TValue> ItemInfo);
public delegate void EventHandlerUntyped<TKey>(object sender, EventArgsUntyped<TKey> ItemInfo);
PS: The Typed Event Args inherits the untyped event args.
I know it's not exactly what you want, but I think it's a good hint.
You can ignore the TKey and try something like:
new EventHandler<PropertyChangedEventArgs>(SomeMethodUsingPropertyChangedEventHandlerSignature).
new PropertyChangedEventHandler(SomeMethodUsingCommonEventHandlerSignature);
Related
I've been looking for an answer for about an hour on Google but I did not found exactly what I'm looking for.
Basically, I have a static Helper class that helps perform many things I do frequently in my App. In this case, I have a method named "CreateDataContextMenu" that creates a context menu on a given TreeView control.
public static void CreateDataContextMenu(Form parent, TreeView owner, string dataType)
{ ... }
TreeView owner is the control in which I will associate my context menu.
Then later on I add a Click event to a MenuItem like this:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click);
The problem I have here is that I want to pass "owner" and "dataType" as arguments to the menuItemFolder_Click event.
I tried the following:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
(...)
private static void menuItemFolder_Click(object sender, System.EventArgs e, Treeview owner, string dataType)
{...}
But it doesn't work at all. It might be very naive of me to do it that way but I"m not very comfortable with event handler yet.
Any idea on how I could do that?
My first guess is that I need to create my own EventHandler for this specific case. Am I going in the right direction with that?
You should create a lambda expression that calls a method with the extra parameters:
menuItemFolder.Click += (sender, e) => YourMethod(owner, dataType);
Honest admission up front: I have not tried the code below.
I think the reason
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
won't work is because you are actually passing to System.EventHandler () the result of the invocation of menuItemFolder_Click () with the parameters provided. You are not passing a pointer to the function itself.
Try to write another function that implements the details of menuItemFolder_Click (). See if something like
private void menuItemFolder_Click_Helper (object sender, EventArgs e, object Owner, object DataType) {
// implement details here
}
and then call the function from within menuItemFolder_Click ().
I think the simplest code would be this:
EventHandler myEvent = (sender, e) => MyMethod(myParameter);//my delegate
myButton.Click += myEvent;//suscribe
private void MyMethod(MyParameterType myParameter)
{
//Do something
//if only one time
myButton.Click -= myEvent;//unsuscribe
}
Passing custom args into an event handler is not too difficult. Below is a clean and easily reusable method of doing so. Check it:
public class MyClass
{
public CustomArgsEventHandler MyEvent1;
public MyClass(){MyEvent1+=observer;}
public void observer(object sender, CustomEventArgs e){print(e.myArg);}
//...
}
//place in the same file if you like!
public class CustomEventArgs : EventArgs
{
public float myArg {get;set;}
public CustomEventArgs (float d) { myArg = d; }
}
public delegate void CustomArgsEventHandler (object sender, CustomEventArgs e);
I know that the purpose of event keyword just to used in pair with += operator to make list of delegate. And when constructing delegate we can make any signature (of parameter) for method that compatible with that delegate.
For example I create delegate
public delegate void StartEventHandler(object sender, StartEventArgs e);
with two parameter: the first with the type object and the second with the type StartEventArgs. But in many article that I found on the internet, the second parameter for that delegate must inherited EventArgs type. Why we do this instead to make the second parameter come/inherited from arbitrary type?
You don't need to and I'm sure the code will still compile if you used an arbitrary base class but it is a convention.
Conventions are good because it makes it easier to understand for people who are not familiar with your code already. If I subscribe to an event in C# I expect a certain method signature.
It is also good because it makes different types of events interchangeable.
For example, say you have three delegates
public delegate void AEventHandler(object sender, AEventArgs e);
public delegate void BEventHandler(object sender, BEventArgs e);
public delegate void CEventHandler(object sender, CEventArgs e);
You could write a generic method that conforms to all three delegates because all the args objects inherit from the same base class.
public void eventMethod(object sender, EventArgs e) {
// Any one of the events fired
}
public void subscribeToEvents() {
eventA += new AEventHandler(this.eventMethod);
eventB += new BEventHandler(this.eventMethod);
eventC += new CEventHandler(this.eventMethod);
}
And even cast if you know what types to expect
public void eventMethod(object sender, EventArgs e) {
// Any one of the events fired
if (e is BEventArgs) {
// Event type B fired
var eventB = e as BEventArgs;
eventB.doSomething()
}
}
One thing that annoys me is that in a default Event, Sender is of type object and therefore almost always requires a manual cast before we can use it. Luckily since VB now also supports variance in delegates, we can update the signature of an event in such a way that the sender is strongly typed, see: Event parameter; "sender as Object", or "sender as T"?
Unfortunatly this doesn't work for existing declared events which senders are of type object.
Now one solution would be ofcourse to generate a fake EventHandler which internally takes care of the cast for you. I made a quick example, see:
struct EventHandler<TSender, TEventArgs>
where TEventArgs: EventArgs
{
private readonly Action<TSender, TEventArgs> _delegate;
public EventHandler(Action<TSender, TEventArgs> #delegate)
{
if (#delegate == null)
throw new ArgumentNullException("#delegate");
_delegate = #delegate;
}
public static implicit operator EventHandler<TEventArgs>(EventHandler<TSender, TEventArgs> eventHandler)
{
return new EventHandler<TEventArgs>(eventHandler.Execute);
}
private void Execute(object sender, EventArgs e)
{
TSender typedSender = (TSender)sender;
TEventArgs typedEventArgs = (TEventArgs)e;
_delegate(typedSender, typedEventArgs);
}
}
which can be used as you would expect it to be used:
class Program
{
event EventHandler<EventArgs> Test;
static void Main(string[] args)
{
new Program().Main();
}
void Main()
{
Test += new EventHandler<Program, EventArgs>(TestEventHandler);
Test(this, EventArgs.Empty);
}
void TestEventHandler(Program sender, EventArgs e)
{
throw new NotImplementedException();
}
}
Now if I really want to use this, there is alot of work to be done. (The struct should behave just like the original delegate). Yet i do have the feeling that there is either already a great implementation out there, or there is no implementation since there are some major drawbacks i overlooked.
Who can answer me the above question? Any other hints?
I can't think of any solution that would have less code than
var original = (OriginalType)sender;
Also if class is yours nothing stops you from creating your own delegate instead of EventHandler delegate
delegate void EventHandler<in TSender, in TArgs>(TSender sender, TArgs args);
This is contravariant in args and sender
People usually also have the reference of the object for which the event is raised and therefore they rarely need to get the thing out of the sender. Could it be possible in your case?
Edit: Since you mentioned you're dealing with events that are not written by you so modifying the delegate is out of question and you probably don't have reference to the object so you have to resort to the sender in that case. Now in your solution you can not unsubscribe to the event. If you modify it to support that then you'll have to keep the reference to this struct of yours which is more work than simple casting. I think casting is still the cleanest solution.
I'm of the opinion that using methods as event handlers is now bad OOP practice. Now that we have anonymous delegates we just don't need to make methods just to handle events. Creating an event handler method that could get called by every other method in a class is much like making class members public and letting any class call anything.
In the past we have had to write this:
public class Program
{
public void Main()
{
var button = new Button();
button.Click += button_Click;
}
void button_Click(object sender, EventArgs e)
{
var button = (Button)sender; //Need to cast here
}
}
But now we can write this:
public class Program
{
public void Main()
{
var button = new Button();
button.Click += (sender, e) =>
{
//Can use `button` here
//Just ignore `sender`
};
}
}
Using anonymous delegates allows us to use the event "sender" reference directly without the need of any casting.
Much cleaner object oriented coding.
I've been looking for an answer for about an hour on Google but I did not found exactly what I'm looking for.
Basically, I have a static Helper class that helps perform many things I do frequently in my App. In this case, I have a method named "CreateDataContextMenu" that creates a context menu on a given TreeView control.
public static void CreateDataContextMenu(Form parent, TreeView owner, string dataType)
{ ... }
TreeView owner is the control in which I will associate my context menu.
Then later on I add a Click event to a MenuItem like this:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click);
The problem I have here is that I want to pass "owner" and "dataType" as arguments to the menuItemFolder_Click event.
I tried the following:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
(...)
private static void menuItemFolder_Click(object sender, System.EventArgs e, Treeview owner, string dataType)
{...}
But it doesn't work at all. It might be very naive of me to do it that way but I"m not very comfortable with event handler yet.
Any idea on how I could do that?
My first guess is that I need to create my own EventHandler for this specific case. Am I going in the right direction with that?
You should create a lambda expression that calls a method with the extra parameters:
menuItemFolder.Click += (sender, e) => YourMethod(owner, dataType);
Honest admission up front: I have not tried the code below.
I think the reason
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
won't work is because you are actually passing to System.EventHandler () the result of the invocation of menuItemFolder_Click () with the parameters provided. You are not passing a pointer to the function itself.
Try to write another function that implements the details of menuItemFolder_Click (). See if something like
private void menuItemFolder_Click_Helper (object sender, EventArgs e, object Owner, object DataType) {
// implement details here
}
and then call the function from within menuItemFolder_Click ().
I think the simplest code would be this:
EventHandler myEvent = (sender, e) => MyMethod(myParameter);//my delegate
myButton.Click += myEvent;//suscribe
private void MyMethod(MyParameterType myParameter)
{
//Do something
//if only one time
myButton.Click -= myEvent;//unsuscribe
}
Passing custom args into an event handler is not too difficult. Below is a clean and easily reusable method of doing so. Check it:
public class MyClass
{
public CustomArgsEventHandler MyEvent1;
public MyClass(){MyEvent1+=observer;}
public void observer(object sender, CustomEventArgs e){print(e.myArg);}
//...
}
//place in the same file if you like!
public class CustomEventArgs : EventArgs
{
public float myArg {get;set;}
public CustomEventArgs (float d) { myArg = d; }
}
public delegate void CustomArgsEventHandler (object sender, CustomEventArgs e);
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.