如何将列表项绑定到它是否包含在另一个集合中 [英] How to binding list item to whether it is contained in another collection

查看:17
本文介绍了如何将列表项绑定到它是否包含在另一个集合中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The scenario is that we have a list of items displayed in a list (ListView on Android and UITableView on iOS). The ItemSource comes from a web service. In addition to the data properties of the item, each of the items has the additional state that it can be added to a collection of currently selected items which is reflected in another collection on the view model.

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

I need to be able to alter the display of the item in the list based on whether it is in the currently selected list.

为绑定提供的数据上下文是来自 ItemsSource 的数据,它只是一个项目的原始数据,无法知道该数据是否是当前选定的项目之一.这必须通过询问 ViewModel 来完成.

The datacontext given for binding is the data that comes from the ItemsSource, which is jsut the raw data for an item and has no way of knowing if that data is one of the currently selected items. That has to be done by asking the ViewModel.

我正在努力寻找解决此问题的最佳方法.我当然不想修改 ItemsSource 为源中的每个项目添加一个伪造的属性.

I'm struggling to find the best solution to this problem. I certainly do not want to modify the ItemsSource to add a bogus property to each item in the source.

我可以通过覆盖 Adapter/TableViewSource 对其进行硬编码而不实际使用绑定来破解它,但这似乎是错误的.

I can hack something around it by overriding the Adapter/TableViewSource to hardcode this without actually using binding for this, but that seems wrong.

更新

那不是很好解释,所以让我尝试使用现有的 MvvmCross 示例作为基础来解释.

That was not explained very well, so let me try to explain using an existing MvvmCross example as the basis.

考虑来自 N=6 和 N=7 的书籍样本.我们想添加一个新功能,我们可以标记我们已经拥有的书籍,对于那些我们已经标记的书籍,它会显示一个指示器,告诉您您拥有这本书.

Consider the books sample from N=6 and N=7. We want to add a new feature where we can mark the books that we already own and for those that we have marked it shows an indicator to tell you that you own the book.

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

Imagine that the view model has methods like:

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

这些的实现对于本次讨论并不重要,但我可能还需要某种形式的事件处理程序来通知书的所有权何时发生变化.

The implementation of those is not important for this discussion, but I would probably also need some form of Event Handler to notify when ownership has changed for a book.

我想弄清楚如何在 ListViewItem/TableViewCell 上设置绑定,以便它可以使用 DataContext 中的 ID 查询 ViewModel 上的 IsOwned 方法以控制该项目的 UI 元素的可见性记录.

I am trying to figure out how I can set up a binding on the ListViewItem/TableViewCell so that it can query the IsOwned method on the ViewModel using the ID from the DataContext for the item to control visibility of a UI element in that record.

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

I'm guessing that I will have to create a custom source binding, but that binding has to reference the ViewModel and the DataContext so I am a little lost here.

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

Note that in my real problem I am trying to solve, the status of whether something is marked is temporary and local only to the ViewModel (if I exit the screen it is not remembered). This precludes the idea of having a Service for getting the information.

推荐答案

您的 UI 列表项单元格有一个 DataContext,它的控件绑定到该 DataContext - 该 DataContext 是单元格自己的单个 ViewModel.

Your UI list item cell has a DataContext that its controls bind to - that DataContext is the cell's own individual ViewModel.

MvvmCross 不允许(从今天的 v3.1 开始)让您查看该 DataContext 之外的内容 - 它不允许您(例如)检查另一个 UIElement 的 DataContext 或不允许您请求父级.

MvvmCross doesn't (as of v3.1 today) let you look outside that DataContext - it doesn't let you (for example) inspect another UIElement's DataContext or doesn't let you ask for a Parent.

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

To work within this, I generally create my DataContext for my list item cell's so that they contain all the data and actions that those cells need - so that they are genuinely the ViewModel for the list item view. Where there is some convenient Model class to reuse, then this will often mean that I create a wrapper class to assist with the DataContext. Occasionally (rarely) I will do this in a ValueConverter (see below)*, but in general I will do this using wrapper classes exposed by the ViewModel instead.

例如,对于像您这样的情况,如果我有一个 List 作为我的核心模型,那么在页面级别的 ViewModel 我将创建一个 FooWithSelectionState班级:

For example, for something like your case, if I had a List<Foo> as my core model, then in the page level ViewModel I would create a FooWithSelectionState class:

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

这将允许我的 ViewModel 将 List 作为属性公开 - 然后单个列表项单元格可以绑定到 IsSelected 以及链接的属性像 Foo.Name.

This would then allow my ViewModel to expose a List<FooWithSelectedState> as a Property - and individual list item cells can then bind to IsSelected as well as to chained properties of the underlying object like Foo.Name.

在体系结构上,这种模式是健壮的、可测试的、干净的并且是正确的",因为每个列表项单元格都有自己定义良好的 ViewModel.但是,我确实理解它可能会感觉有点代码繁重 - 感觉有点矫枉过正.在某些情况下,开发人员只想直接使用 Model 类而无需 ViewModel 步骤.也许在 Mvx 的某个未来版本中,然后 MvvmCross 可能会添加一些替代机制,以允许 DataBound UI 元素在它们自己的 DataContext 之外查看——这已在例如https://github.com/MvvmCross/MvvmCross/issues/35 - 但今天这个不存在(我知道)

Architecturally, this pattern is robust, it's testable, it's clean and it's "correct" in that each list item cell has its own well-defined ViewModel. However, I do understand it can feel a bit code-heavy - it can feel a bit overkill. In some cases, dev's just want to use the Model classes directly without a ViewModel step. Maybe in some future version of Mvx, then MvvmCross might add some alternative mechanism to allow DataBound UI elements to look outside of their own DataContext - this has been suggested in e.g. https://github.com/MvvmCross/MvvmCross/issues/35 - but today this doesn't exist (that I know of)

如果您确实想使用 ValueConverter 路由,那么一种方法是在 ItemsSource 上使用 ValueConverter - 例如,类似于:

If you did want to use the ValueConverter route, then one way of doing this is to use a ValueConverter on the ItemsSource - for example, something like:

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();
     }
}

这可用于与诸如 ItemsSource FooWrapping(SourceList, .) 之类的表达式绑定 - 但在动态情况下(列表频繁更改)使用它时要小心,因为这可能会很慢.

This could be used in binding with an expression like ItemsSource FooWrapping(SourceList, .) - but be careful using this in dynamic situations (where the list changes frequently) as this could be slow.

这篇关于如何将列表项绑定到它是否包含在另一个集合中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆