How do I bind a ReactiveCommand to a control in a Xamarin.Forms ListView?

I am using ReactiveUI, Xamarin.Forms and XAML. I am trying to implement a simple scenario with a ListView where each row has a delete button. Here is the ListView XAML:

<ListView x:Name="playerListView" ItemsSource="{Binding Players}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout Orientation="Horizontal" Padding="20, 5, 20, 5">
                    <Label Text="{Binding .}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
                    <Button x:Name="deleteButton" Text="Delete" Clicked="onDeleteClicked"  VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand" />
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

      

As you can see, the delete button has a registered Clicked handler. It works, but it doesn't seem like the RxUI way. Here is the code behind:

    private void onDeleteClicked(object sender, EventArgs e)
    {
        var button = (Button)sender;            
        this.ViewModel.RemovePlayer.Execute(button.BindingContext);                
    }

      

How do I replace this onDeleteClicked event handler with a declarative binding with my RemovePlayer command? I don't see a good way to do this because I decided to bind the ListView to ReactiveList<string>

, so if I try to execute Command="{Binding RemovePlayer}"

it fails because the cell is row bound.

For completeness, my model is presented here:

public class NewGameViewModel : ReactiveObject
{
    public ReactiveList<string> Players { get; private set; }
    public ReactiveCommand<Object> AddPlayer { get; private set; }
    public ReactiveCommand<Object> RemovePlayer { get; private set; }
    public ReactiveCommand<Object> StartGame { get; private set; }
    public ReactiveCommand<Object> RandomizeOrder { get; private set; }

    string newPlayerName;
    public string NewPlayerName {
        get { return newPlayerName; }
        set { this.RaiseAndSetIfChanged(ref newPlayerName, value); }
    }

    public NewGameViewModel()
    {
        Players = new ReactiveList<string> ();

        var canStart = this.Players.CountChanged.Select(count => count >= 3);
        StartGame = canStart.ToCommand();
        RandomizeOrder = canStart.ToCommand();          

        AddPlayer = this.WhenAnyValue(x => x.Players.Count, x => x.NewPlayerName, 
            (count, newPlayerName) => count < 7 && !string.IsNullOrWhiteSpace(newPlayerName) && !this.Players.Contains(newPlayerName))
            .ToCommand();

        RemovePlayer = ReactiveCommand.Create();            
    }
}

      

+3


source to share


3 answers


Since there is no support for relative binding in Xamarin Forms at this time (see this Xamarin Forms forums forum for more information ), you won’t be able to bind a command to yours Button

in your ListView

DataTemplate

. Any binding in this DataTemplate

will be BindingContext

relative to the current item in the list - simple in your case string

. If yours ListView

was bound to an object say a Person

then command binding Button

will still fail with error something along the No Command lines RemovePlayer

found on the objectPerson

So implementing Command

in code like what you did is one option. Another uses C # DataTemplate

(not XAML) and implements there Command

, but both are the same. None of these are great solutions if you like to keep things like that out of your views and only in view models; but until support for relative binding is provided, there are really no other options.



I faced the same problem as you, but I bound mine ListView

to a collection of objects. The class for my object was in a separate class library that ONLY had a POCO and I didn't like the idea to implement Command

in one of my POCOs.

+3


source


The tricky bit is that your "SelectedPlayer" is not visible in your ViewModel, so there is no way to do it in RxUI way. If so, you can do something like:

RemovePlayer.Select(_ => SelectedPlayer).Subscribe(x => {
    SelectedPlayer = null;
    Players.Remove(x);
});

      

If your Player object was itself a ViewModel and "RemovePlayer" was on the Player itself, you can do this Tricky Trick:



Players.Changed.StartWith(null)
    .Select(_ => Players
        .Select(x => x.RemovePlayer.Select(__ => x))
        .Merge())
    .Switch()
    .Subscribe(x => Players.Remove(x));

      

Here we say, "Every time the player list changes, I want to create a new Observable: Take a list of all current players and select them in Observable, which fires when someone presses the RemovePlayer button - tell me when any of these new lights Observers "

+2


source


My opinion on this is not a purist :)

Not the best design model, but extend your object to handle ICommand and bind to it. This is the best solution at the moment imo.

If your objects are in the same assembly you can use partial, if they are different you can create a small view model for your poco.

0


source







All Articles