我正在使用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();
}
}
答案 0 :(得分:3)
由于目前Xamarin表单中没有相对绑定支持(请参阅this Xamarin Forms forums post for more info),因此您无法将命令绑定到Button
中的ListView
} DataTemplate
。 DataTemplate
中的任何绑定都会相对于列表中的当前项具有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。