I have this code :
public void SomeMethod()
{
MyClass clss = new MyClass(); //note: MyClass implements INotifyPropertyChanged
clss.DoSomething();
clss.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MyEventHandler);
}
static void MyEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Debug.WriteLine("some property on MyClass has changed!");
}
This is working ok and when the property in SomeClass changes, MyEventHandler() is run.
But now I need to pass aditional data from SomeMethod() to MyEventHandler() , how can I do this?
* UPDATE *
ok I guess I should have explained better the whole problem: the method DoSomething() in MyClass makes a call to an external web service, passing it a callback so when the web service finish its work, it will call the callback, passing it a value with the result of the operation. Inside that callback, I am changing a property of the class to assign it the value received from the web service, thus triggering the propertyChanged event.
Then in the caller class, I subscribe to that event so I can do some things when it happens.
The final objective is, after calling DoSomething(), be able to wait until the web service has finished its job and returned a result, so I can then save some things in the database etc. and only then, return from SomeMethod()...
so this is MyClass, simplified:
public class MyClass : INotifyPropertyChanged
{
private long _wsReturnValue;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public long wsReturnValue
{
get { return _wsReturnValue; }
set {
_wsReturnValue = value;
OnPropertyChanged("wsReturnValue");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new System.ComponentModel.PropertyChangedEventArgs(name));
}
public void DoSomething(object entity)
{
//here I just call external web service and returns, the webservice will call TheCallback() when finished
}
public void TheCallback(CommunicationEventArgs e)
{
this.wsReturnValue = e.res;
}
}
And this is the class that uses MyClass:
class MainClass
{
public void SomeMethod(object someObject)
{
MyClass clss = new MyClass(); //note: MyClass implements INotifyPropertyChanged
clss.DoSomething(someObject); //someObject contains data that I want to use later in the event handler
clss.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MyEventHandler);
}
private static void MyEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//here I need to use the object someObject...
}
}
Not sure if this is even close to what you mean, but here you go;
class EventClass
{
public void SomeMethod()
{
MyClass clss = new MyClass(); //note: MyClass implements INotifyPropertyChanged
clss.DoSomething(new object());
clss.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MyEventHandler);
}
private static void MyEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var customeEventArgs = (CustomEventArgs) e;
Debug.WriteLine("some property on MyClass has changed! Extra Data : {0}", customeEventArgs.ExtraData);
}
}
Implemented some more shell code to illustrate
internal class MyClass
{
public void DoSomething(object data)
{
var e = new CustomEventArgs("Property")
{
ExtraData = data
};
PropertyChanged?.Invoke(this, e);
}
public event PropertyChangedEventHandler PropertyChanged;
}
internal class CustomEventArgs : PropertyChangedEventArgs
{
public CustomEventArgs(string propertyName) : base(propertyName)
{
}
public object ExtraData { get; set; }
}
Any help? :)
Related
I will explain what I am trying to do first.
I have a quite a few of DataGrids and each DataGrid use different classes for there data type and instead of subscribing an Event handler for each one, I was hoping to make a generic event handler and get the type from from the sender object.
I am using EntityFramework Database First
One example of one of the classes:
public partial class StaffData : INotifyPropertyChanged
{
public long ID { get; set; }
public string StaffNameFirst { get; set; }
public string StaffNameSecond { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
My ViewModel:
VeiwModelBase holds the INotifyPropertyChanged data.
public class MasterViewModel : ViewModelBase
{
public static ObservableCollection<StaffData> MasterDataBinding
{
get { return _mMasterData; }
private set
{
if (value == _mMasterData)
return;
_mMasterData = value;
OnPropertyChanged();
}
}
public MasterViewModel()
{
_mMasterData.CollectionChanged += master_CollectionChanged;
}
public void master_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//asign PropertyChanged event here
}
private void master_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Type foo = sender.GetType().GetGenericArguments()[0];
var newRowData = sender as foo;
SaveData(newRowData);
}
private static void SaveData(object newRowData)
{
Type foo = newRowData.GetType().GetGenericArguments()[0];
var originalData = dataBaseEntities.foo.FirstOrDefault(p => p.ID == newRowData.ID);
entities.Entry(originalData).CurrentValues.SetValues(newRowData);
dataBaseEntities.SaveChanges();
}
}
These are the two methods above which I can't seem to figure this out, I have tried countless ways using Getype with not much success (I left my last attempt in hopefully to illustrate what I am trying to do). I have commented out how I am normally going about this:
private void master_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Type foo = sender.GetType().GetGenericArguments()[0];
var newRowData = sender as foo;
//var newRowData = sender as StaffData
SaveData(newRowData);
}
//private static void SaveData(StaffData newRowData)
private static void SaveData(object newRowData)
{
Type foo = newRowData.GetType().GetGenericArguments()[0];
var originalData = dataBaseEntities.foo.FirstOrDefault(p => p.ID == newRowData.ID);
//var originalData = dataBaseEntities.StaffData.FirstOrDefault(p => p.ID == newRowData.ID);
entities.Entry(originalData).CurrentValues.SetValues(newRowData);
entities.SaveChanges();
}
When trying to use the variable as a type I get this error,
Error CS0118 'foo' is a variable but is used like a
type
Is there a way to get the type when you don't know which datagrid will implement the PropertyChanged event and use it so as you can make a generic event handler for all the Datagrid controls?
Or am I going about this the wrong way?
Not sure if I really understand your question, but you could check the type of the sender argument at runtime and call an appropriate method like this:
private void master_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is StaffData)
{
DoSomething((StaffData)sender);
}
else if (sender is SomeOtherData)
{
DoSomething((SomeOtherData)sender);
}
...
}
private void DoSomething(StaffData data)
{
...
}
private void DoSomething(SomeOtherData data)
{
...
}
However, I'd prefer to have different PropertyChanged handler methods for different sender types.
You cant get the type inside the propertyChanged event handler but you can get the property name from PropertyChangedEventArgs.
Something like:
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "SomePropertyName")
{
//... do your stuf
}
}
I'm currently writing an application in which I deserialize relatively large objects (which can also grow in size, depending on what the user adds to them). I don't want to load all of them into RAM since that might cause problems when there are many of them.
Anyway, I want to handle events raised by the loaded instance of that class if there is one which is already my problem.
How can I subscribe an event handler to an object that is still null?
I think of something like "if there is an object and it raises that event handle it with that method".
Here is some sample code and the only approach I could think of though I already thought it couldn't work..
public class MyClassA
{
public event EventHandler PropertyChanged;
private string someProperty
public string SomeProperty
{
set
{
someProperty = value;
PropertyChanged?.Invoke(this, EventArgs.Empty);
}
}
public static MyClassA Load(string path)
{
/*...*/
}
}
public class MyClassB
{
public MyClassA InstanceOfA { get; private set; }
public MyClassB
{
//InstanceOfA.PropertyChanged += MyEventHandler; Not working, NullReference
}
// Handle InstanceOfA.PropertyChanged here...
public void MyEventHandler(object sender, EventArgs e)
{
/*...*/
}
}
Of course you cannot subscribe an event handler to an object that is still null,but you can subscribe when you assign a non null value to it.
Just use a property and a backing field:
public class MyClassB
{
private MyClassA myVar;
public MyClassA InstanceOfA
{
get { return myVar; }
private set
{
myVar = value;
if (myVar != null)
myVar.PropertyChanged += MyEventHandler;
}
}
public void MyEventHandler(object sender, EventArgs e)
{
}
}
I have a code which should notify a delegate in one of service classes on receiving an event:
public class TestClass : ParentClass
{
public event EventHandler<string> MyDelegate;
public override void OnAction(Context context, Intent intent)
{
var handler = MyDelegate;
if (handler != null)
{
handler(this, "test");
}
}
}
I instantiate it by:
private TestClass mytest= new TestClass ();
And then assign it in one of the functions:
mytest.MyDelegate+= (sender, info) => {
};
The delegate never gets called. I have stepped through with debugger and I see that delegate is being assigned, but the check inside a class is always null... Cant figure out whats the problem...
Sounds like an execution order issue. What could be happening is that OnAction within TestClass is being called before the delegate hookup. Try the following:
public class TestClass : ParentClass
{
public event EventHandler<string> MyDelegate;
public class TestClass(Action<string> myAction)
{
MyDelegate += myAction;
}
public override void OnAction(Context context, Intent intent)
{
var handler = MyDelegate;
if (handler != null)
{
handler(this, "test");
}
}
}
Just pass the delegate through the constructor, this should make sure its hooked up before any calls to OnAction()
You can pass through the handler in a couple ways:
1.) As an anonymous method:
private TestClass mytest= new TestClass ((sender, info) => { Console.WriteLine("Event Attached!") });
2.) Pass in the method group:
public class MyEventHandler(object sender, string e)
{
Console.WriteLine("Event Attached!");
}
private TestClass mytest= new TestClass(MyEventHandler);
I generally recommend the second way as it allows you to unhook the handler and do clean up once you are done with it:
myTest.MyDelegate -= MyEventHandler;
I am trying to write an abstract class that contains data common to multiple components of an application and provides the ability for those components to notify other components that the data they're working on has changed. Unfortunately, none of the other components are picking up the events being fired. I decided to write up a quick test to see if I could pinpoint what was wrong, but I'm running into the exact same issues. Am I misunderstanding how events work? Or am I missing something obvious?
void Main()
{
EventHandler<string> EVENT = delegate {};
var test = new DerivedClass("test", ref EVENT, 6);
var test2 = new DerivedClass("test2", ref EVENT, 8);
test.Number = 7;
test2.Number = 4;
}
public class DerivedClass : BaseClass
{
protected override void OnChanged(object sender, string e)
{
Console.WriteLine(string.Format("Derived class event fired from {0}! New value is {1}", sender, e));
}
public DerivedClass(string name, ref EventHandler<string> handler, int val) : base(ref handler, val)
{
this._name = _name;
}
public override string ToString()
{
return _name;
}
string _name;
}
public abstract class BaseClass
{
public virtual int Number
{
get { return _number; }
set
{
_handler(this, value);
_number = value;
}
}
protected virtual void OnChanged(object sender, string e)
{
Console.WriteLine("Base class event fired! " + e);
}
protected BaseClass(ref EventHandler<string> handler, int val)
{
_number = val;
_handler = handler;
if (_handler != null) _handler += this.OnChanged;
}
protected event EventHandler<string> _handler;
protected int _number;
public override string ToString()
{
return "A BaseClass";
}
}
output:
Derived class event fired from test! New value is 7
Derived class event fired from test2! New value is 4
Am I misunderstanding how events work?
Probably. Specifically, you seem to be under the impression either that events are handled on a per-class basis, or that child class instances will all share the same parent class instance.
You've declared your event to be non-static:
protected event EventHandler<string> _handler;
So every instance of any class that extends this class will have its own instance of this event. Depending on what you're trying to accomplish, you might consider making the event static?
I'm writing a class library for a Web API.
I have a base class and an interface with 30 blocks like this:
interface ISomethingApi {
void AuthenticateAsync(string username, string password);
event AsyncResponseHandler AuthenticateEnded;
void GetMemberAsync(string username);
event AsyncResponseHandler<Member> GetMemberEnded;
// more...
}
The base class called BaseHttpClient contains the implementation and all methods are empty and virtual.
class BaseHttpClient : ISomethingApi {
public virtual void GetMemberAsync(string username) {
throw new NotImplementedException();
}
public event AsyncResponseHandler<Member> GetMemberEnded;
// more...
}
Because the API is pretty non-standard, I am inheriting the base class with a XmlClient class. This class overrides virtual methods and do the job.
class XmlClient : BaseHttpClient {
public override void GetMemberAsync(string username) {
Member member;
// process here
// raising the event
GetMemberEnded(this, new AsyncResponseArgs<Member>(member));
// error: LogoffEnded can only appear on the left hand side of += or -=
}
}
The problem is I can't raise the events:
The event 'BaseHttpClient.LogoffEnded' can only appear on the left hand side of += or -=
A basic solution is to create methods in the base class like
protected void RaiseLogoffEnded(AsyncResponseArgs args) {
if (LogoffEnded != null) {
LogoffEnded(this, args);
}
}
But there are too many methods to create. I'd like to do something like:
public override void GetMemberAsync(string username) {
Member member;
// work done here
RaiseEvent(x => x.GetMemberEnded, new AsyncResponseArgs<Member>(member));
}
I suppose this is about reflection and expressions.
Is it a right way to do? (performace)
What documentation could I read to make this?
could you show me a valid code for this?
You could use a couple of static extension methods:
static class Extensions
{
public static void Raise(this EventHandler #event, object sender, EventArgs e)
{
if (#event != null)
#event(sender, e);
}
public static void Raise<T>(this EventHandler<T> #event, object sender, T e) where T : EventArgs
{
if (#event != null)
#event(sender, e);
}
}
Whereby you could do:
public class MyClass
{
public event EventHandler MyEvent;
public void DoSomething()
{
MyEvent.Raise(this, EventArgs.Empty);
}
}
While you can in fact use an expression, e.g.:
public void Raise<T>(Expression<Func<EventHandler<T>>> expr, T eventArgs)
where T : EventArgs
{
EventHandler<T> handler = expr.Compile().Invoke();
handler(this, eventArgs);
}
You probably want to do away with the redundant expression, and just use a Func<T> instead, as you are raising the event from the class directly. Through expressions, you would need to compile the expression, whereas Func<T> you don't:
public void Raise<T>(Func<EventHandler<T>> func, T eventArgs)
where T : EventArgs
{
EventHandler<T> handler = func();
handler(this, eventArgs);
}
You can make use of System.ComponentModel.EventHandlerList which will net you two advantages:
1) You will have your FireEvent mechanism.
2) The Events member doesn't use memory unless there are delegates subscribed. If you have a class with 30 events, you have 30 pointers in your class' footprint, whether or not there are any subscribers. EventHandlerList is a single object that contains any and all delegates subscribed. It's a very light-weight map (not a Dictionary). Notice that the event keys are static objects so as not to add to the class' footprint.
class AsyncResponseArgs : EventArgs
{
public Member Member { get; private set; }
public AsyncResponseArgs(Member m)
{
Member = m;
}
}
interface ISomethingApi
{
void AuthenticateAsync(string username, string password);
event EventHandler<AsyncResponseArgs> AuthenticateEnded;
void GetMemberAsync(string username);
event EventHandler<AsyncResponseArgs> GetMemberEnded;
}
class BaseHttpClient : ISomethingApi
{
private EventHandlerList Events = new EventHandlerList();
public virtual void AuthenticateAsync(string username, string password)
{
throw new NotImplementedException();
}
protected static object AuthenticateEndedEvent = new object();
public event EventHandler<AsyncResponseArgs> AuthenticateEnded
{
add { Events.AddHandler(AuthenticateEndedEvent, value); }
remove { Events.RemoveHandler(AuthenticateEndedEvent, value); }
}
public virtual void GetMemberAsync(string username)
{
throw new NotImplementedException();
}
protected static object GetMemberEndedEvent = new object();
public event EventHandler<AsyncResponseArgs> GetMemberEnded
{
add { Events.AddHandler(GetMemberEndedEvent, value); }
remove { Events.RemoveHandler(GetMemberEndedEvent, value); }
}
protected void FireEvent(object key, AsyncResponseArgs e)
{
EventHandler<AsyncResponseArgs> handler = (EventHandler<AsyncResponseArgs>)Events[key];
if (handler != null)
handler(this, e);
}
}
class XmlClient : BaseHttpClient
{
public override void GetMemberAsync(string username)
{
Member member;
// process here
FireEvent(GetMemberEndedEvent, new AsyncResponseArgs(member));
}
}
Added:
You can save yourself some typeing in BaseHttpClient by writing a code snippet.
You have to move your RaiseXXX methods to parent class, where you have your events defined. Make sure these methods are at least protected.
And don't forget to call your events via local variable to minimize error field.
var e = MyEvent;
if (e != null) e(this, EventArgs.Empty);
You could add a method to the base class that takes the event name as a String and raises the corresponding event via reflection like
public void Raise(String eventName, object source, EventArgs eventArgs)
{
var field = this.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new ArgumentException("No such event: " + eventName);
var eventDelegate = (MulticastDelegate)field.GetValue(this);
if (eventDelegate != null)
foreach (var handler in eventDelegate.GetInvocationList())
handler.Method.Invoke(handler.Target, new object[] { source, eventArgs });
}
I don't know anything about performance, though.