XAM335 Mobile application architecture

Exercise 2: Extend an existing renderer

In this exercise, you'll extend the provided label renderers to create a clickable "hyperlink label" that can launch the default browser when tapped.

This exercise includes steps for all platforms supported by Xamarin.Forms. You're not required to complete the steps for every platform but it's recommend you run the exercises on at least two.

Screenshot of the completed exercise.
Completed exercise
To complete the exercise, you will need Visual Studio for Windows or macOS with the Xamarin development tools installed. You will also need either an emulator/simulator or a device to run the exercise on. Please see the setup page if you need help installing the Xamarin development environment.

Open the starter solution

This exercise is a continuation of the previous exercise. You can use your existing solution or begin from the prior Exercise 1 > Completed solution in your copy of the cloned or downloaded course materials.

If you're using Visual Studio on Windows, you'll see all of the platform or head projects: iOS, Android and Windows. If you're using macOS, the Windows projects will be disabled (grayed out).


Create a derived element

You'll create a new element in your shared code that derives from Label. Later, when you create the renderers, you'll apply them to this element.

  1. In the XFDraw shared project, create a new class named HyperlinkLabel.
  2. Update the signature to derive from Label.
using Xamarin.Forms;

namespace XFDraw
{
   class HyperlinkLabel : Label
   {
   }
}

Consume the custom element

  1. Open MainPage.xaml in the XFDraw shared project.
  2. Add an xml namespace named local to reach the XFDraw namespace.
  3. Replace the existing Label with your new HyperlinkLabel and leave all existing properties as they are.
<ContentPage ...
   xmlns:local="clr-namespace:XFDraw">
   <Grid x:Name="mainLayout" Padding="10">
       <local:HyperlinkLabel Text="university.xamarin.com" HorizontalOptions="Center" VerticalOptions="End" />
   </Grid>
</ContentPage>

Create the Android renderer

  1. In the Android head-project, create a new class named HyperlinkLabelRenderer that derives from LabelRenderer.
  2. Add a constructor that takes an Android Context and passes the Context to the base constructor. This is required for Android renderers.
  3. Override OnElementChanged.
  4. The Label renderer creates an Android TextView which is reachable from the Control property. Call Linkify.AddLinks to update the native TextView to a clickable hyperlink control; set the match options to All.
  5. Add the ExportRenderer assembly attribute above the namespace declaration to connect the HyperlinkLabelRenderer to the HyperlinkLabel element you created earlier.
[assembly: ExportRenderer(typeof(HyperlinkLabel), typeof(HyperlinkLabelRenderer))]
namespace XFDraw.Droid
{
   class HyperlinkLabelRenderer : LabelRenderer
   {
      public HyperlinkLabelRenderer(Context context) : base (context)
      {
      }

      protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
      {
         base.OnElementChanged(e);

         Linkify.AddLinks(Control, MatchOptions.All);
      }
   }
}

Create the iOS renderer

  1. In the iOS head-project, create a new class named HyperlinkLabelRenderer that derives from LabelRenderer.
  2. Override OnElementChanged.
  3. The Label renderer creates an iOS UILabel which is reachable from the Control property. Enable touch interaction on the native control by setting UserInteractionEnabled to true.
  4. Change the native control's text color to blue by setting the TextColor property.
  5. Create a UITapGestureRecognizer.
  6. Set the recognizer's target using the AddTarget method and point it to either a new named method or a delegate.

    • Create an NSUrl from Control.Text.
    • Open the url using UIApplication.SharedApplication.OpenUrl.
  7. Add the gesture to the native control via the renderer's Control property.
  8. Add the ExportRenderer assembly attribute above the namespace declaration to connect the HyperlinkLabelRenderer to the HyperlinkLabel element you created earlier.
[assembly: ExportRenderer(typeof(HyperlinkLabel), typeof(HyperlinkLabelRenderer))]
namespace XFDraw.iOS
{
    class HyperlinkLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            Control.TextColor = UIColor.Blue;

            Control.UserInteractionEnabled = true;

            var gesture = new UITapGestureRecognizer();

            gesture.AddTarget(() =>
            {
                var url = new NSUrl("https://" + Control.Text);

                if (UIApplication.SharedApplication.CanOpenUrl(url))
                    UIApplication.SharedApplication.OpenUrl(url);
            });

            Control.AddGestureRecognizer(gesture);
        }
    }
}

Create the Windows renderer

  1. In the UWP head-project, create a new class named HyperlinkLabelRenderer that derives from LabelRenderer.
  2. Override OnElementChanged.
  3. The Label renderer creates a Windows TextBlock which is reachable from the Control property. Set its Forground property to a blue solid color brush.
  4. In OnElementChanged, subscribe to its Tapped event.
  5. In the handler, use Launcher.LaunchUriAsync to open a webpage. You'll need to create a well-formed Uri. This means inserting "http://" before the label's content. You can reach the Xamarin.Forms Label using the Element property.
  6. Add the ExportRenderer assembly attribute above the namespace declaration to connect the HyperlinkLabelRenderer to the HyperlinkLabel element you created earlier.
[assembly: ExportRenderer(typeof(HyperlinkLabel), typeof(HyperlinkLabelRenderer))]
namespace XFDraw.UWP
{
    class HyperlinkLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            Control.Foreground = new SolidColorBrush(Colors.Blue);
     
            Control.Tapped += LabelTapped;
        }

        private async void LabelTapped(object sender, TappedRoutedEventArgs e)
        {
            var website = Element.Text;

            if (website.IndexOf("http://") == -1)
                website = "http://" + website;

            await Launcher.LaunchUriAsync(new Uri(website));
        }
    }
}

Exercise summary

In this exercise, you extended a renderer to add new behavior to an existing Xamarin.Forms element.

You can view the completed solution in the Exercise 2 > Completed folder of your copy of the cloned or downloaded course materials.

Go Back