Can I define my own events and have them bubble up the UX-hierarchy?

I’m looking for a good way to handle errors for my Parse.com API, and I’m looking for a way to propagate errors up the hierarchy of views until they can be handled.

Use cases:

  • connection lost errors should be intercepted by the root, so it can show a “no connection” overlay, while periodically trying to reconnect
  • “session lost” errors should be propagated to the root by most views, so that the “login” screen can be thrown up.
  • The login screen however, would intercept these errors, and display a “invalid user name or password” message
  • errors connected with user actions should simply display an error message and let you continue, this would have to be done by the view where the action is performed

I image an event handler delegate that returns a “bool” specifying whether the event has been handled or not:

public delegate bool ErrorHandler(object sender, Parse.ErrorEventArgs args);

The instantiations could look something like this:

MyApp.ux:

<App>
    <Parse.Root ux:Name="_parse" ClientId="Foo" ApplicationId="Bar" />
    ...
    <PageControl>
        <LogInPage ux:Name="_logInPage"/>
        ..
    </PageControl>
</App>

LogInPage.ux:

<Page ux:Class="LogInPage">
    <Parse.Child ux:Name="_parse" />
     ...
</Page>

In the codebehinds for both files, you would set an error handler:

self._parse.Error += ParseError;

(Alternative: specify Error="ParseError" in the tag.)

In LogInPage.ux it would look like this:

bool ParseError(object sender, Parse.ErrorEventArgs args) {
    if (var tmp = args as Parse.LoginError) {
        ShowPopup("Invalid username or password - try again!");
        return true; // handled
    }
    return false; // not handled - bubble up!
}

And in MyApp.ux it would look like this:

bool ParseError(object sender, Parse.ErrorEventArgs args) {
    if (var tmp = args as Parse.InvalidSessionEventArgs) {
        LogInPage.Activate(); // show login dialog, because the session is invalid
        return true;
    } // else if etc etc
    return false; // note: unhandled error at root: fatal!
}

Is something like this possible? Am I fantasizing?

Hi!

All events in Fuse are implemented as extrinsics - e.g. the Node class actually has no idea what a Pointer event is, yet they still bubble. So yes - bubbling is possible! :slight_smile:

You can have a look at the implementation of Fuse.Input.Pointer for a complete reference

Basically, this is the pattern for implementing a bubbling event:

public static class ParseEvents
{

  static PropertyHandle _eventHandle = Properties.CreateHandle();

  [Uno.UX.AttachedEventAdder("Parse.Error")]
  public static AddHandler(Node n, Parse.ErrorEventHandler handler)
  {
      n.Properties.AddToList(_eventHandle, handler);
  }

  [Uno.UX.AttachedEventRemover("Parse.Error")]
  public static RemoveHandler(Node n, Parse.ErrorEventHandler handler)
  {
      n.Properties.RemoveFromList(_eventHandle, handler);
  }

  public static RaiseError(Node n, Parse.ErrorEventArgs ars)
  {
        // Bubbling loop
        while (n != null && !args.IsHandled)
      {
          n.Properties.ForEachInList(_eventHandle, Invoke, args);
          n = n.ParentNode;
      }
  }

  static void Invoke(object handler, object args)
  {
        var h = handler as Parse.ErrorEventHandler;
      var a = args as Parse.ErrorEventArgs;
      h(a);
  }

}

With this setup, you can handle the event at any level in the UX heirarchy :slight_smile:

To use it from UX:

<Panel Parse.Error="handler">

Pretty damn neat! Exactly what I was hoping for :wink: