Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin - Binding Command to property of object inside User Control

I have began learning XAML a few days ago and I have a trouble with wrapping my head around this problem.

In Xamarin Forms, I want to create a user control which will contain a label and a button and be able to bind a command to usercontrol in XAML from another page which uses my user control.

I am currently getting exception:

Xamarin.Forms.Xaml.XamlParseException: 'Position 8:24. Cannot assign property "Test1": Property does not exists, or is not assignable, or mismatching type between value and property'

Here is a dumbed down version of it I am currently trying to get to work.

My Custom Control XAML

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App3.Controls.Control1">
    <StackLayout>
        <Button Command="{Binding Test1}" Text="Test"/>
    </StackLayout>
</ContentView>

Control using my Custom Control XAML

<?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:local="clr-namespace:App3"
             xmlns:controls="clr-namespace:App3.Controls"
             x:Class="App3.MainPage">

    <controls:Control1 Test1="{Binding Test2}" />
</ContentPage>

My Custom Control Codebehind

using System.Windows.Input;
using Xamarin.Forms;

namespace App3.Controls
{
    public partial class Control1 : ContentView
    {

        private static readonly BindableProperty controlProperty = BindableProperty.Create("Test1", typeof(ICommand), typeof(Control1), null);

        public Control1()
        {
            InitializeComponent();
        }

        public ICommand Test1
        {
            get
            {
                return (ICommand)GetValue(controlProperty);
            }
            set
            {
                SetValue(controlProperty, value);
            }
        }

    }
}

View Model of page using Custom Control

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;

namespace App3
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        public MainPageViewModel()
        {
            this.Test2 = new Command(test);
        }

        public void test()
        {
            Application.Current.MainPage.DisplayAlert("it works", "it works", "it works");
        }

        public ICommand Test2
        {
            get; set;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
like image 415
Krzysztof Wrona Avatar asked Jan 19 '17 23:01

Krzysztof Wrona


1 Answers

There are a few naming convention to follow if you want your BindableProperty detected, and bindable, from Xaml:

When you create the identifier field, name this field by the name of the property as you registered it, plus the suffix Property. This field is your identifier for the dependency property, and it will be used later as an input for the SetValue and GetValue calls you will make in the wrappers, by any other code access to the property by your own code, by any external code access you allow, by the property system, and potentially by XAML processors.

and

Define a DependencyProperty identifier as a public static readonly field on the owner type.

(this is the documentation for DependencyProperty, but it applies to BindableProperties quite well in this case. See https://msdn.microsoft.com/en-us/library/ms753358(v=vs.110).aspx)

You can fix your code by replacing

private static readonly BindableProperty controlProperty = BindableProperty.Create("Test1", typeof(ICommand), typeof(Control1), null);

public ICommand Test1
{
    get { return (ICommand)GetValue(controlProperty); }
    set { SetValue(controlProperty, value); }
}

by

public static readonly BindableProperty Test1Property = BindableProperty.Create("Test1", typeof(ICommand), typeof(Control1), null);


public ICommand Test1
{
    get { return (ICommand)GetValue(Test1Property); }
    set { SetValue(Test1Property, value); }
}

That should prevent the property detection issue.

to get your full stuff working, the Button command binding should have it's source set to the control itself, and you should set your MainPageViewModel as BindingContext for MainPage.

like image 126
Stephane Delcroix Avatar answered Oct 29 '22 06:10

Stephane Delcroix