23

I would like to pass a Xamarin.Forms.Button in it's own Command as the CommandParameter to my ViewModel. I know how to achieve this from the code behind e.g. ...

XAML (with most properties missed out for brevity)

<Button x:Name="myButton"
    Text="My Button"
    Command="{Binding ButtonClickCommand}"/>

XAML.cs

public partial class MyTestPage
{
    public MyTestPage()
    {
        InitializeComponent();

        myButton.CommandParameter = myButton;
    }
}

ViewModel

public class MyViewModel : ViewModelBase
{
    public MyViewModel()
    {
        ButtonClickCommand = new Command(
            (parameter) =>
            {
                var view = parameter as Xamarin.Forms.Button;
                if (view != null)
                {
                    // Do Stuff
                }
            });
    }

    public ICommand ButtonClickCommand { get; private set; }
}

... BUT is it possible to declare the CommandParameter in the XAML itself? Or in other words what is the binding syntax to set the parameter to the button itself?

<Button x:Name="myButton"
        Text="My Button"
        Command="{Binding ButtonClickCommand}"
        CommandParameter="{[WHAT WOULD GO HERE]}"/>

btw I've already tried CommandParameter="{Binding RelativeSource={RelativeSource Self}}" and that didn't work.

Thanks,

Gavin Sutherland
  • 1,666
  • 3
  • 23
  • 36
  • Why do you need the button in your VM? – Alex Anderson Sep 18 '14 at 12:19
  • Hi @AlexAnderson. Yeah I'm not super keen about it either. We are introducing Xamarin.Forms into an existing Xamarin iOS project. Just experimenting with ways to get a native UIPopoverController to show from a Xamarin.Form Page. There is no Forms support for Popups so just looking at ways around it at the moment and one of the hurdles is I need to know which UIElement to show the popup from ... hence passing the button. As I said ... kinda feels hokey. I'll go try your suggestion below. Thanks – Gavin Sutherland Sep 18 '14 at 13:48
  • As for popups check out the implementation of `ContentPage.DisplayActionSheet(...)` - if the action sheet is not enough you can reverse-engineer the code that shows it. And getting the Button reference will still require finding the UIButton reference from that. You are going to be better off with a custom renderer – Sten Petrov Sep 18 '14 at 14:10
  • Hi @StenPetrov. The issue with creating a custom renderer for the popup is that the UIPopoverController is an NSObject and not a UIView object. – Gavin Sutherland Sep 18 '14 at 15:46

4 Answers4

31

Xamarin.Forms has a Reference markup extension that does just that:

<Button x:Name="myButton"
    Text="My Button"
    Command="{Binding ButtonClickCommand}"
    CommandParameter="{x:Reference myButton}"/>

Although, this is the first time I'm seeing this need, and you probably can better separate your Views from your ViewModels and solve this by using a cleaner pattern, or by not sharing a command across buttons.

Stephane Delcroix
  • 16,134
  • 5
  • 57
  • 85
  • Hi @StephaneDelcroix. Unfortunately that didn't work either .. "Exception has been thrown by the target of an invocation." – Gavin Sutherland Sep 18 '14 at 15:44
  • could you share a stack trace ? are you using the xaml 2009 xmlns ? are you using the latest (1.2.2 or 1.2.3-pre2) version of XF ? – Stephane Delcroix Sep 18 '14 at 19:33
  • I'm sorry @GavinSutherland, but I just tested this on 1.2.3-pre-2 and it works as expected. – Stephane Delcroix Sep 19 '14 at 12:38
  • Hi @StephaneDelcroix. I'll check the namespace and xf versions. If they are up to date then I'll perhaps try it in an empty project. Thanks for the update – Gavin Sutherland Sep 19 '14 at 15:10
  • Hi @StephaneDelcroix. I'm using v1.2.2 and I've got the following namespaces declared: `xmlns="http://xamarin.com/schemas/2014/forms"` and `xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"`. Does this look correct? – Gavin Sutherland Sep 21 '14 at 15:13
  • @GavinSutherland: yes, that's all right. I haven't tried with 1.2.2. – Stephane Delcroix Sep 21 '14 at 18:16
  • What would this look like in C#? And how would I refer to the button inside the command itself? – jbyrd Apr 07 '16 at 16:25
  • When I try that in code, I get an error "Specified cast is not valid", directly related to the CommandParameterProperty line. – jbyrd Apr 07 '16 at 18:26
  • This is exactly what I was looking for, thank you. Although it somewhat destroys the separation of user interface and code, I could not come up with a better solution to my problem: I want to be able to switch between input fields (Xamarin.Forms.Entry) after the user presses the 'Return' key using this solution: https://stackoverflow.com/a/37448069/5400351 @jbyrd: This is how my ViewModel's code looks like: `this.FocusVisualElementCommand = new Command(visualElement => visualElement?.Focus());` – Sam del Rö Jun 26 '17 at 10:00
8
 <Button x:Name="myButton"
        Text="My Button"
        Command="{Binding ButtonClickCommand}"
        CommandParameter="{x:Reference myButton}"/>

In your ViewModel

public YourViewModel()
{
    ButtonClickCommand= new Command(ButtonClicked);
}

private async void ButtonClicked(object sender)
{
    var view = sender as Xamarin.Forms.Button;
}
3

A simple method would be:

In XAML:

 <Button Text="BUTTON-TEST"
            Clicked="Avaliar"
            CommandParameter="like"/>

In C#:

private void Avaliar(object sender, EventArgs e)
{
     Console.WriteLine(((Button)sender).CommandParameter);
}
0
<Button x:Name="myButton"
    Text="My Button"
    Command="{Binding ButtonClickCommand}"
    CommandParameter={Binding RelativeSource=
                               {RelativeSource
                                Mode=FindAncestor,
                                AncestorType={x:Type Button}}/>

Should work, but im still at a loss why you need the button? The point of MVVM is to seperate Data and UI. everything you should need todo to the button can be done via DataBindings.

If the above doesnt work, the only other thing to try is to give the button an x:Key and CommandParamter = {StaticResource 'x:Key'}

Alex Anderson
  • 840
  • 5
  • 11