I try to return an object in my event:
public class MyEvent : EventArgs
{
public Channels number = new Channels(); // Channels is a class where i declared only variables( i try to return all variables inside this class)
public MyEvent(Channels numero)
{
return numero;
}
}
This code doesn't work and i don't know how to return an object which contains my variables of Channels.
Change that to:
public class MyEvent : EventArgs
{
public Channels Number {get;}
public MyEvent(Channels numero)
{
Number = numero;
//return numero; You cannot use "return" in a CTOR!
}
}
Then you can use it in an EventHandler like this:
void MyEventHandler( object sender, MyEvent e )
{
// sender => object that raised the event
// e => an instance of `MyEvent`, having a property, we can read.
var channels = e.Number; // use the info
}
Of course you would have registered it, before it will be triggered:
someInstanceProvidingTheEvent.MyEventHappened += MyEventHandler;
Raising the event works something like this:
// assume we are in the class that offers the Event
public event EventHandler<MyEvent> MyEventHappened;
protected virtual void OnMyEventHappened( Channels chans )
{
// You may want to add some error fortification, here
MyEventHappened?.Invoke(this, new MyEvent(chans));
}
// raise it
public void SomeMethod(){
var theChannels = new Channels();
// yadda yadda
// now it happens!
OnMyEventHappened(theChannels);
}
public class MyEvent : EventArgs
{
public Channels _channels { get; set; }
public MyEvent(Channels numero)
{
_channels = numero;
}
}
public class Program
{
public Main()
{
Channels myChannels = new Channels();
MyEvent _myEvent = new MyEvent(myChannels);
var youWant = _myEvent._channels;
}
}
Related
I have a .json file with so-called commands:
"Commands":[{
"EventName": "MouseLeftButtonUp",
"MethodToExecute": "NextJson",
"Args": "Next.json"
},{
"EventName": "MouseRightButtonUp",
"MethodToExecute": "CloseApp"
}
I deserialize this json to this class:
public class Command
{
[JsonPropertyName("EventName")]
public string EventName { get; set; }
[JsonPropertyName("MethodToExecute")]
public string MethodToExecute { get; set; }
[JsonPropertyName("Args")]
public string Args { get; set; }
/*Methods*/
}
EventName is a name of UIElement class events.
MethodToExecute is a name of method to call, when event triggered.
Args are the args, passed to the MethodToExecute.
I don't want my users to be able to call any method in the application, so I don't use reflection to get MethodInfo, instead I create Dictionary: Dictionary<string, Delegate> MethodsDictionary. The key in this dictionary is the name of method (MethodToExecute from Command class), and the value is something like this:
MethodsDictionary.Add(nameof(CloseApp), new Action(CloseApp));
MethodsDictionary.Add(nameof(NextJson), new Action<string>(NextJson));
Without using reflection, I'd added the event handler like this:
button.MouseLeftButtonUp += (sender, args) => MethodsDictionary[command.MethodToExecute].DynamicInvoke(command.Args);
But I'd like to make a dynamic binding of events. Well, of course I can make it through ugly switch-case on command.Name property, but I still would like to try the solution with reflection.
The solutuion, as I see it, should looke something like:
foreach (var command in commands)
{
command.Bind(uielement, MethodsDictionary[command.MethodToExecute]);
}
//And command.Bind method is like:
public void Bind(UIElement uielement, Delegate methodToExecute)
{
//I know there's no such method like GetEventHandler, just an example
var handler = uielement.GetEventHandler(EventName);
handler += (sender, args) => methodToExecute.DynamicInvoke(Args);
}
I searched through several pretty similar questions:
Subscribe to an event with Reflection
https://social.msdn.microsoft.com/Forums/vstudio/en-US/d7f184f1-0964-412a-8659-6759a0e2db83/c-reflection-problem-subscribing-to-event?forum=netfxbcl
https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-hook-up-a-delegate-using-reflection
AddEventHandler using reflection
Add Event Handler using Reflection ? / Get object of type?
But these doesn't help me to solve the problem the way I want to. I tried some of the solutions above, but they didn't work out for me, failing with different exceptions.
UPD.
I tried to implement handler binding through switch-case, as I mentioned above. It resulted in this method inside Command class:
public void Bind(UIElement element)
{
switch (this.Name)
{
case "MouseRightButtonUp":
{
element.MouseRightButtonUp += (sender, args) => MethodsDictionary[this.MethodToExecute].DynamicInvoke(this.Args);
break;
}
case "Click":
{
//UIElement doesn't have Click event
var button = element as ButtonBase;
button.Click += (sender, args) => MethodsDictionary[this.MethodToExecute].DynamicInvoke(this.Args);
break;
}
/*And so on for each event*/
default:
{
throw new NotSupportedException();
}
}
}
I don't like, how that part with adding new handlers is just a copy-paste of previous section, but I don't see another workaround in this situation. I'd like to use the reflection in this case, but I don't know if it's possible.
If you can't get reflection to work, you can use another Dictionary to store supported event subscriber methods:
Command.cs
public class Command
{
[JsonPropertyName("EventName")]
public string EventName { get; set; }
[JsonPropertyName("MethodToExecute")]
public string MethodToExecute { get; set; }
[JsonPropertyName("Args")]
public string Args { get; set; }
/*Methods*/
}
JsonEventMapper.cs
class JsonEventMapper
{
private Dictionary<string, Action<UIElement, EventHandler>>> SupportedEventSubscriberMap { get; }
private Dictionary<string, EventHandler> RegisteredEventHandlerMap { get; }
public JsonEventMapper()
{
this.SupportedEventSubscriberMap = new Dictionary<string, Action<UIElement, EventHandler>>()
{
{
nameof(UIElement.MouseRightButtonUp), (uiElement, handler) => uiElement.MouseRightButtonUp += handler.Invoke
},
{
nameof(UIElement.LostFocus), (uiElement, eventSubscriber) => uiElement.LostFocus += handler.Invoke
}
};
this.RegisteredEventHandlerMap = new Dictionary<string, EventHandler>()
{
{
nameof(UIElement.MouseLeftButtonUp), CloseApp
},
{
nameof(UIElement.LostFocus), NextJson
}
};
}
public void RegisterJsonCommands(IEnumerable<Command> commands, UIElement uiElement)
{
foreach (var command in commands)
{
BindCommandToEvent(uiElement, command);
}
}
private void BindCommandToEvent(UIElement uiElement, Command command)
{
if (this.SupportedEventSubscriberMap.TryGetValue(command.EventName, out Action<UIElement, EventHandler> eventSubscriber)
&& this.RegisteredEventHandlerMap.TryGetValue(command.EventName, out EventHandler eventHandler))
{
eventSubscriber.Invoke(uiElement, eventHandler);
}
}
private void CloseApp(object sender, EventArgs args)
{
// Handle event
}
private void NextJson(object sender, EventArgs args)
{
// Handle event
}
}
Usage
IEnumerable<Command> commands = DeserializeJsonToCommands();
var eventMapper = new JsonEventMapper();
eventMapper.RegisterJsonCommands(commands, button);
You surely want to adjust details to your specific scenario.
Don't forget to unsubscribe from the events (e.g., by defining another SupportedEventUnsubscriberMap).
I think what you're trying to do is something like that:
public class CommandModel
{
/// properties
public void Bind(UIElement element)
{
EventBinder.Bind(() => MainViewModel.RunCommand(MethodToExecute, Args), element, EventName);
}
}
public static class EventBinder
{
public static void Bind(Action action, object eventSource, string eventName)
{
var eventInfo = eventSource.GetType().GetEvent(eventName);
EventHandler eventHandler = (s, e) => action();
eventInfo.AddEventHandler(eventSource, ConvertDelegate(eventHandler, eventInfo.EventHandlerType));
}
private static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(
targetDelegateType,
originalDelegate.Target,
originalDelegate.Method);
}
}
I use C# and I want to trigger an event from within a class :
So if the Price property of a class was changed then an event onPriceChanged (outside the class) should be fired.
However, I get an error:
The name 'onPriceChanged' does not exist in the current context
How could I fix this?
(I guess that I could pass the eventhandler to the class via constructor...but if possible I would prefer not to pass the eventhandler to the class)
Here is my code :
using System;
public delegate void delEventHandler();
class clsItem
{
//private static event delEventHandler _show;
private delEventHandler _show;
private int _price;
public clsItem() //Konstruktor
{
_show += new delEventHandler(Program.onPriceChanged); // error here : The name 'onPriceChanged' does not exist in the current context
}
public int Price
{
set
{
_price = value;
_show.Invoke(); //trigger Event when Price was changed
}
}
}
class Program
{
static void Main()
{
clsItem myItem = new clsItem();
myItem.Price = 123; //this should trigger Event "onPriceChanged"
}
//EventHandler
public static void onPriceChanged()
{
Console.WriteLine("Price was changed");
}
}
You're doing this the wrong way round - you're trying to attach the event handler from the class, and clearly that cannot have access to the Program.onPriceChanged method!
You should expose your event, and attach the event handler from the client code (Program).
class clsItem
{
//private static event delEventHandler _show;
private delEventHandler _show;
private int _price;
public clsItem() //Konstruktor
{
}
public event delEventHandler Show
{
add { _show += value; }
remove { _show -= value; }
}
public int Price
{
set
{
_price = value;
_show?.Invoke(); //trigger Event when Price was changed
}
}
}
And:
clsItem myItem = new clsItem();
myItem.Show += onPriceChanged;
myItem.Price = 123; //this now does trigger Event "onPriceChanged"
Live example: http://rextester.com/WMCQQ40264
The way you're dealing with events is not a good practice. the reason why we use Events is to decouple the objects we create from the methods they need to call.
For example if you want to create another object of the same type(clsItem) and get it to call another method once its price changed, you get into trouble. So I'd suggest this code rather than the current one:
using System;
public delegate void delEventHandler();
class clsItem
{
public event delEventHandler PriceChanged;
private int _price;
public clsItem() //Konstruktor
{
}
public int Price
{
set {
if(value!=_price) // Only trigger if the price is changed
{
_price = value;
if(PriceChanged!=null) // Only run if the event is handled
{
PriceChanged();
}
}
}
}
}
class Program
{
static void Main()
{
clsItem myItem = new clsItem();
myItem.PriceChanged += new delEventHandler(onPriceChanged);
myItem.Price = 123; //this should trigger Event "PriceChanged" and call the onPriceChanged method
}
//EventHandler
public static void onPriceChanged()
{
Console.WriteLine("Price was changed");
}
}
Here is the more traditional way of doing what you want:
public delegate void delEventHandler();
class clsItem
{
public event delEventHandler Show;
private int _price;
public clsItem() //Konstruktor
{
}
public int Price
{
set
{
_price = value;
Show?.Invoke(); //trigger Event when Price was changed
}
}
}
class Program
{
static void Main()
{
clsItem myItem = new clsItem();
myItem.Show += onPriceChanged;
myItem.Price = 123; //this should trigger Event "onPriceChanged"
}
//EventHandler
public static void onPriceChanged()
{
Console.WriteLine("Price was changed");
}
}
Notice that clsItem no longer knows who is subscribing to its event. All it cares about is notifying any listeners who happens to be subscribed. There is no longer a dependency between clsItem and the onPriceChanged method.
I try to run some code when collection is changed. I keep collection as property in Data class:
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set { deviceAdjustedConfigurations = value; }
}
and register it in code like that:
Data.DeviceAdjustedConfigurations.CollectionChanged += new NotifyCollectionChangedEventHandler(DeviceAdjustedConfigurationsCollectionChanged);
But after registration CollectionChanged is null and the appropriate code in delegated method is not run. In this place DeviceAdjustedConiguration already contains some data. What am I doing wrong?
You should avoid having a set property accessor for collection types, one reason being the one you experienced here with events. Another problem is if someone caches the collection and adds items to it later.
var old = obj.DeviceAdjustedConfigurations;
obj.DeviceAdjustedConfigurations = new ObservableCollection<OfferedConfiguration>();
old.Add(new OfferedConfiguration()); // what should happen here?
instead, remove the set-accessor and use the existing collection directly.
obj.DeviceAdjustedConfigurations.Add(new OfferedConfiguration());
If you really need to set the collection, you need to handle this with for instance a property change event from the class that owns the DeviceAdjustedConfigurations.
public class Item
{
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set
{
if (deviceAdjustedConfigurations != value)
{
onDeviceConfigurationsChanging(deviceAdjustedConfigurations, value);
deviceAdjustedConfigurations = value;
}
}
}
public static event EventHandler<ConfigurationChangedEventArgs> DeviceConfigurationsChanging;
private static void onDeviceConfigurationsChanging(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
var handler = DeviceConfigurationsChanging;
if (handler != null)
{
handler(null, new ConfigurationChangedEventArgs(oldList, newList));
}
}
}
public class ConfigurationChangedEventArgs : EventArgs
{
public ConfigurationChangedEventArgs(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
OldList = oldList;
NewList = newList;
}
public ObservableCollection<OfferedConfiguration> OldList { get; private set; }
public ObservableCollection<OfferedConfiguration> NewList { get; private set; }
}
public class Consumer
{
public void foo()
{
Item.DeviceConfigurationsChanging += updateEvents;
}
private void updateEvents(object sender, ConfigurationChangedEventArgs args)
{
args.OldList.CollectionChanged -= onCollectionChanged;
args.NewList.CollectionChanged += onCollectionChanged;
}
private void onCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { }
}
well ive read a lot of posts here and there about why it isnt reliable to raise events via reflection.. my problem is this.. im using PostSharp to define an interface which allows a class to notify before and after a property is changed..
the NotifyAttribute ive created needs to be able to raise the PropertyBeforeChange and PropertAfterChange events.. thing is, even though i can retrieve the event, its GetRaiseMethod() returns null and hence i cannot raise the said events.. how can i go about doing that?
using PostSharp.Aspects;
namespace Core
{
public delegate void PropertyBeforeChangeEventHandler(object sender, string sPropertyName, object oCurrentValue, ref object oProposedValue, ref bool bCancel);
public delegate void PropertyAfterChangeEventHandler(object sender, string sPropertyName, object oOldValue, object oNewValue);
public interface INotify
{
event PropertyBeforeChangeEventHandler PropertBeforeChange;
event PropertyAfterChangeEventHandler PropertyAfterChange;
}
[Serializable]
public sealed class NotifyAttribute : LocationInterceptionAspect, IInstanceScopedAspect
{
bool _NotifyBefore { get; set; }
bool _NotifyAfter { get; set; }
public NotifyAttribute() { _NotifyAfter = true; }
public NotifyAttribute(bool bNotifyBefore, bool bNotifyAfter) { _NotifyBefore = bNotifyBefore; _NotifyAfter = bNotifyAfter; }
public override void OnSetValue(LocationInterceptionArgs args)
{
INotify oNotify = args.Instance as INotify;
if (oNotify == null) return;
object oCurrentValue = args.GetCurrentValue();
object oProposedValue = args.Value;
if (object.Equals(oCurrentValue, oProposedValue)) return;
bool bCancel = false;
if (_NotifyBefore)
{
var oObj = args.Instance.GetType().GetEvent("PropertyBeforeChange");
// RAISE EVENT HERE
}
if (bCancel) return;
args.Value = oProposedValue;
args.ProceedSetValue();
if (_NotifyAfter)
{
var oObj = args.Instance.GetType().GetEvent("PropertyAfterChange");
// RAISE EVENT HERE
}
}
public object CreateInstance(AdviceArgs adviceArgs) { return this.MemberwiseClone(); }
public void RuntimeInitializeInstance() { }
}
}
having defined this interface and this attribute, i can use it as follows..
public class Test : INotify
{
public event PropertyBeforeChangeEventHandler PropertyBeforeChange;
public event PropertyAfterChangeEventHandler PropertyAfterChange;
[Notify]
public string Name { get; set; }
}
Test oTest = new Test();
oTest.PropertyBeforeChange += Test_PropertBeforeChange;
oTest.PropertyAfterChange += Test_PropertyAfterChange;
oTest.Name = "Asim";
void Test_PropertBeforeChange(object sender, string sPropertyName, object oCurrentValue, ref object oProposedValue, ref bool bCancel)
{
}
void Test_PropertyAfterChange(object sender, string sPropertyName, object oOldValue, object oNewValue)
{
}
i created a class and when i create an employee object via form , i want to give a message;
this is my class, event and delegate
public delegate void ctorDel();
class Employee
{
private int empID;
private string empName;
public event ctorDel myEvent;
public Employee(int empID,string empName)
{
this.empID = empID;
this.empName = empName;
**if (myEvent != null)
{
myEvent();
}**
}
and in form
int id = Convert.ToInt16(textBox1.Text);
string name = textBox2.Text;
Employee emp = new Employee(id, name);
emp.myEvent += new ctorDel(showMessage);
and function
public void showMessage()
{
MessageBox.Show("An employee is created");
}
What is it you're trying to accomplish? The reason what you've tried doesn't work is because you're attaching your delegate after the ctor. Once you've called "new Employee" the event is long since fired.
If you really need such an event, create a factory class:
public delegate void EmpCreated();
public EmployeeFactory {
public event EmpCreated myEvent;
public Employee Create(int empId, string empName){
var result = new Employee(empId, empName);
if(myEvent != null) myEvent();
return result;
}
}
Subscribe to the event on the factory class and you'll get the event.
You're attaching the event after the constructor has already run.
It doesn't make sense to raise an instance event in the constructor, because since the initialization of the instance is not yet complete, there can't be any handler attached to the event...
However, you could create a static event:
public static event ctorDel myEvent;
...
Employee.myEvent += new ctorDel(showMessage);
(but don't subscribe to the event every time you create an Employee, or the handler will be invoked as many times as there are instances...)
Solution
Here's a generic approach to your problem
public class EventFactory
{
public U Create<U, V>(V constructorArgs)
{
var instance = (U)Activator.CreateInstance(typeof(U), constructorArgs);
OnCreated?.Invoke();
return instance;
}
public delegate void CreatedEventHandler();
public event CreatedEventHandler OnCreated;
}
You can then do
var ef = new EventFactory();
ef.OnCreated += myEventHandler;
var instance = ef.Create<Employee>(employeeArgs);
.. Going further
It is possible to adjust my code to provide greater flexiblity when you need to pass event arguments or when the constructor is parameterless. I haven't tested it but it should look somewhere along the lines of
public class EventFactory<T>
{
public U Create<U, V>(V constructorArgs, T eventArgs)
{
var instance = (U)Activator.CreateInstance(typeof(U), constructorArgs);
OnCreated?.Invoke(eventArgs);
return instance;
}
public U Create<U>(T eventArgs)
{
return Create<U, object>(null, eventArgs);
}
public delegate void CreatedEventHandler(T args);
public event CreatedEventHandler OnCreated;
}
public class EventFactory
{
public U Create<U, V>(V constructorArgs)
{
var instance = (U)Activator.CreateInstance(typeof(U), constructorArgs);
OnCreated?.Invoke();
return instance;
}
public U Create<U>() where U : new()
{
var instance = new U();
OnCreated?.Invoke();
return instance;
}
public delegate void CreatedEventHandler();
public event CreatedEventHandler OnCreated;
}
You can pass the handler when creating the Employee:
private Employee(ctorDel construcEvent)
{
if (construcEvent != null)
this.myEvent += construcEvent;
}
public Employee(int empID,string empName, ctorDel construcEvent)
: this(construcEvent)
{
this.empID = empID;
this.empName = empName;
if (myEvent != null)
{
myEvent();
}
}
And then:
Employee emp = new Employee(id, name, new ctorDel(showMessage));
By the time you subscribe for this event the instance is already constructed.
I would recommend using Factory pattern to hide the constructor.
class EmployeeFactory
{
public Employee Create(int id, string name)
{
Employee instance = new Employee(id, name);
var handler = EmployeeCreated;
if (handler != null)
{
EmployeeEventArgs e = new EmployeeEventArgs(instance);
handler(e);
}
return instance;
}
public event EventHandler<EmployeeEventArgs> EmployeeCreated;
}
Event subscription:
factory.EmployeeCreated += MyHandler;
Instance construction:
var emp = factory.Create(id, name);