How to pass object as bindable property to Xamarin.Forms custom control

Viewed 1449

As the title suggests, I'm trying to pass in a Person object to a custom control instead of passing in each property separately.

So this:

 <controls:PersonControl 
           Person="{Binding Person}"
           ControlTemplate="{StaticResource PersonControlTemplate}">
   </controls:PersonControl>

instead of this (which works, based on this implementation)

<controls:PersonControl 
       Name="{Binding Person.Name}"
       Age="{Binding Person.Age}" 
       ControlTemplate="{StaticResource PersonControlTemplate}">
</controls:PersonControl>

I've tried changing the bindable property signature on the PersonControl code behind but it's not working. I actually just get a blank screen.

So: 1 - Is this even possible (i know it's called a bindable property but does it take objects as well? and 2 - If not what is the recommended approach?

The reason I want to do this is the person object may grow over time and I would rather just update the custom control instead of the consuming page AND it's view model.

Update: Here's the PersonControl Code:

public partial class PersonControl : ContentView
    {

        public static readonly BindableProperty PersonProperty = BindableProperty.Create(
            nameof(Person),
            typeof(Person),
            typeof(PersonControl),
            string.Empty);

        public string Name
        {
            get { return this.Person.Name; }
        }

        public Person Person
        {
            get { return (Person)GetValue(PersonProperty); }
            set { SetValue(PersonProperty, value); }
        }

        public PersonControl()
        {
            InitializeComponent();
        } 

    }

And here's the PersonControl xaml:

<ContentView.Content>
     <StackLayout>
            <Label  Text="{TemplateBinding Person.Name, Mode=OneWay}"/>
      </StackLayout>
    </ContentView.Content>

and lastly the consuming page:

 <ContentPage.Resources>
    <ControlTemplate x:Key="PersonControlTemplate">
        <controls:PersonControl></controls:PersonControl>
    </ControlTemplate>        
</ContentPage.Resources>

 <ContentPage.Content>
    <StackLayout Spacing="10" x:Name="layout">
        <controls:PersonControl
            Person="{Binding Person}"
             ControlTemplate="{StaticResource PersonControlTemplate}"></controls:PersonControl>
              </StackLayout>
</ContentPage.Content>

The person object is a property on the page's viewmodel as per mvvm pattern. Thanks in advance for your help.

Update: Ive followed this tutorial and tried to replace the bindable string type with an object but still no joy

2 Answers

Yes you can, I used a Entry, type some text, then transfer text to the Label in ContentView. Here is running GIF.

enter image description here

First of all I add a property called PersonName like following code

  public partial class PersonControl : ContentView
    {
        public PersonControl()
        {
            InitializeComponent();
        }

        public static BindableProperty PersonNameProperty = BindableProperty.Create(
                    propertyName: "PersonName",
                    returnType: typeof(string),
                    declaringType: typeof(PersonControl),
                    defaultValue: "",
                    defaultBindingMode: BindingMode.OneWay);

        public string PersonName
        {
            get { return (string)GetValue(PersonNameProperty); }
            set { SetValue(PersonNameProperty, value); } 
        }
    }

Then, here is code about layout of ContentView. Please do not forget to add x:Name="ParentControl" in ContentView tab.

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
              x:Name="ParentControl"
             x:Class="CustomDemoControl.PersonControl">
  <ContentView.Content>
        <StackLayout>
            <Label  Text="{Binding Source={x:Reference ParentControl}, Path=PersonName}"/>
        </StackLayout>
    </ContentView.Content>
</ContentView>

Then I use it in the other page.

 <StackLayout>
        <Entry x:Name="myEntry" Text="1"/>

        <local:PersonControl
             BindingContext="{x:Reference Name=myEntry}" 
              PersonName="{Binding Path=Text,  StringFormat='Welcome Mr {0}'}"
             ></local:PersonControl>
    </StackLayout>

So, it turns out the answer was to pass a default object in the bindable property signature (in the custom control's cs) like so:

public static readonly BindableProperty PersonProperty =
           BindableProperty.Create(
               nameof(Person),
               typeof(Person),
               typeof(PersonProperty ),
               default(Person)); //this was the fix

Now, I missed this because I wasn't aware of errors in the control. It just wasn't showing up in the output window. Can anyone point me in the direction of how to comprehensively debug xamarin.forms apps properly? Or at least catch these kind of errors in future? Thank you

Related