basically:
public delegate void RecvCommandHandler (ChatApplication sender, byte[] content);
event RecvCommandHandler[] commands = new RecvCommandHandler[255];
I want to activate a different method/function for each command number, but I am really uncertain of the syntax. How am I supposed to do it?
I think I'll go with just an array of delegates for this one, but the question is still interesting.
You could create an array of a class with operator overloading to simulate the behavior you are interested in...
public delegate void EventDelegate(EventData kEvent);
public class EventElement
{
protected event EventDelegate eventdelegate;
public void Dispatch(EventData kEvent)
{
if (eventdelegate != null)
{
eventdelegate(kEvent);
}
}
public static EventElement operator +(EventElement kElement, EventDelegate kDelegate)
{
kElement.eventdelegate += kDelegate;
return kElement;
}
public static EventElement operator -(EventElement kElement, EventDelegate kDelegate)
{
kElement.eventdelegate -= kDelegate;
return kElement;
}
}
public EventElement[] commands = new EventElement[255];
commands[100] += OnWhatever;
commands[100].Dispatch(new EventData());
commands[100] -= OnWhatever;
There's really no concept of an array of events - it's like talking about an array of properties. Events are really just methods which let you subscribe and unsubscribe handlers. If you need to be able to do this by index, I suggest you just have a pair of methods. (AddCommandHandler(int, RecvCommandHandler) and RemoveCommandHandler(int, RecvCommandHandler)). That won't support the normal event handling syntactic sugar, of course, but I don't see that there's a lot of alternative.
The other option is to specify and index in the delegate prototype and have one event handler that "delegates" to the others, e.g.:
public delegate void RecvCommandHandler (int id, ChatApplication sender, byte[] content);
// ...
private RecvCommandHandler[] internalhandlers;
public void MyCommandHandler(int id, ChatApplication sender, byte[] content)
{
internalHandlers[id](id, sender, content);
}
I was just looking for the same answer, however my class is also event sender for WPF, so it should look as much as normal C#/WPF event sender class. So I simply added this:
To sender:
enum with properties name -- this is lame workaround for lack of nameof
one additional method to record requests
To receiver:
request event for given enum
The code, sender:
public enum Properties
{
NetworkFileName,
DatasetFileName,
LearningWatch
}
private string network_filename;
public string NetworkFileName
{
get { return network_filename; }
private set
{
if (network_filename != value)
{
network_filename = value;
OnPropertyChanged(Properties.NetworkFileName.ToString());
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public void OnChange(Properties prop, Action<object, PropertyChangedEventArgs> action)
{
PropertyChanged += new PropertyChangedEventHandler((obj, args) => { if (args.PropertyName == prop.ToString()) action(obj, args); });
}
And to the receiver:
private void OnNetworkLoaded(object sender, PropertyChangedEventArgs e)
{
SetTitle();
}
...
ExpManager.OnChange(ExperimentManager.Properties.DatasetFileName, OnDatasetLoaded);
It is still ugly, but at least:
I don't have to deal with "ifs" in receiver
I can easily create multiple event handlers
it is compatible with WPF
no magic strings (I hate those)
Disadvantage:
obsfuscation ruins this (but I have special class for that case, this project is just for me, so no problem here)
Related
I'm building a MVVM application in which a ToBeListened class has a couple of properties, PropertyA and PropertyB, and I want to listen to them.
public class ToBeListened : INotifyPropertyChanged
{
private double _propertyA;
private string _propertyB;
/*Here I'm omitting the update part where NotifyPropertyChanged gets called*/
public double PropertyA{get; set; }
public double PropertyB{get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Those two properties are listened by a Listener class, so I've implemented an EventHandler in it, that listens to a ToBeListened object.
public class Listener
{
private ToBeListened toBeListenedObject;
public Listener()
{
toBeListenedObject = new ToBeListened();
toBeListenedObject.PropertyChanged += newPropertyChangedEventHandler(PropertyListener_PropertyChanged);
}
private void PropertyListener_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "PropertyA":
{
/*...DO SOMETHING...*/
}
case "PropertyB":
{
/*...Do something else...*/
}
}
The thing is, I don't really like this solution I've found. A switch-case isn't polymorphism-friendly, so
is there a better way to do this? Maybe something that uses overloading? (Like private void PropertyListener_PropertyChanged(double sender, PropertyChangedEventArgs e)
most of all, is it right to code a ViewModel like this?
I like Josh Smith's PropertyObserver, which you can get at http://mvvmfoundation.codeplex.com/ (some documentation from Josh at https://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/). It's a nice class that encapsulates the plumbing logic you're talking about, so you can focus on just handling changes to certain properties. So in your case, you could write code like:
var observer = new PropertyObserver<ToBeListened>(toBeListenedObject)
.RegisterHandler(tbl => tbl.PropertyA, tbl => HandlePropertyA(tbl))
.RegisterHandler(tbl => tbl.PropertyB, tbl => HandlePropertyB(tbl));
You can start using it by installing the MVVM Foundation nuget package into your solution. The ID is MvvmFoundation.Wpf.
In the past I've used a little class derived from Dictionary<string, Action> for this purpose. It was something like this:
public class PropertyChangedHandler : Dictionary<string, Action>
{
public PropertyChangedHandler(INotifyPropertyChanged source)
{
source.PropertyChanged += Source_PropertyChanged;
}
private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Action toDo;
if (TryGetValue(e.PropertyName, out toDo))
{
toDo();
}
}
}
Then your listener looks like this:
public class Listener
{
private ToBeListened toBeListenedObject = new ToBeListened();
PropertyChangedHandler handler;
public Listener()
{
handler = new PropertyChangedHandler(toBeListenedObject)
{
{ "PropertyA", DoA },
{ "PropertyB", DoB }
};
}
private void DoB()
{
}
private void DoA()
{
}
}
This is just an example to give you an idea - it can be expanded for more complex purposes, of course.
i think MVVM Light framework (or library?) has what you need. Take a look at their ObservableObject class http://www.mvvmlight.net/help/SL5/html/d457231f-6af7-601d-fa1f-1fe7c9f60c57.htm
Basically what it does, is making your object observable.
I've got quite simple problem but gives me a headache and I'm wasting too much time. I've ran out of ideas anyway:)
For some reason I can't pass the value of my property variable to my event handler. Here is what I've got , an for me everything is fine but it won't work:(
Any idea why it's not passing the actual value of the variable? Thanks in advance:)
Just as the name suggests, propertyName should contain the property's name, not value. Also see PropertyChangedEventHandler and PropertyChangedEventArgs on MSDN for more details.
As to why your event handler null, I suspect you haven't subscribed to it. You should have something like the following somewhere in your program:
obj.PropertyChanged += new PropertyChangedEventHandler(obj_PropertyChanged);
private void obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
....
}
then when obj.Moves changes obj_PropertyChanged will be called.
I understand your confusion, so let me give a little more explanations. Suppose you have two classes A and B, and you want B to be notified when a property of A is changed. Then you can make A implement INotifyPropertyChanged, and make B subscribe to the PropertyChanged event of A, like the following:
public class A: INotifyPropertyChanged
{
private int moves;
public int Moves
{
get { return moves; }
set
{
if (value != moves) {
moves = value;
OnPropertyChanged("Moves");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class B
{
private A a = new A();
public B()
{
a.PropertyChanged += new PropertyChangedEventHandler(a_PropertyChanged);
}
private void a_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
....
}
}
With the above, B.a_PropertyChanged will be called whenever A.Moves is changed. More precisely, if b is an instance of B, then b.a_PropertyChanged will be called whenever b.a.Moves is changed. Also please note how my implementation of the setter of Moves differs from yours.
I have an extension method to subscribe a PropertyChanged event of an object that implements INotifyPropertyChanged.
I would like that the event fires just once. Not more.
This is my method.
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
PropertyChangedEventHandler handler = (obj, e) =>
{
if (propertyName == e.PropertyName)
{
action();
}
};
target.PropertyChanged -= handler;
target.PropertyChanged += handler;
}
But it does not work. I cannnot remove the event handler so the event fires every time I call this method.
I have try a different approach. Instead of using annonymous methods, something more traditional, like this:
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
target.PropertyChanged -= target_PropertyChanged;
target.PropertyChanged += target_PropertyChanged;
}
static void target_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//do stuff here
}
And it just works fine. The event fires just once, but I also need the Action parameter. I cannot use it with this approach.
Any workaround or different aproach to solve this issue?Is there something strange with anonymous methods inside static methods?
Thanks in advance.
That is a limitation of using anonymous methods as event handlers. They cannot be removed as you would a normal method (which is technically a delegate instance automatically create via a method group conversion) because anonymous methods get compiled into a compiler-generated container class and a new instance of the class is created each time.
In order to preserve the action parameter you could create a container class which would have the delegate for your event handler inside. The class can be declared private inside the of the other class you're working with - or made internal, maybe in a "Helpers" namespace. It would look something like this:
class DelegateContainer
{
public DelegateContainer(Action theAction, string propName)
{
TheAction = theAction;
PopertyName = propName;
}
public Action TheAction { get; private set; }
public string PropertyName { get; private set; }
public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
if(PropertyName == e.PropertyName)
TheAction();
}
}
Then, create and store the reference to the container in your class. You might create a static member currentContainer and then set the handler like this:
private static DelegateContainer currentContainer;
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
if(currentContainer != null)
target.PropertyChanged -= currentContainer.PropertyChangedHandler;
currentContainer = new DelegateContainer(action, propertyName);
target.PropertyChanged += currentContainer.PropertyChangedHandler;
}
You can get your first example to work if you unsubscribe from within the event handler itself.
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
// Declare the handler first, in order to create
// a concrete reference that you can use from within
// the delegate
PropertyChangedEventHandler handler = null;
handler = (obj, e) =>
{
if (propertyName == e.PropertyName)
{
obj.PropertyChanged -= handler; //un-register yourself
action();
}
};
target.PropertyChanged += handler;
}
The above code serves as a "one and done" event handler. You can register an unlimited number of these, and each one will only be executed once before unregistering itself.
Keep in mind that it's possible to have one of these handlers execute multiple times, if you raise the event across multiple threads in short succession. To prevent this, you might need to create a static Dictionary(T,T) mapping object instances to "lock objects," and add some sentry code to ensure that a handler is only executed once. Those implementation specifics seem to be a bit outside the scope of your question as currently written, however.
Technically, it's not the same anonymous method you are trying to unsubscribe. .NET creates new instance of that method every time your OnPropertyChanged called. That's why unsubscription will not work.
I have a set of events that have the same signature . now I wonder if I can create a generic event handler raising method to do this for all of the events ?
is this possible to send an event as <T> ?
If this is all within a single class, you can make a method to raise the event which works with any of them. For example, if your events all were EventHandler<T>, you could use:
private void RaiseEvent<T>(EventHandler<T> eventHandler, T eventArgs)
{
if (eventHandler != null)
{
eventHandler(this, eventArgs);
}
}
You could then call this via:
this.RaiseEvent(this.MyEvent, new MyEventArgs("Foo"));
For a static version of Reed Copsey's reply, I created a static class Event:
public static class Event
{
public static bool Raise<T>(Object source, EventHandler<T> eventHandler, T eventArgs) where T : EventArgs
{
EventHandler<T> handler = eventHandler;
if (handler != null)
{
handler(source, eventArgs);
return true;
}
return false;
}
}
This also assumes your event handlers are of the type EventHandler<T>. The return type was changed from void to bool and returns whether there were any listeners of the event. Rarely used, so feel free to change back to void.
Example usage:
public event EventHandler<FooArgs> FooHappend;
public void Foo()
{
Event.Raise(this, FooHappend, new FooArgs("Hello World!");
}
See this. It describes what you want.
You can create a typed event by using a typed delegate and using that for your event:
public delegate void myDel<T>(T stuff);
public event myDel<int> myEvent;
public doStuff()
{
myDel(1);
}
I am working a problem which is about delegate and event. I am a newbid in this aspect. I don't know how to call the event.
Would some tell me?
Thanks in advance.
Here is simple example to call event....
// event_keyword.cs
using System;
public delegate void MyDelegate(); // delegate declaration
public interface I
{
event MyDelegate MyEvent;
void FireAway();
}
public class MyClass: I
{
public event MyDelegate MyEvent;
public void FireAway()
{
if (MyEvent != null)
MyEvent();
}
}
public class MainClass
{
static private void f()
{
Console.WriteLine("This is called when the event fires.");
}
static public void Main ()
{
I i = new MyClass();
i.MyEvent += new MyDelegate(f);
i.FireAway();
}
}
There is Link which may helpful.
The event can be invoked in the class in which it is declared. First you'll usually want to check if your event is null.
if (MyEvent != null) MyEvent(this, new EventArgs());
The arguments you pass to the event will depend on the declaration of the event. To give you a little more background, an event is just a compiler trick. When an event such as
public event ChangedEventHandler Changed;
is compiled it will look like
protected ChangedEventHandler _change;
public ChangedEventHandler Change
{
add { _change += value; }
remove { _change -= value; }
}
so anything inside where it is declared will use _change, while anything outside will use Change. In other words, inside where it is declared, it is just a delegate, and all the normal rules apply.
To resuse the event you just need to attach event with the you control for example .
buttonone.Click+= event1;
buttonTwo.Click+= event1;
Fore more details have look : C# Event Implementation Fundamentals, Best Practices and Conventions
Once you have defined the delegate, you need to define when to call the event. I mean you can call the event at assignment of any value to the specific variable.
here is the example of defining the delegate with the same variable class.
public class callbackdel : EventArgs
{
public readonly string resp = null;
public callbackdel(string s)
{
resp = s;
}
}
public delegate void WorkerEndHandler(object o, callbackdel e);
Now in the control you are using, you need to add this method.
public void OnWorkEnd(object o, callbackdel e)
{
WorkEnd(o, e);
}
after creating method and defining the delegate, you can fire the event from any of the delegate simply by calling the method.
OnWorkEnd((object)this, e);
When using an Event you first have to declare it:
// Create some custom arguments for the event
public class SampleEventArgs
{
public SampleEventArgs(string s)
{
Text = s;
}
public String Text {get; private set;}
}
// Define a class that uses the event
public class EventPublisher
{
// Declare the delegate
public delegate void SampleEventHandler(object sender, SampleEventArgs e);
// Declare the event.
public event SampleEventHandler SampleEvent;
// Wrap the event in a protected virtual method
// to enable derived classes to raise the event.
protected virtual void RaiseSampleEvent()
{
// Raise the event by using the () operator.
if (SampleEvent != null)
SampleEvent(this, new SampleEventArgs("Hello"));
}
}
You can then subscribe to the event:
EventPublisher publisher = new EventPublisher();
publisher.SampleEvent += new EventPublisher.SampleEventHandler(SampleEventHandler);
public void SampleEventHandler(object sender, SampleEventArgs args)
{
}
Your event handler will be called when EventPublisher executes RaiseSampleEvent()