I have the following class
public class Foo
{
private string? _bar;
public event EventHandler<CancelPropertyChangingEventArgs>? CancelPropertyChanging;
public string? Bar
{
get => _bar;
set
{
if (CancelPropertyChanging is { } cancelPropertyChanging)
{
var eventArgs = new CancelPropertyChangingEventArgs()
{
Cancel = false,
NewValue = value,
OldValue = _bar,
PropertyName = nameof(Bar),
};
cancelPropertyChanging(this, eventArgs);
if (eventArgs.Cancel)
return;
}
_bar = value;
}
}
public override string ToString() => Bar ?? "";
}
Where I can register to the event CancelPropertyChanging and potentially cancel a setter.
Everything works as expected when no async/await is involved.
With the following code.
var foo = new Foo();
foo.Bar = "Init Value";
foo.CancelPropertyChanging += Foo_CancelPropertyChanging;
foo.Bar = "Hello World";
foo.Bar = "Hello World 2";
Console.WriteLine(foo.Bar);
Console.WriteLine(foo.Bar == "Hello World 2" ? "Error" : "Correct");
void Foo_CancelPropertyChanging(object? sender, CancelPropertyChangingEventArgs e)
{
Console.WriteLine($"Changing Sync - OldValue: {e.OldValue} | NewValue: {e.NewValue}");
if (Convert.ToString(e.NewValue) == "Hello World 2")
e.Cancel = e.Cancel || true;
}
I am getting this output:
Changing Sync - OldValue: Init Value | NewValue: Hello World
Changing Sync - OldValue: Hello World | NewValue: Hello World 2
Hello World
Correct
So I did successfully Cancel the setting of Hello World 2 into the Bar property of my Foo object.
The same code will fail when I declare the event handler async and introduce a await Task.Delay(1_000);
How could I await for all event handlers to really finish even if they are declared as async?
There isn't really a way to just say
await cancelPropertyChanging(this, eventArgs);
I wouldn't know if someone somewhere would register an event handler and mark it async and does what not, the second this happens my code will give undesired results.
Here you may find a demo of the code above:
https://dotnetfiddle.net/GWhk3w
Notice:
That this code demonstrates the issue at hand in the easiest setup I could think of, the acutal issue is more meaning full than a cancable setter, but it revolves around events and the ability to cancel, where I am facing the wrong cancel signal.
Edit:
A maybe realistic example would be.
Imagine you have a WPF window and register to the Closing event, which has a Cancel member.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.closing?view=windowsdesktop-6.0
No one would stop me from writing this
async void WpfWindow_Closing(object sender, CancelEventArgs e)
{
await Task.Delay(10_000);
e.Cancel = true;
}
How does the Window - if it actually does - wait for this code to finish to know if I set the Cancel member, and actually cancel the close of the window.