Emixam23
Emixam23

Reputation: 3964

Pass a method as parameter to an XAML object

I'm currently working with a CustomObject that needs a CustomObjectRenderer for each platform.

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 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.

Upvotes: 1

Views: 2033

Answers (2)

Emixam23
Emixam23

Reputation: 3964

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 !

Upvotes: 0

Sven-Michael St&#252;be
Sven-Michael St&#252;be

Reputation: 14750

You can achieve this by using events.

MyView

public class MyView : View
{
    public event EventHandler<string> MyEvent;

    public void RaiseEvent(string parameter)
    {
        MyEvent?.Invoke(this, parameter);
    }
}

Page.xaml

<local:MyView MyEvent="MyView_OnMyEvent"></local:MyView>

Page.xaml.cs

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void MyView_OnMyEvent(object sender, string e)
    {
        Debug.WriteLine(e);
    }
}

Renderer

public class MyViewRenderer : ViewRenderer<MyView, SomeNativeView>
{
    private void METHOD_CALLED_BY_EVENT(string param)
    {
        Element.RaiseEvent(param);
    }
}

Upvotes: 1

Related Questions