-1

Ok, I will try to make it simple, lets say I'm trying to make a textbox background to appear in red when an event occurs.

for example:

//Logic Class

private void SomeLogic() 
  {
    //some logic
    ...
    event() //trigger the event
    //some more logic
    ...
    event() //trigger the event again, it should not reach here until the last event is done
  }

On Main.xmal.cs

//Gets triggered when invoking the event()
private async void ChangeRed()
{
  Textbox1.Background = Brushes.Red;
  await Task.Delay(2000); //supposed to make program wait 2 seconds so we could see the change
  Textbox1.Background = Brushes.White;
}

Note: Its Import that the task.delay will hold the execution until its done, because the event might occur several times throughout the "logic"

Amit Kahlon
  • 108
  • 1
  • 1
  • 10
  • Is it essential to the question that the `ChangeRed` event handler is `async`? – Theodor Zoulias Sep 26 '20 at 09:58
  • Related: [How to await an event handler invocation?](https://stackoverflow.com/questions/58077892/how-to-await-an-event-handler-invocation) – Theodor Zoulias Sep 26 '20 at 10:01
  • It's not clear what you want to do and how your required flow should actually look like. Maybe you should provide a real problem instead of an abstraction. Providing abstractions is only reasonable, if you have fully understood the problem (and of course the solution), so that your abstraction can reflect all important details. It's not possible to suggest improvements or improve existing implementation/logic based on your provided details. – BionicCode Sep 26 '20 at 10:36
  • Generally the WPF event system is synchronous: each event handler is invoked after the previous has returned. This allows to either mark the event as handled (e.g. by introducing a flag) or to remove the handler from the event delegate (from inside the handler) in order to prevent further invocation. This way you can ensure that the handler is only invoked once. Synchronous also means that the event source only continues execution after the last event handler of the raised event has returned. – BionicCode Sep 26 '20 at 10:36
  • Note that `await` will make your event handler temporarily return, so that the execution continues e.g. next event handler is invoked or the event source continues. – BionicCode Sep 26 '20 at 10:54
  • @TheodorZoulias I need to for using the await keyword, Thanks for the link Im reading it right now – Amit Kahlon Sep 26 '20 at 11:38
  • @BionicCode thank you for the replay, So I understand that my await isnt doing what i wanted, could you suggest a way which the event will hold the execution untill it finish? – Amit Kahlon Sep 26 '20 at 11:43
  • @TheodorZoulias Ok I read it, Im not sure if thats related to me, should my event return anything? because I dont need anything from it, just need to to hold the code until its done – Amit Kahlon Sep 26 '20 at 11:50
  • Unfortunately you can't hold the code using the standard C# event pattern. Your best bet is to change the event declaration to accept asynchronous handlers (`Func` instead of `EventHandler`), so that when you invoke the handlers you get back tasks that you can subsequently `await`. Here is a more specific question: [How do I await events in C#?](https://stackoverflow.com/questions/27761852/how-do-i-await-events-in-c) – Theodor Zoulias Sep 26 '20 at 12:07
  • You have to implement some notification logic. Wait I'll show you a simple one. – BionicCode Sep 26 '20 at 12:26

1 Answers1

0

Your question is very broad and unspecific. It is not possible to give you the best suited solution as your true problem and context remains unknown.

Both simple generic solutions I provide, require to split up the code you wish to execute in partitions. Both solutions require a contract between observable and observer: the observer has to take the responsibility to notify the observable that handling the event is completed.

private void BeginSomeLogic() 
{
  //some logic
  ...
  event() //trigger the event
}

// Trigger the event again
// It should not reach here until the last event is done
public void EndSomeLogic() 
{
  //some logic
  ...
  event() //trigger the more events
}

Solution 1
Exposes details to the observer. Observer needs to know the relevant member (not recommended for library API).

private async void ChangeRedOnEvent(object sender, EventArgs e)
{
  Textbox1.Background = Brushes.Red;
  await Task.Delay(2000); //supposed to make program wait 2 seconds so we could see the change
  Textbox1.Background = Brushes.White;

  // Continue execution on the event source.
  (sender as MyEventSource).EndSomeLogic();
}

Solution 2
Implement a custom EventArgs that allows to set the event to handled. This enables the listener to control the flow implicitly (without knowing details).
In this scenario the observer must follow the unwritten rule to set the event to a handled state (not recommended for library API).

public class SomeEventArgs : EventArgs
{
  public SomeEventArgs(int listenerCount)
  {
    this.ListenerCount = listenerCount;
  }

  private int ListenerCount { get; set; }
  private bool isHandled;
  public bool IsHandled
  {
    get => this.isHandled;
    set
    {
      this.isHandled = value; 
      OnHandledChanged();
    }
  }

  public event EventHandler HandledChanged;
  protected virtual void OnHandledChanged()
  {
    if (--this.ListenerCount < 1)
    {
      this.HandledChanged?.Invoke(this, EventArgs.Empty);
    }
  }
}

Raise the event:

private SomeEventArgs HandledEventArgs { get; set; }

private void BeginSomeLogic() 
{
  //some logic
  ...

  EventHandler<SomeEventLogicArgs> handler = this.SomeEvent;
  if (handler == null)
  {
    return;
  }

  this.HandledEventArgs = new SomeEventArgs(handler.GetInvocationList().Length);
  this.HandledEventArgs.HandledChanged += EndSomeLogic;
  this.SomeEvent?.Invoke(this, this.HandledEventArgs) //trigger the event
} 

// Trigger the event again
// It should not reach here until the last event is done
public void EndSomeLogic(object sender, EventArgs e) 
{
  this.HandledEventArgs.HandledChanged -= EndSomeLogic;

  //some logic
  ...
  event() //trigger the more events
}

Handle the event:

private async void ChangeRedOnEvent(object sender, SomeEventArgs e)
{
  Textbox1.Background = Brushes.Red;
  await Task.Delay(2000); //supposed to make program wait 2 seconds so we could see the change
  Textbox1.Background = Brushes.White;

  // Continue execution on the event source.
  e.IsHandled = true;
}

You can use some WaitHandle instead or what ever synchronization mechanism is better suited.
Again since you have asked a very broad and unspecific question you can only get a very vague and unspecific answer.

My recommendation is to revisit your class design and refactor it to get rid of this situation. Your problem is the result of a bad/no design.

BionicCode
  • 1
  • 4
  • 28
  • 44
  • First thank you very much for the the answer, as you say it seems that my design is wrong and thats part of the reason I post here, as I wasnt sure if what Im doing is correct, have a great week. – Amit Kahlon Sep 26 '20 at 22:23