BindingContext в элементах TabbedPage переопределяется - PullRequest
0 голосов
/ 10 марта 2020

У меня есть TabbedPage в моем приложении, и я хочу использовать отдельный [View + ViewModel] для каждой вкладки, и все вкладки должны быть созданы во время выполнения.

Это то, что я сделал так далеко:

CategoryItem.cs

using SQLite;
using System;
using System.Collections.Generic;
using System.Text;

namespace BlankApp1.Models
{
    public class CategoryItem
    {
        [PrimaryKey, AutoIncrement]
        public int ID { get; set; }
        public string ObjectId { get; set; }
        public string Name { get; set; }
        public long Position { get; set; }
        public long ParentId { get; set; }
        public string Description { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }

        public override string ToString()
        {
            return this.Name;
        }
    }
}

ProductsPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:prism="http://prismlibrary.com"
            xmlns:Views="clr-namespace:BlankApp1.Views"
            x:Class="BlankApp1.Views.ProductsPage"
            ItemsSource="{Binding CategoryList}"
            CurrentPageChanged="TabbedPage_CurrentPageChanged"
            Title="{Binding Title}">
    <TabbedPage.Behaviors>
        <prism:EventToCommandBehavior EventName="CurrentPageChanged"
                                  Command="{Binding FilterProductsByCategoryName}"/>
    </TabbedPage.Behaviors>
    <TabbedPage.ItemTemplate>
        <DataTemplate>
            <Views:CategoryTab Title="{Binding Name}"/>
        </DataTemplate>
    </TabbedPage.ItemTemplate>
</TabbedPage>

Я знаю, событие CurrentPageChanged используется дважды, но один из EventToCommand не работает.

ProductsPageViewModel.cs

using BlankApp1.Models;
using BlankApp1.Services;
using Prism;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BlankApp1.ViewModels
{
    public class ProductsPageViewModel : ViewModelBase, IPageDialogService
    {
        IPageDialogService PageDialogService { get; }
        private IDatabaseService _databaseService { get; }
        private IParseService _parseService { get; }

        public DelegateCommand FilterProductsByCategoryNameCommand { get; private set; }


        private ObservableCollection<CategoryItem> _categoryList;
        public ObservableCollection<CategoryItem> CategoryList
        {
            get => _categoryList;
            set => SetProperty(ref _categoryList, value);
        }

        private string _currentCategoryTab = "All";
        public string CurrentCategoryTab
        {
            get => _currentCategoryTab;
            set
            {
                if (SetProperty(ref _currentCategoryTab, value))
                {
                    FilterProductList();
                }
            }

        }

        public ProductsPageViewModel(INavigationService navigationService, IPageDialogService pageDialogService, IDatabaseService databaseService, IParseService parseService)
            : base(navigationService)
        {
            PageDialogService = pageDialogService;
            _databaseService = databaseService;
            _parseService = parseService;

            FilterProductsByCategoryNameCommand = new DelegateCommand(FilterProductsByCategoryName);

            Title = "Products";
        }

        private void FilterProductsByCategoryName()
        {
            // Can't get this to work
        }

        public override void Initialize(INavigationParameters parameters)
        {
            if (CategoryList == null || CategoryList.Count == 0)
            {
                CategoryList = new ObservableCollection<CategoryItem>(GetCategoryListAsync().GetAwaiter().GetResult());
            }


        }
    }
}

CategoryTab.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="http://prismlibrary.com"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="BlankApp1.Views.CategoryTab"
             Title="{Binding Title}"
             Appearing="ContentPage_Appearing">
    <ContentPage.Behaviors>
        <prism:EventToCommandBehavior EventName="Appearing"
                                      Command="{Binding AppearingCommand}"/>
    </ContentPage.Behaviors>
    <ContentPage.Content>
        <StackLayout>
            <Label Text="AAAAAAAAAAAAAA"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

CategoryTab.xaml.cs

using BlankApp1.ViewModels;
using Xamarin.Forms;

namespace BlankApp1.Views
{
    public partial class CategoryTab : ContentPage
    {
        public CategoryTab()
        {
            InitializeComponent();
            var vm = BindingContext as CategoryTabViewModel;
            vm.Title = Title;
        }

        private void ContentPage_Appearing(object sender, System.EventArgs e)
        {
            ContentPage contentPage = sender as ContentPage;
            var x = BindingContext as CategoryTabViewModel;
            x.Title = contentPage.Title;
        }
    }
}

CategoryTabViewModel.cs

using BlankApp1.Models;
using BlankApp1.Services;
using Prism;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;
using System;
using System.Collections.Generic;
using System.Linq;

namespace BlankApp1.ViewModels
{
    public class CategoryTabViewModel : ViewModelBase, IActiveAware
    {
        private IDatabaseService _databaseService { get; }
        public CategoryItem CategoryItem { get; set; }
        public DelegateCommand AppearingCommand { get; private set; }

        public event EventHandler IsActiveChanged;

        private bool _isActive;
        public bool IsActive {
            get { return _isActive; }
            set { SetProperty(ref _isActive, value, FilterProductsByCategoryName); }
        }

        public CategoryTabViewModel(INavigationService navigationService, IDatabaseService databaseService)
            : base(navigationService)
        {
            _databaseService = databaseService;
            AppearingCommand = new DelegateCommand(Appearing);

            CategoryItem = _databaseService.GetCategoryByNameAsync(Title).GetAwaiter().GetResult();

        }

        protected virtual void FilterProductsByCategoryName()
        {
            IsActiveChanged?.Invoke(this, EventArgs.Empty);
            //PageDialogService.DisplayAlertAsync("Test", "Some category is selected", "Cancel");
            var x = Title;
            var xx = 1;
        }

        private async void Appearing()
        {

        }


    }
}

Проблема в том, что при использовании ItemsSource в TabbedPage:

ItemsSource="{Binding CategoryList}"

BindingContext представления вкладок переопределяется и больше не равен экземпляру CategoryTabViewModel в вкладка «Просмотр»:

var x = BindingContext;

Возвращает объект CategoryItem, унаследованный от ItemsSource = "{Binding CategoryList}"

Моя проблема Это означает, что команды в Category.xaml не фиксируются в связанной с ним модели представления. Привязка также не работает.

Мой вопрос: как я могу передать объект CategoryItem в представление вкладок, и иметь возможность использовать привязку и отправлять команды в / с классом ViewModel ( CategoryTab.xaml + CategoryTabViewModel.xaml )

PS: все представления, ViewModels, службы, ... зарегистрированы в App.xaml.cs

Ответы [ 2 ]

0 голосов
/ 10 марта 2020

Я нашел решение для себя.

Итак, что я сделал, гарантируя использование тега <TabbedPage.ItemTemplate> для заполнения страниц вкладок, я сделал это: Извлечение ViewModel из TabbedPage, и отправьте ему объект TabbedPage (не удалось найти другой способ доступа к объекту xaml из BindingContext ViewModel):

public ProductsPage()
{
    InitializeComponent();
    ProductsPageViewModel vm = BindingContext as ProductsPageViewModel;
    vm.MainTabbedPage = tabbedPage1;
}

В классе ViewModel добавьте страницы вкладок, используя наш список:

foreach (CategoryItem item in CategoryList)
{
    MainTabbedPage.Children.Add( new CategoryTab(item) { Title = item.Name  });
}

А теперь в конструкторе Tab Page, получите CategoryItem и отправьте его в его ViewModel:

public CategoryTab(CategoryItem currentCategoryItem)
{
    InitializeComponent();
    var vm = BindingContext as CategoryTabViewModel;
    vm.CurrentCategoryItem = currentCategoryItem;
}

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

0 голосов
/ 10 марта 2020

Я думаю, что здесь есть проблема:

    public CategoryTab()
    {
        InitializeComponent();
        var vm = BindingContext as CategoryTabViewModel;
        vm.Title = Title;
    }

Измените ее на:

    CategoryTabViewModel vm; 
    public CategoryTab()
    {
        InitializeComponent();
        BindingContext = vm = new CategoryTabViewModel(...);
        vm.Title = Title;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...