2
votes

Based on @stuart's blog post, I can see how to bind to a literal string as a CommandParameter.

local:MvxBind="Click FindUserCommand, CommandParameter=foo"
public class FindUserViewModel: ViewModelBase
{
    public FindUserViewModel(IFindUserCommand findUserCommand)
    {
        _findUserCommand = findUserCommand;
    }

    // class truncated for brevity

    private IFindUserCommand _findUserCommand;
    public IFindUserCommand FindUserCommand
    {
        get { return _findUserCommand; }
        set { _findUserCommand = value; RaisePropertyChanged(() = > FindUserCommand); }
    }
}
public class FindUserCommand: IFindUserCommand
{
    // class truncated for brevity
    public void Execute(object parameter)
    {
        // RIGHT HERE:
        // value of parameter == foo
    }
}

How do I bind the entire ViewModel as a CommandParameter of a Button when the Command lives in it's own command object and NOT the ViewModel?

public void Execute(object parameter)
{
    var vm = (FindUserViewModel)parameter;
    var firstName = vm.FirstName;
    var lastName = vm.LastName;
    // etc...
}

note: if I have to extend MvvmCross somehow to make this function in a uniform manner similar to WPF, I'm ok with that.. I just don't know where to start.


All the examples I find declare the Command directly in the ViewModel like this:

MvvmCross Examples

private Cirrious.MvvmCross.ViewModels.MvxCommand _insertCommand;
public System.Windows.Input.ICommand InsertCommand
{
    get
    {
        _insertCommand = _insertCommand ? ? new Cirrious.MvvmCross.ViewModels.MvxCommand(DoInsert);
        return _insertCommand;
    }
}

private int _countAdded = 0;

private void DoInsert()
{
    _countAdded++;
    var kitten = _kittenGenesisService.CreateNewKitten(_countAdded.ToString());
    _dataService.Insert(kitten);
    RefreshDataCount();
}

And then bind the button to the View like this:

<Button
    android:text="Insert"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/button1"
    local:MvxBind="Click InsertCommand" />

But I want to declare the Command object in its own class file so it's more testable, and then inject it into the ViewModel.

Is CommandParameter binding possible in MVVMCross for Android, similar to how WPF does its command parameter binding?


WPF Example:

View button definition:

<Button 
    Command="{Binding SearchCommand}"
    CommandParameter="{Binding}"/>

ViewModel command property:

private ICommand _findUserCommand;
public ICommand FindUserCommand
{
    get { return _findUserCommand; }
    set { _findUserCommand = value; RaisePropertyChanged( () => FindUserCommand ); }
}

Command object definition:

public class FindUserCommand : ICommand
{
    private readonly IUserService _userService;

    public FindUserCommand(IUserService userService)
    {
        _userService = userService;
    }

    public bool CanExecute ( object parameter )
    {
        var findUserViewModel = ( FindUserViewModel )parameter;
        return findUserViewModel.NameSearch.Length > 0;
    }

    public void Execute ( object parameter )
    {
        FindUserViewModel.Users = _userService.Find( FindUserViewModel.NameSearch );
    }

    public event EventHandler CanExecuteChanged;
    public void RaiseCanExecuteChanged ()
    {
        if ( CanExecuteChanged != null )
            CanExecuteChanged( this, new EventArgs() );
    }
}
1

1 Answers

2
votes

Android widgets (like buttons) don't have Command and CommandParameter members by default - so it's not possible to use them "out of the box".

However, you can easily inherit from Button to produce MyButton and you can then provide Command and CommandParameter members if you want to - e.g. something like:

public class MyButton : Button
{
    public MyButton(Context c, IAttributeSet a) : base(c,a) 
    {
        Click += (s,e) => {
            if (Command == null) return;
            if (!Command.CanExecute(CommandParameter)) return;
            Command.Execute(CommandParameter);
        };
    }

    public ICommand Command { get;set; }
    public object CommandParameter { get;set; }
}

Using this, you can then use MyButton instead of Button in your axml

 <MyButton
     local:MvxBind="Command SearchCommand;CommandParameter ." />

For more on data-binding see https://github.com/MvvmCross/MvvmCross/wiki/Databinding


Alternatively, another way of doing this is to use a ValueConverter based approach - similar to the CommandParameter functionality supplied with MvvmCross - see http://slodge.blogspot.co.uk/2013/06/commandparameter-binding.html