Thursday 28 July 2016

Wire up event and event handler in between Xamarin.Forms, and custom renderer

How to fire an event on Xamarin.Forms, and handle the event through custom renderer  in native platform? 


First of all, why do you need a custom renderer? 

hmm...   allow you to customise controls on each platform...    allow developer to override the appearance and behaviour on each platform, etc.

Right,  this post is not about how to create custom view and custom renderer. Instead, this post is all about how to trigger the event in Xamarin.Forms, declare event and attach it to Custom View and finally handle the event in Custom Renderer.

The idea is to: 


  • Create a "custom label". 
  • Create a "Custom Renderer for the custom label". 
  • Click on the custom label will fire an event. 
  • Event will be handled by the handler in native platform. 


This is my main page (landing page) in Xamarin.Forms. I have custom label named "SampleLabel".
It is registered with a tap event.  When TAP being triggered, it fires another event named 'sampleLabel.Pass_EventFromFormsToRenderer' . This is an event declared in the customView.
public class LandingPage : ContentPage
    {
        public LandingPage ()
        {
            Title = "Landing Page";
            Padding = 20;

            //Instantiate a custom label.
            SampleLabel sampleLabel = new SampleLabel ();
            //Register the tap event to this label. 
            TapGestureRecognizer tapg = new TapGestureRecognizer ();
            tapg.Tapped += (sender, e) => {
                var customArgs = new CustomArgs{
                    Date = DateTime.Now,
                    AnyText = "Hello Jeff"
                };
                //Fire this event....  

//Handle this event in native element (via custom renderer).
                sampleLabel.Pass_EventFromFormsToRenderer(this, customArgs);
            };
            sampleLabel.GestureRecognizers.Add (tapg);



            var mainLayout = new StackLayout {
                BackgroundColor = Color.Yellow,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                VerticalOptions = LayoutOptions.FillAndExpand,
                Children = {sampleLabel}
            };

            Content = mainLayout;
        }
    }
 


Let's see what is in the custom view (SampleLabel.cs):
This custom view is a derived class from View. So that we can use ViewRenderer in the custom renderer.  If you are using a different derived class such as Entry, then you might need a EntryRenderer in your custom renderer.

Besides, 2 events have been declared in this custom view. It also means I want to have 2 events attached to this custom view.
namespace testevents
{
    //This class has a mapped renderer in native platform. 
    public class SampleLabel : View 
    {
        //Define my event handler here, with custom event arguments.
        public EventHandler<CustomArgs> Pass_EventFromFormsToRenderer;
        public EventHandler<CustomArgs> EventTwo;
    }

    public class CustomArgs : EventArgs
    {
        public DateTime Date {
            get;
            set;
        }

        public string AnyText {
            get;
            set;
        }
    }
}
 


Next, lets see what is in my custom renderer. 
Again, you wouldn't see any appearance being customised in the example as below, because this is all about how to wire the events and events handlers from Forms to Custom renderer.

So, in my Xamarin.iOS projects, I have created a custom renderer class named "SampleLabelIOS". As usual, you need to apply the assembly attribute to register your custom renderer to the custom view you have created in Xamarin.Forms. Subsequently, you need to extend your custom renderer class from a renderer class. In this case, I am using "ViewRenderer".

Note: Remember, if you are using ViewRendere, then you must apply the SetNativeControl().

using System;
using testevents;
using Xamarin.Forms;
using testevents.iOS;
using Xamarin.Forms.Platform.iOS;
using UIKit;

[assembly: ExportRenderer (typeof(SampleLabel),typeof(SampleLabelIOS))]
namespace testevents.iOS
{
    //Note:  I am using ViewRenderer here. Therefore apart from registering this class to SampleLabel in XamarinForms, 
    //you also need to specify what native view you want to use. In this case, I want to present UILable.
    public class SampleLabelIOS : ViewRenderer<SampleLabel, UILabel>
    {
        protected override void OnElementChanged (ElementChangedEventArgs<SampleLabel> e)
        {
            base.OnElementChanged (e);
        
            //Usually, if you use a direct renderer such as labelrenderer for a label,entryrenderer for entry or etc, 
            //you will get something in the 'Control'.  
            //In this example, Control equals to null. 
            //this is because this is a view renderer...  You need to create a view yourself. 
            //and you must apply the SetNativeControl() method.
            if (Control == null) {
                
                UILabel lbl = new UILabel {
                    Text = "Text from native renderer",
                    BackgroundColor = UIColor.Gray,
                    Frame = new CoreGraphics.CGRect (0, 0, this.Frame.Width , 100)
                };

                //Note: this is required. Without this, you will hit compilation error.
                SetNativeControl (lbl);
            }


            //Now, Handle the event in customRenderer. Why move the event handler to native view?  
            //Purely in the case when you need something, or you want to perform something available in native view. 

            //The newElement is refering to 'SampleLabel view'.
            if (e.NewElement != null) {
                var theLabelViewInForm = e.NewElement;

                //This is the place when you need to write your logic of how to handle the event. 
                theLabelViewInForm.Pass_EventFromFormsToRenderer += ((sender2, e2) => {
                    if (e2!= null){
                        var myDatetime = e2.Date;
                        var myText = e2.AnyText;

                        var alert = new UIAlertView("Alert from native iOS", "Hi from SampleListiOS renderer" + myDatetime.ToString() + " " + myText, null, "Cancel");
                        alert.Show();
                    }else{
                        var alert = new UIAlertView("Alert from native iOS", "Hi from SampleListiOS renderer", null, "Cancel");
                        alert.Show();
                    }

                });
            }


        }
    }
}


Try it out! click on the custom view (custom label), then you should see the alert. It is an alert from iOS. Not the displayAlert from Xamarin.Forms.

Happy coding!



No comments:

Post a Comment

How to run unit test for your Xamarin Application in AppCenter?

How to run unit test for your Xamarin application in AppCenter?  When we talk about Building and Distributing your Xamarin app, you m...