如何将ReactiveCommand绑定到Xamarin.Forms ListView中的控件?

时间:2014-08-11 07:19:07

标签: xaml reactiveui xamarin.forms

我正在使用ReactiveUI,Xamarin.Forms和XAML。我试图用ListView实现一个简单的场景,其中每一行都有一个删除按钮。这是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>

如您所见,删除按钮已注册了Clicked处理程序。这有效,但它不像RxUI方式。这是背后的代码:

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

如何使用对RemovePlayer命令的声明性绑定替换此onDeleteClicked事件处理程序?我无法看到一个很好的方法,因为我选择将ListView绑定到ReactiveList<string>,所以如果我尝试Command="{Binding RemovePlayer}"它会失败,因为单元格绑定到一个字符串。

为了完整性,这里是我的观点模型:

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 个答案:

答案 0 :(得分:3)

由于目前Xamarin表单中没有相对绑定支持(请参阅this Xamarin Forms forums post for more info),因此您无法将命令绑定到Button中的ListView } DataTemplateDataTemplate中的任何绑定都会相对于列表中的当前项具有BindingContext - 在您的情况下,是一个简单的string。如果你的ListView被绑定到一个对象,让我们说Person,那么你的Button命令绑定仍然会失败,并出现 No的错误在对象RemovePlayer上找到命令Person

因此,在视图代码中实现Command就像你所做的那样是一种选择。另一个是使用C#DataTemplate(不是XAML)并在那里实现Command - 但这两者都是一样的。如果你喜欢保留你的观点 out 之类的内容并且只在你的视图模型中保存,那么这两种方法都不是很好的解决方案。但是在引入相对绑定支持之前,实际上还没有任何其他选择。

我遇到了与你完全相同的问题,但是我将ListView绑定到了一组对象。我的对象的类在一个单独的类库中,它只包含POCO,我不喜欢在我的一个POCO中实现Command的想法。

答案 1 :(得分:2)

棘手的一点是你的&#34; SelectedPlayer&#34;在ViewModel中没有公开,所以RxUI方式无法做到这一点。如果是,您可以执行以下操作:

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

如果您的Player对象本身是ViewModel和&#34; RemovePlayer&#34;在玩家本身,你可以做这个棘手的伎俩:

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

在这里,我们说,&#34;每次玩家列表发生变化时,我都想构建一个新的Observable:获取所有当前玩家的列表,然后选择它们进入一个Observable,当有人点击一个RemovePlayer按钮时触发 - 告诉我何时任何的新Observables开火#34;

答案 2 :(得分:0)

我对此的看法不是纯粹主义者:)

不是最好的设计模型,而是扩展您的对象以处理ICommand并绑定它..它是现在最好的解决方案。

如果你的对象在同一个程序集上,你可以使用部分对象,如果不同,你可以为你的poco创建一个小的viewmodel。