Cross Platform C#

Events and the Xamarin MessagingCenter

Application code needs to raise events and respond to them. The MessagingCenter makes this whole process much simpler than standard .NET events.

Tightly coupled systems are a nightmare to deal with. Having references everywhere causes all sorts of problems with reusability. Think about this in the standpoint of mobile systems where we would like to share code between iOS, Android and Windows .NET systems. Having to pass around a reference to a UIViewController or Activity/Fragment so that you can make some type of change can be a nightmare. iOS doesn't understand an Android Activity/Fragment, and Android doesn't understand a UIViewController.

What's a developer to do? Thankfully, there are a couple of answers to this problem. The first, and most obvious, solution is to use standard .NET/C# events. Remember, nearly every feature in the .NET Framework is a part of Xamarin products. The second possible solution is something that is Xamarin.Forms-specific: the Xamarin MessagingCenter, which I'll explore in this article.

Event
Events in iOS/Android happen all of the time. Events are fired when the user touches a button, when a location changes, and when some status changes on the device. For example, in iOS, there's the TouchUpInside event on a button. A button raises this event when a user releases the UIButton while still having their finger inside the button's perimeter. A lot of these events happen inside the OS in a device, and developers are expected to process them as necessary.

Application code can also create events and raise them as necessary. What good is this? To communicate between screens, objects and layers in an application, it can be advantageous to fire events in one layer that's handled in another layer. For example, without an event, object references are necessary to communicate changes, and it can be quite a mess to keep those references around. (I went back and looked at my first Xamarin code from 2009 -- yuck.)

If you are working with Xamarin.Forms, the MessagingCenter is available for you. The MessagingCenter allows a developer to send and process events easily in code. The parts of the MessagingCenter are:

  • Send: Send will raise an event. It publishes a message on which a listener can act.
  • Subscribe: Subscribe will listen for messages that have a certain signature. Multiple listeners can subscribe to the same message signature.
  • Unsubscribe: Unsubscribe will stop a listener from responding to messages with the defined signature.

There are two basic signatures for the MessagingCenter. If no parameter is being passed, the signatures for the methods will be:

  • .Send<Sender>(Sender sender, string message)
  • .Subscribe<Sender>(object subscriber, string message, Action)
  • .Unsubscribe<Sender>(object subscriber, string message)

If a parameter needs to be passed, the signatures look a bit different:

  • .Send<Sender, Args>(Server sender, string message, Args args)
  • .Subscribe<Sender, Args>(Sender sender, string message, Action)
  • .Unsubscribe<Sender, Args>(object subscriber, string message)

Let's move on and look at an example. Listing 1 shows a simple example of a message update being sent. This message contains a parameter. In this example, the parameter will be an object with the current address at which a device is.

Listing 1: MessagingCenter Example
using Geolocator.Plugin;
using Geolocator.Plugin.Abstractions;

namespace MCenter
{
  public class LocationPage : ContentPage
  {
    private Label lbl;
    private IGeolocator loc;

    public LocationPage()
    {
      lbl = new Label()
      {
        XAlign = TextAlignment.Center,
        Text = "Welcome to Xamarin Forms!"
      };
      Content = new StackLayout
      {
        VerticalOptions = LayoutOptions.Center,
        Children = { lbl }
      };
      MessagingCenter.Subscribe<LocationPage, LocationAddress>(
        this, "UpdateAddress", (sender, args) => {
        lbl.Text = args.Address1;
      });
      loc = Geolocator.Plugin.CrossGeolocator.Current;
      loc.DesiredAccuracy = 25;
      loc.PositionChanged += Loc_PositionChanged;
      loc.StartListening(1, 25.0);
    }

    async void InitialLocation()
    {
      try
      {
        var pos = await loc.GetPositionAsync(timeoutMilliseconds: 50000);
        PerformReverseGeocode(pos);
      }
      catch (System.Exception sysExc)
      {
        System.Diagnostics.Debug.WriteLine("Error: {0}", sysExc.Message);
      }
    }
    async private void PerformReverseGeocode(Position p)
    {
      var lat = p.Latitude;
      var lon = p.Longitude;
      var ds = DependencyService.Get<IReverseGeocode>();
      var address = await ds.ReverseGeoCodeLatLonAsync(lat, lon);
      MessagingCenter.Send<LocationPage, LocationAddress>(this,
        "UpdateAddress", address);
    }
    void Loc_PositionChanged(object sender, PositionEventArgs e)
    {
      var pos = e.Position;
      PerformReverseGeocode(pos);
    }
    protected override void OnDisappearing()
    {
      base.OnDisappearing();
      MessagingCenter.Unsubscribe<LocationPage>(this, "UpdateAddress");
    }
  }

In this example, the application code:

  1. Gets the current latitude and longitude in Xamarin.Forms. The latitude and longitude is returned via James Montemagno's geolocation component. This component is available via NuGet.
  2. Calls the PositionChanged event on the geolocator as the device moves.
  3. Within the method that handles the PositionChanged event, the code will call into the platform-specific code to perform a reverse-geocode lookup.
  4. Posts a message to the MessagingCenter along with the address object when the address is returned; that message is posted via the .Send method.
  5. Finally handles the message via the .Subscribe method; the first line of the address is then displayed on the screen.

Note: I've written the code to be bare and simple and all within one class file, so it's easier for you to view the code and not have to switch between various files.

The output of this code is show in Figure 1.

[Click on image for larger view.] Figure 1. Messaging Center Example Results, Showing the Nearest Address for the App Running on a Device

Where To Use the MessagingCenter?
The MessagingCenter provides some great functionality for firing and listening for events. It's a great improvement over all of the wiring that needs to be written to support standard .NET events. One of the first questions that comes up when looking at the MessagingCenter is where does the MessagingCenter make sense? I have found a couple of places where it makes sense:

  • Screen to screen. For example, a user opens a screen and pushes a button to synchronize data with a central data repository. When that operation is completed and new data is brought to the device, it's probably a good idea to send a message out that tells the other screens that there's new data, and that they should update their data screens as necessary.
  • In an Model-View-ViewModel architected app, the MessagingCenter can be a great way for view models to communicate with each other quickly and easily.

I have complete confidence that there are more places, but these are the ones that I have experience with.

Wrapping Up
Applications need to respond to events that occur within an application. Application code needs to raise events, as well as respond to those raised events. The MessagingCenter makes this whole process much simpler than standard .NET events.As you get into it, you definitely want to try this feature out.

About the Author

Wallace (Wally) B. McClure has authored books on iPhone programming with Mono/Monotouch, Android programming with Mono for Android, application architecture, ADO.NET, SQL Server and AJAX. He's a Microsoft MVP, an ASPInsider and a partner at Scalable Development Inc. He maintains a blog, and can be followed on Twitter.

comments powered by Disqus

Featured

Subscribe on YouTube