How to pass a command in XAML?

Viewed 336

I have a Page with a ViewModel as a BindingContext:

public Page()
{
    InitializeComponent();

    this.BindingContext = new ViewModel();
}

ViewModel has a command:

public class ViewModel : INotifyPropertyChanged
{
    ...
    public ICommand SomeCommand { get; set; }

    public ViewModel()
    {
        SomeCommand = new Command((object data) => {});
    }
    ...
}

In my Page.xaml I use my custom View component which serves the only to be displayed and has an ability to be clicked:

<local:CircleView
    Radius="20"
    InnerText="Click me"
    InnerTextSize="15"
    TapCommand="{Binding SomeCommand}"
/>

In my CircleView.xaml.cs

...
    public ICommand TapCommand { get; set; }
...

In my CircleView.xaml:

...
<TapGestureRecognizer
    Command="{Binding Path=TapCommand, Source={x:Reference Name=CircleView}}"
    CommandParameter="{Binding Path=InnerText, Source={x:Reference Name=CircleView}}"
/>
...

When I run a program I get an error "No property, bindable property, or event found for TapCommand, or mismatching...". How can I pass a command in XAML?

2 Answers

You should add your TapCommand as dependency property to your user control. Add this to your CircleView.xaml.cs and remove the previously defined TapCommand.

See also: dependency-properties-overview

//making is a bindab
public static readonly DependencyProperty TapCommandProperty =
    DependencyProperty.Register("TapCommand", typeof(ICommand), typeof(CircleView) 
            /* possible more options here, see metadata overrides in msdn article*/);

public ICommand TapCommand
{
    get { return (ICommand)GetValue(TapCommandProperty); }
    set { SetValue(TapCommandProperty, value); }
}

Then, I am not sure, but since you are using the TapCommand in your TapGestureRecognizer I think you'll need to implement INotificationChanged on your CircleView as well.

You need to pass a reference to the ViewModel to the CircleView by adding a bindable property to the CircleView:

public static BindableProperty ParentBindingContextProperty = 
    BindableProperty.Create(nameof(ParentBindingContext), typeof(object), 
    typeof(CircleView), null);

public object ParentBindingContext
{
    get { return GetValue(ParentBindingContextProperty); }
    set { SetValue(ParentBindingContextProperty, value); }
}

You can then bind that in your xaml (note the x:Name must match the x:Reference):

<ContentView ... x:Name="Home" ... >
    ...
    <local:CircleView ParentBindingContext="{Binding Source={x:Reference Home}, Path=BindingContext}"/>

And finally, bind your tap gesture to the command in your "parent" view model in your xaml in your CircleView:

    <TapGestureRecognizer BindingContext="{Binding Source={x:Reference CircleView}, Path=ParentBindingContext}" Command="{Binding Path=TapCommand}" CommandParameter="{Binding Path=InnerText, Source={x:Reference Name=CircleView}}" />

You won't need the TapCommand in your CircleView.

Related