async/await in INotifyPropertyChanging EventHandler with cancellable EventArgs - c#

I am using WPF with the MVVM pattern.
In my model class I implement the INotifyPropertyChanging Interface with custom EventArgs which derive from PropertyChangingEventArgs. This is necessary because I want to be able to "cancel" the property setter.
This event is being handled in the ViewModel containing that model.
In the event handler I have to make a call to the database and based on the result sometimes I have to cancel the property setter via the event args.
The model look similar to the following:
public class CancellablePropertyChangingEventArgs : PropertyChangingEventArgs
{
public bool Cancel { get; set; }
}
public class Model : INotifyPropertyChanging
{
public string MyProperty
{
get { return _myProperty; }
set
{
var args = RaisePropertyChanging();
if(!args.Cancel)
_myProperty = value;
}
}
public CancellablePropertyChangingEventArgs RaisePropertyChanging([CallerMemberName] string propertyName = "")
{
var eventArgs = new CancellablePropertyChangingEventArgs(propertyName);
if(PropertyChanging != null)
{
PropertyChanging(this, eventArgs);
}
return eventArgs;
}
}
The viewmodel which contains the above model and handles the PropertyChanging event and in the event handler I have a code similar to this:
private async void HandleModelPropertyChanging(object sender, PropertyChangingEventArgs e)
{
var args = e as CancellablePropertyChangingEventArgs;
if(args.PropertyName = "MyProperty")
{
var result = await CallToDataBaseAsync(...);
if(result == null)
args.Cancel = true;
}
}
I know that the async void in my event handler means "Fire and Forget" and therefore the event handler continues execution at CallToDataBaseAsync instead of awaiting it and so args.Cancel will always be false.
But are there any other possible solutions I could try while not blocking the GUI-Thread?

I did go this road before - the cancellation of setter is wrong approach and it will not work as you expect it to even with the awaiting. The wpf binding that is setting this property expects you to change the property or to throw an exception as a validation mechanism. So simple cancellation by not assigning value and not firing PropertyChanged event will result in desynchronization of your control that is setting this property - it will show the data that was typed by user and not what actually is stored in property.
If you really want to try it anyway change your handler to return Task but remember that this is flawed solution - if you would have multiple handlers attached only the result of the last one will be awaited. With some more work you could aggregate those tasks but is is pointless anyway.

Related

Provide feedback to event caller using writable property in EventArgs

In Dustin Campbell's answer in question Return a value from a Event — is there a Good Practice for this? it is stated that instead of returning data from an event handler, we can have a writable property on a set of custom EventArgs that is passed to the event similar to Cancel property of the WinForms FormClosing event.
How do I provide feedback to event caller using properties in EventArgs?
My specific scenario is that there is a Controller class that does Job A and there are many classes requesting the Job A to be done. Thus, the controller is subscribed to this event on all classes.
I want to give some feedback to the caller that the job is done. The tricky part is that those classes are module-like and controller doesn't know anything about them.
My though is to include that writable property to the delegate of the event in order for the controller to give feedback through it. This property could somehow be invoked using reflection, which is fine in my scenario.
you cannot define properties for delegates.
Also you do not need reflection for such a mechanism.
What you want to do is to define your "return"-properties in the EventArgs-derived class.
A simple such class would be:
public class JobEventArgs : EventArgs {
public bool Done { get; set; }
}
Now you can declare your event in the class as
public event EventHandler<JobEventArgs> Job;
Usage in the method which handles the event:
public void DoJob(object s, JobEventArgs args) {
// do stuff
args.Done = true;
}
and in the event invoking code:
public void FireJobEvent() {
var args = new JobEventArgs();
this.Job(this, args);
if(!args.Done) {
// the job was not handled
}
}
But frankly it rather seems like you want to do a job asynchronously with a notification when it finishes.
Which would result in syntax like..
class Module {
public void JobCompleted(IAsyncResult r) {
if(!r.IsCompleted)
return;
Console.WriteLine("The job has finished.");
}
public void ExecuteJob() {
var job = new EventArgs<JobEventArgs>((s, a) => { this.controller.JobA(); });
job.BeginInvoke(null, null,
r =>
{
this.JobCompleted(r);
if(r.IsCompleted)
job.EndInvoke(r);
}, null);
}
}

how do I subscribe to an event of a usercontrol

I have a group of usercontrols that I use multiple instances of through out my form.
The usercontrols have contain either a textbox, combobox, or checkbox and a get value method to return the value of it's repective control. Usually I have a button on the form whose clicked event calls the usercontrols getValue function, but now I need for something to happen on the form whenever the usercontrols controls changed event happens. Something like the following.
In form1.cs
form1.Controls.Add(UserControl1);
form1.Controls.Add(UserContorl2);
// gets called every time the combobox on UserControl1 has it's
// ValueChanged event raised
private void UserControl1_Changed(object Sender, EventArgs e)
{
form1.property1 = UserControl1.getValue();
}
// gets called everytime the textbox on UserControl2 has it's
// textChanged event raised
private void UserControl2_Changed(object Sender, EventArgs e)
{
form1.property2 = UserControl2.getValue();
}
I can't figure out how to throw/catch that event in form. I'm using VS 2005.
here is the code in one of my usercontrols. txtValue is a textbox
public partial class StringParameterControl : BaseParameterControl
{
public StringParameterControl(string aName, string aValue)
: base(aName)
{
InitializeComponent();
txtValue.Text = aValue;
}
public StringParameterControl(string aName)
: base(aName)
{
InitializeComponent();
}
public StringParameterControl()
: base()
{
InitializeComponent();
}
public void SetValue(string aValue)
{
txtValue.Text = aValue;
}
public override object GetValue()
{
return txtValue.Text;
}
}
UserControl1.Changed += UserControl1_Changed;
Update your control to include the following:
// A delegate type for hooking up change notifications.
// This is _what kind_ of event you want. It sets the signature your event handler methods must have.
public delegate void ChangedEventHandler(object sender, EventArgs e);
//the actual event
public event ChangedEventHandler Changed;
// Method to raise/fire the Changed event. Call this whenever something changes
protected virtual void OnChanged(EventArgs e)
{
ChangedEventHandler handler = Changed;
if (handler != null) handler(this, e);
}
//and update your existing SetValue() function like so:
public void SetValue(string aValue)
{
txtValue.Text = aValue;
OnChanged(EventArgs.Empty);
}
You can change your event signature to pass any information you want — for example the old or new value of the property (or both). I just used the standard event arguments for the example.
And speaking or properties, don't write separate Get/Set methods in C# like you just did. If you find yourself doing that, you probably want to use a property instead, which will enforce the correct get/set semantics automatically:
public string Value
{
get { return txtValue.Text;}
set {txtValue.Text = value; OnChanged(EventArgs.Emtpy); }
}
As far as I understand the usercontrols you are using do not fire events whenever their value changes, so you can't just subscribe to some "ValueChanged" event.
A possible solution might be to find the control you are interested in (Combobox, Textbox, etc.) in the usercontrols' "Controls" collection and directly subscribe to its appropriate events.
Or you can do with type inference style.
UserControl.Changed = (sender, e) => this.controlFired = true; //or whatever
The Changed is the public event you expose through a property in your control with the type of the delegate (void(object sender, EventArges e)). You can look up how to publish the event on msdn - there is plenty of articles on that.

Is my approach to assign values to an instance of custom EventArgs recommended?

In my current situation I have a class that performs an operation synchronously. During that operation an certain event will the thrown several times, depending on how often the situation where the event is raised occurs.
I know how the event-mechanism works and what the recommended ways of using are. Works well.(Note: My application is single-threaded)
What I want to is, to set a value to a property in my EventArgs. I've never had the need to set properties there.
This is a simplified situation of my current situation: (Note, I don't need answers telling me to use regex, or stringreplace, because that won't work in this situation)
I have this EventArgs:
public class TestEventArgs : EventArgs
{
public String OldString { get; private set; }
public String NewString { get; set; }
public TestEventArgs(String oldString)
{
this.OldString = oldString;
}
}
I do normally raise events this way:
public event EventHandler<TestEventArgs> ChangeString;
protected virtual void OnChangeString(String oldString)
{
EventHandler<TestEventArgs> handler = this.ChangeString;
if (handler != null)
{
handler(this, new TestEventArgs(oldString));
}
}
and by calling the OnChangeString method, because I read that this in the way to raise events a long time ago.
Modified code where I need a value of the EventArgs after it's been raised:
public event EventHandler<TestEventArgs> ChangeString;
protected virtual void OnChangeString(TestEventArgs args)
{
EventHandler<TestEventArgs> handler = this.ChangeString;
if (handler != null)
{
handler(this, args);
}
}
public void Foo()
{
String oldString = "this is the old string";
// this.OnChangeString(oldString) // this is the way I called before
// now I need to keep a reference to the EventArgs
TestEventArgs args = new TestEventArgs(oldString);
this.OnChangeString(args);
// here I do have full access to args.NewString
}
So, is it ok to keep a reference for the EventArgs and to raise the event with a method that accepts my EventArgs as parameter?
I think your question boils down to:
Is it OK to use the the arguments of an event to communicate data back to the source of the event?
First and foremost: at the low level, this will work. You can modify the event object, and you can see those changes in the code that raises the event.
But what if there are multiple event listeners. Who gets to set NewString?
If you expect only one, it is more clear to pass a delegate (of type Func<String, String>) to the class that now raises the event.
If I get your question right, you generally don't want to keep a reference to your TestEventArg, and reuse this object on another event.
In your case, your TestEventArg has a public properties call NewString. If any event handler updates this property, while it is begin reference by multiple part of your program, you may accidentally running into problem where the NewString property kept changing from another part of your problem.
However, if your TestEventArg has only readable property, then I see no problem keeping the reference and reuse it.
public class TestEventArgs : EventArgs
{
public String OldString { get; private set; }
public String NewString { get; private set; }
public TestEventArgs(String oldString, String newString)
{
this.OldString = oldString;
this.NewString - newString
}
}
In general, you want to keep your event arg immutable which will makes your program easier understand and debug.
Only time I keep the TestEventArg arg (in this case) is if you need access to the variable after the method has been called. Example of this is the OnClosing event for a Form. It has a variable you can set to tell it to cancel the close. In this case, if the client registers the callback, and can set a property on the TestEventArg class, then you'd want to have a variable pointed to it. If all you want to do is pass information to the callback and don't need anything back, then the first method is what I use.
One caveat for me is that if I use the OnChangeString method, I write it so that it encapsulates the details of the event args.
protected virtual void OnChangeString(string oldString)
{
EventHandler<TestEventArgs> handler = this.ChangeString;
if (handler != null)
{
var args = new TestEventArgs(oldString);
handler(this, args);
// Do something with args
}
}

INotifyPropertyChanged with threads

I have a
BindingList<T>
which is bound to a datagridview. One property in my class takes long to calculate, so I threaded the action. After the calculation I raise the OnPropertyChanged() event to notify the grid that the value is ready.
At least, that's the theory. But since the OnPropertyChanged Method is called from a differend thread I get some weired exceptions in the OnRowPrePaint method of the grid.
Can anybody tell me how I fore the OnPropertyChanged event to be excecuted in the main thread? I can not use Form.Invoke, since the class MyClass is not aware that it runs in a Winforms application.
public class MyClass : INotifyPropertyChanged
{
public int FastMember {get;set;}
private int? slowMember;
public SlowMember
{
get
{
if (slowMember.HasValue)
return slowMember.Value;
else
{
Thread t = new Thread(getSlowMember);
t.Start();
return -1;
}
}
}
private void getSlowMember()
{
Thread.Sleep(1000);
slowMember = 5;
OnPropertyChanged("SlowMember");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangingEventHandler eh = PropertyChanging;
if (eh != null)
{
eh(this, e);
}
}
}
People sometimes forget that the event handler is a MultiCastDelegate and, as such, has all the information regarding each subscriber that we need to handle this situation gracefully without imposing the Invoke+Synchronization performance penalty unnecessarily. I've been using code like this for ages:
using System.ComponentModel;
// ...
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
foreach (EventHandler h in handler.GetInvocationList())
{
var synch = h.Target as ISynchronizeInvoke;
if (synch != null && synch.InvokeRequired)
synch.Invoke(h, new object[] { this, e });
else
h(this, e);
}
}
}
What it does is simple, but I remember that I almost cracked my brain back then trying to find the best way to do it.
It first "grabs" the event handler on a local property to avoid any race conditions.
If the handler is not null (at lease one subscriber does exist) it prepares the event args, and then iterates through the invocation list of this multicast delegate.
The invocation list has the target property, which is the event's subscriber. If this subscriber implements ISynchronizeInvoke (all UI controls implement it) we then check its InvokeRequired property, and it is true we just Invoke it passing the delegate and parameters. Calling it this way will synchronize the call into the UI thread.
Otherwise we simply call the event handler delegate directly.
By design, a control can only be updated by the thread it was created in. This is why you are getting exceptions.
Consider using a BackgroundWorker and only update the member after the long lasting operation has completed by subscribing an eventhandler to RunWorkerCompleted.
Here's something I wrote a while ago; it should work reasonably well, but note the cost of lots of updates...
using System.ComponentModel;
using System.Threading;
public class ThreadedBindingList<T> : BindingList<T> {
SynchronizationContext ctx = SynchronizationContext.Current;
protected override void OnAddingNew(AddingNewEventArgs e) {
if (ctx == null) { BaseAddingNew(e); }
else { ctx.Send(delegate { BaseAddingNew(e); }, null); }
}
protected override void OnListChanged(ListChangedEventArgs e) {
if (ctx == null) { BaseListChanged(e); }
else { ctx.Send(delegate { BaseListChanged(e); }, null); }
}
void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); }
void BaseAddingNew(AddingNewEventArgs e) { base.OnAddingNew(e); }
}
Consideration 1:
Take a look at UIThreadMarshal class and its usage in this article:
UI Thread Marshaling in the Model Layer
You can change the class from static to instance and inject it into your object. So your object will not know about Form class. It will know only about UIThreadMarshal class.
Consideration 2:
I don't think returning -1 from your property is good idea. It looks like a bad design to me.
Consideration 3:
Maybe your class shouldn't use antoher thread. Maybe it's consumer classes who should decide how to call your property: directly or in a separate thread. In this case maybe you need to provide additional property, such as IsSlowMemberInitialized.

In C#, why can't I test if a event handler is null anywhere outside of the class that it's defined?

I am sure that I am just not understanding something fundamental about events and/or delegates in C#, but why can't I do the Boolean tests in this code sample:
public class UseSomeEventBase {
public delegate void SomeEventHandler(object sender, EventArgs e);
public event SomeEventHandler SomeEvent;
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
}
public class UseSomeEvent : UseSomeEventBase {
public bool IsSomeEventHandlerNull() {
// "LEFT HAND SIDE" COMPILER ERROR
return SomeEvent == null;
}
}
class Program {
static void Main(string[] args) {
var useSomeEvent = new UseSomeEvent();
useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEvent.SomeEvent == null) {
}
var useSomeEventBase = new UseSomeEventBase();
useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEventBase.SomeEvent == null) {
}
}
static void FuncToHandle(object sender, EventArgs e) { }
}
An event is really just an "add" operation and a "remove" operation. You can't get the value, you can't set the value, you can't call it - you can just subscribe a handler for the event (add) or unsubscribe one (remove). This is fine - it's encapsulation, plain and simple. It's up to the publisher to implement add/remove appropriately, but unless the publisher chooses to make the details available, subscribers can't modify or access the implementation-specific parts.
Field-like events in C# (where you don't specify the add/remove bits) hide this - they create a variable of a delegate type and an event. The event's add/remove implementations just use the variable to keep track of the subscribers.
Inside the class you refer to the variable (so you can get the currently subscribed delegates, execute them etc) and outside the class you refer to the event itself (so only have add/remove abilities).
The alternative to field-like events is where you explicitly implement the add/remove yourself, e.g.
private EventHandler clickHandler; // Normal private field
public event EventHandler Click
{
add
{
Console.WriteLine("New subscriber");
clickHandler += value;
}
remove
{
Console.WriteLine("Lost a subscriber");
clickHandler -= value;
}
}
See my article on events for more information.
Of course the event publisher can also make more information available - you could write a property like ClickHandlers to return the current multi-cast delegate, or HasClickHandlersto return whether there are any or not. That's not part of the core event model though.
You can easily use a very simple approach here to not repeatedly subscribe to an event.
Either of the 2 approaches below can be used:
Flag approach : _getWarehouseForVendorCompletedSubscribed is a private variable initialized to false.
if (!_getWarehouseForVendorCompletedSubscribed)
{
_serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
_getWarehouseForVendorCompletedSubscribed = true;
}
Unsubscribe Approach :Include an unsubscribe everytime you want to subscribe.
_serviceClient.GetWarehouseForVendorCompleted -= new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
_serviceClient.GetWarehouseForVendorCompleted += new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
Here the answer:
using System;
delegate void MyEventHandler();
class MyEvent
{
string s;
public event MyEventHandler SomeEvent;
// This is called to raise the event.
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent();
}
}
public string IsNull
{
get
{
if (SomeEvent != null)
return s = "The EventHandlerList is not NULL";
else return s = "The EventHandlerList is NULL"; ;
}
}
}
class EventDemo
{
// An event handler.
static void Handler()
{
Console.WriteLine("Event occurred");
}
static void Main()
{
MyEvent evt = new MyEvent();
// Add Handler() to the event list.
evt.SomeEvent += Handler;
// Raise the event.
//evt.OnSomeEvent();
evt.SomeEvent -= Handler;
Console.WriteLine(evt.IsNull);
Console.ReadKey();
}
}
Here's a slightly different question
What value is there in testing an externally defined event for null?
As an external consumer of an event you can only do 2 operations
Add a handler
Remove a handler
The null or non-nullness of the event has no bearing on these 2 actions. Why do you want to run a test which provides no perceivable value?
It's a rule in place when using the 'event' keyword. When you create an event, you are restricting outside class interaction with the delegate to a "subscribe / unsubscribe" relationship, this includes cases of inheritance. Remember an event is essentially a property, but for method calls, it isn't really an object itself, so really it looks more like this:
public event SomeEventHandler SomeEvent
{
add
{
//Add method call to delegate
}
remove
{
//Remove method call to delegate
}
}
You'd have to do that from the base class. That's the exact reason that you did this:
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
You can't access events from a derived class. Also, you should make that method virtual, so that it can be overridden in a derived class.
Publisher of the event implicitly overload only += and -= operations, and other operations are not implemented in the publisher because of the obvious reasons as explained above, such as don't want to give control to subscriber to change events.
If we want to validate if a particular event is subscribed in the subscriber class, better publisher will set a flag in its class when event is subscriber and clear the flag when it is unsubscriber.
If subscriber can access the flag of publisher, very easily identifiable whether the particular event is subscriber or not by checking the flag value.

Categories

Resources