Связывающий контекст внутри ячейки представления шаблона данных в формах Xamarin - PullRequest
0 голосов
/ 08 октября 2018

У меня есть пользовательская ячейка, отображаемая в виде списка.Он должен не работать, но я обеспокоен тем, что он работает, и я не понимаю, почему.

Позвольте мне изложить вам кое-что, потому что это немного сложно.

По сути, я отображаю список контактов с полем поиска вверху.У меня есть несколько других вещей, которые могут быть показаны за рамками этого вопроса, но для ясности вы увидите их в коде, особенно в xaml и в селекторе шаблонов данных.

Я использую разныетипы пользовательских ячеек для отображения каждой части моего списка контактов (есть ячейка заголовка, ячейка поиска и некоторые другие).

Здесь ContactsPage содержит представление списка и объявление таблицы данных.

<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>

Видите ли, у меня есть различные шаблоны данных, каждый для своего собственного использования.Заголовок работает, остальное находится в процессе выполнения, единственное, что меня волнует - это реализация Search .От ячейки до модели представления и шаблона данных.

Теперь это были только ресурсы, вот фактический пользовательский интерфейс страницы (только соответствующий код)

<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>

Позвольте мне взять васчерез путешествие логики позади этого, в модели представления этого взгляда.Код позади самого представления ничего не делает, и вот 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 уже имеет несколько моделей представления внутри него.Я использую этот источник для заполнения моего списка, и тип объекта определяет, какой тип шаблона данных я буду использовать.Это делается в селекторе 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 (поисковой ячейки).У меня есть привязки внутри, в частности, мой рабочий пример - CurrentText.У меня есть это как текст в моем SearchViewModel

public class SearchViewModel : ISearchViewModel
    {
        public string CurrentText => "<TODO SEARCH>";     //<=== Important

        //NotifyPropertyChanged Implementation
    }

У меня есть другой CurrentText в ContactsViewModel, но текст, который отображается во время выполнения, тот из SearchViewModel.Я вижу "", а не "HelloX".Это то, что я хочу.Я просто не понимаю, как / почему в ячейке используется моя viewmodel.

Я использую только viewmodel, чтобы выбрать, какой шаблон данных отображать, нигде эта viewmodel не задается в качестве связующего контекста этого шаблона данных, ни viewcell.,Или я?Откуда берется контекст привязки?

1 Ответ

0 голосов
/ 08 октября 2018

Спасибо за подробный вопрос, я почти уверен, что слежу за тем, что вы сделали, поэтому, надеюсь, это поможет:

BindingContext каждого элемента на странице устанавливается на основе родительского элемента,если не указано иное.Я уверен, что вы, вероятно, видели, что когда вы устанавливаете BindingContext для страницы, он перетекает вниз на все элементы этой страницы, если вы не переопределите его явно.Таким образом, для списков BindingContext каждого элемента автоматически присваивается соответствующему объекту ItemsSource.Поэтому, когда вы возвращаете SearchTemplate из вашего селектора шаблона, все его элементы будут наследовать контекст привязки этого элемента, который в данном случае является экземпляром SearchViewModel, который вы создали и поместили в ListSource во время GenerateDefaultList.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...