如何将列表项绑定到是否包含在另一个集合中

时间:2014-03-27 17:33:22

标签: listview xamarin mvvmcross

场景是我们有列表中显示的项目列表(Android上的ListView和iOS上的UITableView)。 ItemSource来自Web服务。除了项目的数据属性之外,每个项目都具有附加状态,可以将其添加到当前所选项目的集合中,这些项目将反映在视图模型上的另一个集合中。

我需要能够根据它是否在当前选择的列表中来更改列表中项目的显示。

为绑定提供的datacontext是来自ItemsSource的数据,它是项目的原始数据,并且无法知道该数据是否是当前所选项目之一。这必须通过询问ViewModel来完成。

我很难找到解决这个问题的最佳方法。我当然不想修改ItemsSource来为源中的每个项添加一个伪属性。

我可以通过覆盖Adapter / TableViewSource来硬件解码它,而不是实际使用绑定,但这似乎是错误的。

更新

没有解释得那么好,所以让我尝试用现有的MvvmCross示例作为基础进行解释。

考虑N = 6和N = 7的书籍样本。我们想要添加一个新功能,我们可以在其中标记我们已经拥有的书籍,并且我们标记的书籍会显示一个指示符,告诉您自己拥有该书。

想象一下,视图模型的方法如下:

public bool IsOwned(string id)
public void SetOwned(string id, bool owned)

这些讨论的实施并不重要,但我可能还需要某种形式的事件处理程序来通知书籍的所有权发生变化。

我试图找出如何在ListViewItem / TableViewCell上设置绑定,以便它可以使用DataContext中的ID查询ViewModel上的IsOwned方法,以控制UI元素的可见性。记录。

我猜我必须创建一个自定义源绑定,但该绑定必须引用ViewModel和DataContext,所以我在这里有点迷失。

请注意,在我试图解决的实际问题中,是否标记了某些内容的状态是临时的,只有ViewModel是本地的(如果我退出屏幕则不会被记住)。这就排除了获取服务以获取信息的想法。

1 个答案:

答案 0 :(得分:2)

您的UI列表项单元格具有其控件绑定到的DataContext - DataContext是单元格自己的ViewModel。

MvvmCross没有(从今天的v3.1开始)让你向外看看DataContext - 它不会让你(例如)检查另一个UIElement的DataContext或者没有让你问一位家长。

要在此工作,我通常会为列表项单元格创建我的DataContext,以便它们包含这些单元格所需的所有数据和操作 - 这样它们就是列表项视图的ViewModel。如果有一些方便的Model类可以重用,那么这通常意味着我创建了一个包装类来协助DataContext。偶尔(很少)我会在ValueConverter中执行此操作(见下文)*,但一般情况下我会使用ViewModel公开的包装类来执行此操作。

例如,对于类似你的情况,如果我有一个List<Foo>作为我的核心模型,那么在页面级ViewModel中我会创建一个FooWithSelectionState类:

public class FooWithSelectionState : MvxNotifyPropertyChanged
{
    public Foo Foo { get; set; /* normal INPC in here */ }
    public bool IsSelected { get; set; /* normal INPC in here */ }
}

然后,这将允许我的ViewModel将List<FooWithSelectedState>作为属性公开 - 然后单个列表项单元格可以绑定到IsSelected以及基础对象的链接属性,如{{1} }。

在建筑方面,这种模式很健壮,它是可测试的,它很干净,并且它是正确的。&#34;因为每个列表项单元格都有自己明确定义的ViewModel。但是,我确实理解它可能会感觉有点代码 - 它可能会感觉有点矫枉过正。在某些情况下,开发人员只想在没有ViewModel步骤的情况下直接使用Model类。也许在Mvx的某个未来版本中,MvvmCross可能会添加一些替代机制来允许DataBound UI元素在自己的DataContext之外查看 - 这已在例如https://github.com/MvvmCross/MvvmCross/issues/35 - 但今天这不存在(我知道)


如果您确实想使用ValueConverter路由,那么执行此操作的一种方法是在ItemsSource上使用ValueConverter - 例如:

Foo.Name

这可以用于与public class FooWrappingValueConverter : MvxValueConverter<IList<Foo>, IList<FooWithSelectionState>> { protected override IList<FooWithSelectionState> Convert(IList<Foo> value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var viewModel = parameter as MyViewModel; return value.Select(f => new FooWithSelectionState() { Foo = f, IsSelected = viewModel.IsSelected(f) }) .ToList(); } } 这样的表达式绑定 - 但要小心在动态情况下使用它(列表经常更改),因为这可能很慢。