Send notifications between a renderer and an element

Completed

Note

.NET MAUI is the next evolution of Xamarin and what we recommend you develop mobile and desktop apps with, and you can learn more about .NET MAUI in several training modules. This Xamarin training module will not be maintained going forward.

The Xamarin.Forms element and the platform renderers share the responsibility of putting the native control on the screen and managing its behavior. Part of managing behavior is making sure data and notifications flow between the element and the native control.

Earlier, we used a BindableProperty to allow data changes to flow from the element to the renderer and native control. But sometimes the BindableProperty pattern isn't appropriate because there might not be data to share or state to change. In these cases, we just want to notify other components.

Notifications can be required in both directions. Each direction needs to be handled differently.

Diagram showing notifications flowing from the TouchUpInside event of a UIButton to the iOS ButtonRenderer, then to the Xamarin.Forms Button and your app's Clicked event handling.

Renderer notifies element

Consider a Xamarin.Forms Button. When users activate the button, they're interacting with the native control. The native control notifies the renderer, which then needs to share the notification with the element.

Because the renderer has a reference to the element through its Element property, the renderer can call a public method on the element to notify it of the event. The method needs to be public because the renderer and the element instance are almost always in different assemblies. One is usually a .NET Standard library, and the other is a platform-specific executable.

The following code shows a renderer using a public SendClicked method on an element:

public class ButtonRenderer : ...
{
    protected override void OnElementChanged(...)
    {
        ...
        Control.Tapped += Element.SendClicked();
    }
}

This method is functional, but it requires the element to expose some of its internal workings as public properties. So items other than the renderer have easy access to the SendClicked method.

Note

When you design reusable controls, it's a best practice to make the public API (application programming interface) as simple as possible. Extra methods, especially those that aren't thoroughly tested for use by app code, can confuse app developers and cause unintended behavior.

This code shows a page using the SendClicked method, which we'd like to avoid:

public class Button : View
{
    public void SendClicked();
}
public class MainPage : ContentPage
{
    public MainPage()
    {
        ...
        var button = new Button();
        button.SendClicked(); // This method should be available to the renderer only!
    }
}

Hide methods by using explicit interface implementation

One way to hide methods that we don't want to be fully public is to use explicit interface implementation. This technique is used by many of the built-in controls in Xamarin.Forms.

The first step is to extract the methods we want to hide into an interface:

public interface IButtonController
{
    void SendClicked();
}

Next, in our element code, we'll use explicit interface implementation, meaning the method name is prefixed with the interface:

public class Button : View, IButtonController
{
    ...
    void IButtonController.SendClicked()
    {
        // Handle notification, perhaps raise our own event
    }
}

When the interface is implemented in this way, the SendClicked method can't be called on a variable of type Button, and the method isn't visible to tools like IntelliSense. If you want to call the interface method, you need to explicitly cast the element to the interface type:

public class ButtonRenderer : ...
{
    protected override void OnElementChanged(...)
    {
        ...
        Control.Tapped += (Element as IButtonController).SendClicked();
    }
}

This technique doesn't prevent other code from calling the explicitly implemented methods by using the same casting. But it discourages it and clearly separates these methods from the public API.

Element notifies renderer

We also need to enable notifications to flow from the element to the renderer. For example, we want to be able to clear our SketchView to remove everything the user has drawn so far.

Our element will need to tell the renderer to clear the native object. The element doesn't have a reference to the renderer to make a direct method call, so we'll use a pattern called "Publish/Subscribe" (pub/sub).

There are many ways to implement pub/sub. In C#, the most common way is to use an event. When we raise an event, we're "publishing" a notification and an event consumer is "subscribing" to that notification. In the case of an element and renderer, using events could lead to memory leaks if we aren't very careful, because events tightly couple publisher and subscribers with object references. Xamarin.Forms provides a framework for loosely coupled notifications called MessageCenter. We'll use MessageCenter for our pub/sub.

MessageCenter has a static Send method for publishing and a Subscribe method. Both accept a message name parameter. In the message name and generic type arguments don't match, the message won't be delivered to the subscriber.

Unsubscribe from notifications

It's a good practice to unsubscribe from MessageCenter notifications when your renderer is disposed. Doing so will keep MessageCenter performing optimally and might prevent memory leaks.

Check your knowledge

1.

When coding a custom renderer in Xamarin.Forms, which renderer method should be overridden to do initialization like creating the native control, setting native properties, and so on?

2.

What's the most common way to pass data between a Xamarin.Forms View and a custom renderer that requires data passed from the View?

3.

The Xamarin.Forms renderer has access to both the Xamarin.Forms view and the native view. What's the name of the property in the renderer that provides access to the Xamarin.Forms view?

4.

When coding a custom Xamarin.Forms View and custom renderer that require notifications between each other, what's the advantage of using the messaging service?