c# - Xamarin Forms 中数据模板 View 单元格内的绑定(bind)上下文

标签 c# xamarin data-binding xamarin.forms binding

我在 ListView 中显示了一个自定义单元格。它应该有效,但我担心它有效但我不明白为什么。

让我为您列出所有内容,因为它有点复杂。

基本上,我在顶部显示一个带有搜索字段的联系人列表。我还有各种其他要显示的内容超出了这个问题的范围,但是为了清楚起见,您会在整个代码中看到它们,特别是在 xaml 和数据模板选择器中。

我使用不同类型的自定义单元格来显示我的联系人列表的每个部分(有一个标题单元格、一个搜索单元格等等)。

这里,ContactsPage 包含 ListView 和数据模板的声明。

<ContentPage>
    <ContentPage.Resources>
        <ResourceDictionary>


            <DataTemplate x:Key="HeaderTemplate">
                <ViewCell>
                    <StackLayout>
                        <local:HeaderView/>    
                    </StackLayout>
                </ViewCell>
            </DataTemplate>


            <DataTemplate x:Key="SearchTemplate">
                <local:SearchCell/>                 //<=== Important
            </DataTemplate>


            <DataTemplate x:Key="CategoryTemplate">
                <ViewCell
                    x:Name="CategoryCell">
                    <Label
                        Text="CategoryCell" ></Label>
                </ViewCell>
            </DataTemplate>


            <DataTemplate x:Key="SelectionTemplate">
                <ViewCell
                    x:Name="SelectionCell">
                    <Label
                        Text="Selection Cell" ></Label>
                </ViewCell>
            </DataTemplate>


            <DataTemplate x:Key="ContactTemplate">
                <ViewCell
                    x:Name="ContactCell">
                    <Label
                        Text="{Binding FirstName}" ></Label>
                </ViewCell>
            </DataTemplate>

            <local:ContactDataTemplateSelector x:Key="TemplateSelector"
                                              HeaderTemplate="{StaticResource HeaderTemplate}"
                                              SearchTemplate="{StaticResource SearchTemplate}"
                                              CategoryTemplate="{StaticResource CategoryTemplate}"
                                              SelectionTemplate="{StaticResource SelectionTemplate}"
                                              ContactTemplate="{StaticResource ContactTemplate}"/>
        </ResourceDictionary>
    </ContentPage.Resources>

你看我有各种数据模板,每个模板都有自己的用途。标题正在工作,其余的正在进行中,我关心的唯一搜索实现。从单元格到 View 模型,再到数据模板。

现在这只是资源,这是实际的页面 UI(只有相关代码)

<ContentPage.Content>
   ... Content of the page, including the actual listview
            <ListView 
                x:Name="ContactsListView"
                HasUnevenRows="True""
                ItemTemplate="{StaticResource TemplateSelector}"
                ItemsSource="{Binding ListSource}">
            </ListView>
    </ContentPage.Content>

让我在该 View 的 View 模型中带您了解其背后的逻辑之旅。 View 本身的代码隐藏不执行任何操作,这里是该联系人列表的 ViewModel。

public class ContactsViewModel : BaseViewModel, IContactsViewModel
    {
        readonly IContactsService _service;
        List<object> _listSource;

        public List<object> ListSource
        {
            get => _listSource;
            private set
            {
                _listSource = value; 
                OnPropertyChanged();
            }
        }

        public string CurrentText => "HelloX";             //<=== Important
        readonly ISearchViewModel _searchViewModel;
        readonly ICategoryFilterViewModel _categoryFilterViewModel;
        readonly ISelectionViewModel _selectionViewModel;

        public ContactsViewModel()
        {
            _service = new();

            HeaderViewModel = new HeaderViewModel();

            _searchViewModel = new();
            _categoryFilterViewModel = new();
            _selectionViewModel = new();

            ListSource = GenerateDefaultList();
        }

        public async Task LoadContacts()       //Called when UI appears.
        {
            await _service.LoadContacts();

            var list = GenerateDefaultList();
            list.AddRange(_service.Contacts);

            ListSource = list;
        }

        List<object> GenerateDefaultList()
        {
            return new List<object>()
            {
                HeaderViewModel,
                _searchViewModel,              //<===== important
                _categoryFilterViewModel,
                _selectionViewModel
            };
        }
    }

为了清楚起见,我包含了大部分代码;这里我想强调的重要部分是 ListSource 内部已经有一些 View 模型。我正在使用该源来填充我的 ListView ,对象的类型定义了我将使用的数据模板类型。这是在 DataTemplate 选择器中完成的,它在这里:

public class ContactDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate ContactTemplate { get; set; }
        public DataTemplate HeaderTemplate { get; set; }
        public DataTemplate SearchTemplate { get; set; }
        public DataTemplate CategoryTemplate { get; set; }
        public DataTemplate SelectionTemplate { get; set; }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {            
            switch (item)
            {
                case HeaderViewModel _:
                    return HeaderTemplate;
                case SearchViewModel _:
                    return SearchTemplate;            //<==== important
                case CategoryFilterViewModel _:
                    return CategoryTemplate;
                case SelectionViewModel _:
                    return SelectionTemplate;
                default:
                    return ContactTemplate;
            }
        }
    }

所以我确实有一个 SearchViewModel 的实例(唯一一个对我的问题很重要的实例),但它从未在任何地方说过搜索数据模板的 ViewCell 实际上使用了 SearchViewModel。我只是将它用作我的 if 语句的条件。

这里是数据模板中使用的搜索单元格(它本身是用数据模板选择器选择的)

<ViewCell x:Class="MYNAMESPACE.SearchCell">
    <AbsoluteLayout>
        <Frame>
            <StackLayout>
                <Entry
                    Placeholder="{Binding PlaceHolderText}"/>
                <Button
                    Text="{Binding CurrentText}"
                    Command="{Binding SearchCommand}"/>
            </StackLayout>
        </Frame>
    </AbsoluteLayout>
</ViewCell>

我在不冒混淆上下文的风险的情况下尽可能多地删除了内容。我知道这是一堵代码墙,但我相信如果您决定进行调查,它会很有用。

据我所知,我从不为我的自定义 ViewCell(搜索单元)提供绑定(bind)上下文。我在其中确实有绑定(bind),值得注意的是,我的工作示例是 CurrentText。我在 SearchViewModel

中将其作为文本
public class SearchViewModel : ISearchViewModel
    {
        public string CurrentText => "<TODO SEARCH>";     //<=== Important

        //NotifyPropertyChanged Implementation
    }

我在 ContactsViewModel 中有另一个 CurrentText,但在运行时显示的文本是来自 SearchViewModel 的文本。我看到的是“”而不是“HelloX”。这就是我要的。我只是不明白单元格如何/为什么使用我的 View 模型。

我只使用 viewmodel 来选择要显示的数据模板,没有将 viewmodel 设置为该数据模板或 viewcell 的绑定(bind)上下文。还是我?绑定(bind)上下文来自哪里?

最佳答案

感谢您提出详细的问题,我很确定我正在关注您所做的事情,希望这对您有所帮助:

如果没有另外指定,页面上每个元素的 BindingContext 都是基于其父元素设置的。我相信您可能已经看到,当您为页面设置 BindingContext 时,它会向下流动到该页面的所有元素,除非您显式覆盖它。 因此,对于列表,每个项目的 BindingContext 自动设置为其对应的 ItemsSource 对象。因此,当您从模板选择器返回 SearchTemplate 时,它​​的所有元素都将继承该项目的绑定(bind)上下文,在本例中是您在 GenerateDefaultList 期间创建并放入 ListSource 的 SearchViewModel 实例。

关于c# - Xamarin Forms 中数据模板 View 单元格内的绑定(bind)上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52702468/

相关文章:

c# - Xamarin iOS 向其他应用程序发送命令/事件

silverlight - 2个(或更多)相互依赖的组合框

ios - UITableViewCell 中的 UICollectionView

c# - Parallel.ForEach(...) 内的 System.Drawing 内存泄漏

c# - 使用列表的 ASP.Net 随机生成器

c# - 将 RouteData.Values 与操作参数结合起来

ios - 如何禁用 iPad 支持?

wpf - 在wpf中绑定(bind)颜色的R G B属性

grails 2.3 具有动态表单的一对多数据绑定(bind)

javascript - aspx页面关闭后如何清除 session ?