Xamarin Forms - Pass a method as parameter to an XAML object

Emixam23Emixam23 USMember ✭✭✭

Hi,

I'm currently working with a CustomObject who need a CustomObjectRenderer for each platform.

The thing is, I would like to pass a method as parameter to this object, from the XAML side, so I would be able to use this callback, from my renderer.

<control:CustomObject Callback="CallbackFunction"/>

The CallbackFunction(object param) is then declared in the MainPage.xaml.cs of the PCL part.

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        base.BindingContext = this;
    }

    public void CallbackFunction(object param)
    {
        Debug.WriteLine((object as Element).Name);
    }
}

So, if I'm understanding well, my CustomObject have to be like that:

public CustomObject : Object
{
    public Action<object> Callback { get; set; }
}

But the thing is, I have an error about XAML parsing.. I don't get why this error is thrown..

At the end, what I want to do, it's to call this method from the renderer, and then handle things, do actions from the MainPage.xaml.cs, from the PCL part.

public class CustomObjectRenderer : ObjectRenderer
{
    NativeObject nativeObject;
    CustomObject customObject;

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

        if (e.NewElement != null)
        {
            customObject = e.NewElement as CustomObject;
            nativeObject = Control as NativeObject;
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
    }

    // Etc etc .... 

    private void METHOD_CALLED_BY_EVENT(object o)
    {
        // This method get call by the renderer event and then, I want to call 
        // the method CallbackFunction(object); and do actions.
        customObject.Callback(o as OBJECT_PARAM);
    }
}

Ok, it's a bit hard for me to explain my problem to you, so if you don't understand something, let me know ! Thank in advance for help !

Tagged:

Answers

  • Rod_at_RoxRod_at_Rox AUMember ✭✭
    edited October 2016

    Sorry if this is off track, but I think this is what you are trying to do...

    In the PCL, create a Method for the job you want to perform (eg: PerformJob), and create a second Method that will allow you to set the Action from the renderer:

    public Task PerformJob()
    {
        return Task.Run(() => PerformJobAction?.Invoke(););
    }
    
    public Action PerformJobAction;
    
    public void SetPerformJobAction(Action performJobAction)
    {
        PerformJobAction = performJobAction;
    }
    

    In each renderer, set the Action and implement the job:

    pcl.SetPerformJobAction(() =>
    {
        //TODO: Perform the job for this renderer
    });
    

    If you want to set the Action from XAML, then do it via a ICommand, set the method in the pcl to return ICommand which you can bind to, and set the Action from the implemented Command.

  • Emixam23Emixam23 USMember ✭✭✭

    After lot of tried, which didn't work, I had an idea, I tried and it works as I wanted by asking my question.


    First, create your custom object !

    CustomView

    public class CustomView : View
    {
        public static readonly BindableProperty MainPageCallbackProperty =
            BindableProperty.Create(nameof(MainPageCallback), typeof(Action<object>), typeof(CustomMap), null);
        public Action<object> MainPageCallback
        {
            get { return (Action<object>)GetValue(MainPageCallbackProperty); }
            set { SetValue(MainPageCallbackProperty, value); }
        }
    }
    

    We so use Action which is a container for a method/callback. But in my example, we will use Action<object>. Why? Because it will allows us to have an object has paramter to our callback, so we will be able to bring data back from the renderer.

    Then, create a page called MainPage.xaml by example. In the XAML part of this new page, add the following code:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:control="clr-namespace:Project.CustomControl;assembly=Project"
                 x:Class="Project.Page.MainPage">
    
      <ContentPage.Content>
        <control:CustomView MainPageCallback="{Binding MainPageCallbackAction"
                            VerticalOptions="Fill" HorizontalOptions="Fill"/>
      </ContentPage.Content>
    
    </ContentPage>
    

    About this XAML, two parts interest us.

    • XAML 'References'

      xmlns:control="clr-namespace:Project.CustomControl;assembly=Project"

    By these this xmlns, you can access your custom control.

    • Content of the page

      <ContentPage.Content> <control:CustomView MainPageCallback="{Binding MainPageCallbackAction}" VerticalOptions="Fill" HorizontalOptions="Fill"/> </ContentPage.Content>

    Now, we bind the MainPageCallback of our object to the MainPageCallbackAction, declared in the C# side.

    After that, our MainPage.xaml.cs would seems like that:

    public partial class MainPage : ContentPage
    {
        public Action<object> MainPageCallbackAction { get; set; }
    
        public MainPage()
        {
            base.BindingContext = this;
            MainPageCallbackAction = MainPageCallbackMethod;
            InitializeComponent();
        }
    
        private void MainPageCallbackMethod(object param)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                Debug.WriteLine("Welcome to the Callback :)");
                Debug.WriteLine("Emixam23 - Example");
            });
        }
    }
    

    Now, the last thing to look at is the CustomViewRenderer !

    public class CustomViewRenderer : ViewRenderer<CustomView, NativeView>
    {
        CustomView customView;
        NativeView nativeView;
    
        protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)
        {
            base.OnElementChanged(e);
    
            if (e.NewElement != null)
            {
                customView = e.NewElement as CustomView;
                nativeView = Control as NativeView;
                NativeView.CLicked += METHOD_CALLED_BY_EVENT;
            }
        }
    
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
        }
    
        private void METHOD_CALLED_BY_EVENT(object sender, EventArgs ea)
        {
            customView.MainPageCallback(ea.something.information);
        }
    }
    

    And then, take a look at the output, you'll be able to see the following:

    • Welcome to the Callback :)
    • Emixam23 - Example

    I hope this answer is clear and helps you !

Sign In or Register to comment.