【问题标题】:How to bind a ReactiveCommand to a control in a Xamarin.Forms ListView?如何将 ReactiveCommand 绑定到 Xamarin.Forms ListView 中的控件?
【发布时间】:2014-10-03 22:56:34
【问题描述】:

我正在使用 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);                
    }

如何将这个 onDeleteClicked 事件处理程序替换为与我的 RemovePlayer 命令的声明性绑定?我看不到一个好的方法,因为我选择将 ListView 绑定到ReactiveList&lt;string&gt;,所以如果我尝试做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();            
    }
}

【问题讨论】:

  • 我也有同样的问题。如何在 reactui 7.0 中实现这一点? ReactiveCommand.Execute() 不存在了吗?我应该只使用 reglar xamarin forms 命令来处理相对绑定情况吗?

标签: xaml reactiveui xamarin.forms


【解决方案1】:

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

因此,像您所做的那样在视图代码中实现Command 是一种选择。另一个是使用 C# DataTemplate(不是 XAML 的)并在那里实现 Command - 但两者都是一回事。如果您希望将此类内容排除并仅保留在您的视图模型中,这两者都不是一个很好的解决方案;但在引入相对绑定支持之前,实际上没有任何其他选项。

我遇到了与您完全相同的问题,但我将 ListView 绑定到对象集合。我的对象的类位于一个单独的类库中,其中只有 POCO,我不喜欢在我的一个 POCO 中实现 Command 的想法。

【讨论】:

    【解决方案2】:

    棘手的一点是您的“SelectedPlayer”没有暴露在您的 ViewModel 中,因此无法通过 RxUI 方式来执行此操作。如果是,您可以执行以下操作:

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

    如果您的 Player 对象本身是一个 ViewModel 并且“RemovePlayer”在 Player 本身上,您可以执行此 Tricky Trick:

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

    在这里,我们说,“每次玩家列表发生变化时,我都想构建一个新的 Observable:获取所有当前玩家的列表,然后将它们选择到一个触发的 Observable当有人点击 RemovePlayer 按钮时 - 告诉我任何这些新 Observable 何时触发”

    【讨论】:

    • 现在只关注答案的第一部分:我很高兴将 SelectedPlayer 添加到我的视图模型中,但这有什么帮助?具体来说: 1. 我的 RemovePlayer 命令如何被触发? 2. 当点击删除按钮时,我的 SelectedPlayer 如何在我的 ViewModel 上得到更新?您是否假设删除按钮仅在所选行上可见?
    • 糟糕,我误读了代码,我以为只有一个 RemovePlayer 按钮,而不是每个项目一个。你可以只使用普通的 Event hook 方法,或者在这种情况下我会让 Player 成为 ViewModel
    【解决方案3】:

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

    不是最好的设计模型,但扩展您的对象以处理 ICommand 并绑定到它.. 这是目前 imo 的最佳解决方案。

    如果您的对象在同一个程序集中,您可以使用 partial's,如果不同,您可以为 poco 创建小型视图模型。

    【讨论】:

      猜你喜欢
      • 2018-05-22
      • 2017-02-16
      • 1970-01-01
      • 2021-04-20
      • 2014-09-26
      • 1970-01-01
      • 2014-06-17
      • 2018-08-05
      • 1970-01-01
      相关资源
      最近更新 更多