TabbedPage 的项目中的 BindingContext 被覆盖
BindingContext in TabbedPage's Items is overrided
我的应用程序中有一个 TabbedPage,我想为每个 Tab 使用单独的 [View + ViewModel],并且所有 Tab 都应该在运行时创建。
这是我到目前为止所做的:
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()
{
}
}
}
问题是在 TabbedPage 中使用 ItemsSource :
ItemsSource="{Binding CategoryList}"
选项卡视图的 BindingContext 被覆盖,不再等于视图选项卡中 CategoryTabViewModel 的实例:
var x = BindingContext;
Returns 一个继承自 ItemsSource="{Binding CategoryList}"
的 CategoryItem 对象
我的问题是,Category.xaml 中的命令未在与其关联的 ViewModel 中捕获。绑定也不起作用。
我的问题:如何将 CategoryItem 对象传递给选项卡视图,并能够使用绑定并将命令发送到/与 ViewModel class ( CategoryTab.xaml + CategoryTabViewModel.xaml)
P.S。 : 所有视图、视图模型、服务...都在 App.xaml.cs
中注册
我觉得这里有问题:
public CategoryTab()
{
InitializeComponent();
var vm = BindingContext as CategoryTabViewModel;
vm.Title = Title;
}
改为:
CategoryTabViewModel vm;
public CategoryTab()
{
InitializeComponent();
BindingContext = vm = new CategoryTabViewModel(...);
vm.Title = Title;
}
我自己找到了解决方案。
所以我所做的是,而不是使用标签 <TabbedPage.ItemTemplate>
来填充标签页,我这样做了:
检索 TabbedPage 的 ViewModel,并将 TabbedPage 对象发送给它(找不到从 BindingContext ViewModel 访问 xaml 对象的另一种方法):
public ProductsPage()
{
InitializeComponent();
ProductsPageViewModel vm = BindingContext as ProductsPageViewModel;
vm.MainTabbedPage = tabbedPage1;
}
在 ViewModel class 中,使用我们的列表添加标签页:
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 仅检索该类别中的产品。
我的应用程序中有一个 TabbedPage,我想为每个 Tab 使用单独的 [View + ViewModel],并且所有 Tab 都应该在运行时创建。
这是我到目前为止所做的:
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()
{
}
}
}
问题是在 TabbedPage 中使用 ItemsSource :
ItemsSource="{Binding CategoryList}"
选项卡视图的 BindingContext 被覆盖,不再等于视图选项卡中 CategoryTabViewModel 的实例:
var x = BindingContext;
Returns 一个继承自 ItemsSource="{Binding CategoryList}"
的 CategoryItem 对象我的问题是,Category.xaml 中的命令未在与其关联的 ViewModel 中捕获。绑定也不起作用。
我的问题:如何将 CategoryItem 对象传递给选项卡视图,并能够使用绑定并将命令发送到/与 ViewModel class ( CategoryTab.xaml + CategoryTabViewModel.xaml)
P.S。 : 所有视图、视图模型、服务...都在 App.xaml.cs
中注册我觉得这里有问题:
public CategoryTab()
{
InitializeComponent();
var vm = BindingContext as CategoryTabViewModel;
vm.Title = Title;
}
改为:
CategoryTabViewModel vm;
public CategoryTab()
{
InitializeComponent();
BindingContext = vm = new CategoryTabViewModel(...);
vm.Title = Title;
}
我自己找到了解决方案。
所以我所做的是,而不是使用标签 <TabbedPage.ItemTemplate>
来填充标签页,我这样做了:
检索 TabbedPage 的 ViewModel,并将 TabbedPage 对象发送给它(找不到从 BindingContext ViewModel 访问 xaml 对象的另一种方法):
public ProductsPage()
{
InitializeComponent();
ProductsPageViewModel vm = BindingContext as ProductsPageViewModel;
vm.MainTabbedPage = tabbedPage1;
}
在 ViewModel class 中,使用我们的列表添加标签页:
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 仅检索该类别中的产品。