提问人:dpTech 提问时间:11/4/2023 最后编辑:dpTech 更新时间:11/8/2023 访问量:143
.net MAUI DataTemplate,其 CollectionView 在其 ItemTemplate 中包含 <ContentPresenter />,“未检测到已安装的组件”
.net MAUI DataTemplate with a CollectionView that contains <ContentPresenter /> in its ItemTemplate, "No installed components were detected"
问:
我们有许多显示模型列表的视图,在每个视图中,点击项目会显示一个操作表。
一切正常,但我们希望利用 ControlTemplate 来确保视图之间的一致性。第一次尝试似乎有效,但事实证明它之所以有效,是因为 ItemsSource 中只有一个项目。一旦 ItemsSource 包含多个项,就会引发“未检测到已安装的组件”异常。第一项仍然被适当地呈现,点击它按预期运行。
以下是正在播放的文件:
ListViewsTemplate.xaml(控件模板)
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiIssues.Controls.ListViewsTemplate"
ControlTemplate="{DynamicResource ContainerTemplate}"
x:Name="this">
<ContentView.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="ContainerTemplate">
<Frame BindingContext="{x:Reference this}" Padding="10, 0, 10, 0">
<CollectionView ItemsSource="{TemplateBinding ItemsSource}">
<CollectionView.ItemsLayout><LinearItemsLayout Orientation="Vertical" /></CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame>
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{TemplateBinding ItemTapped}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
<ContentPresenter />
</Frame>
<Rectangle IsVisible="{OnPlatform false, Android=True}" HeightRequest="2" Fill="Black"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Frame>
</ControlTemplate>
</ResourceDictionary>
</ContentView.Resources>
ListViewsTemplate.xaml.cs(控件模板)
using System.Collections;
using System.Windows.Input;
namespace MauiIssues.Controls
{
public partial class ListViewsTemplate : ContentView
{
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(ListViewsTemplate), defaultBindingMode: BindingMode.TwoWay);
public ICommand ItemTapped
{
get => (ICommand)GetValue(ItemTappedProperty);
set => SetValue(ItemTappedProperty, value);
}
public static readonly BindableProperty ItemTappedProperty =
BindableProperty.Create(nameof(ItemTapped), typeof(ICommand), typeof(ListViewsTemplate), defaultBindingMode: BindingMode.TwoWay);
public ListViewsTemplate() { InitializeComponent(); }
}
}
ListViewsTemplate.xaml(内容页)
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:MauiIssues.Models"
xmlns:uc="clr-namespace:MauiIssues.Controls"
xmlns:viewmodel="clr-namespace:MauiIssues.ViewModels"
x:Class="MauiIssues.Views.ListViewsTemplate"
x:DataType="viewmodel:ListViewsTemplateViewModel">
<uc:ListViewsTemplate ItemsSource="{Binding Items}" ItemTapped="{Binding ItemTappedCommand}" VerticalOptions="StartAndExpand" >
<Label BindingContext="{Binding Source={RelativeSource AncestorType={x:Type model:ListViewsTemplateModel}}}"
x:DataType="{x:Type model:ListViewsTemplateModel}"
Text="{Binding Name}" />
</uc:ListViewsTemplate>
</ContentPage>
ListViewsTemplate.xaml.cs(内容页)
using MauiIssues.ViewModels;
namespace MauiIssues.Views
{
public partial class ListViewsTemplate : ContentPage
{
public ListViewsTemplateViewModel ViewModel { get; }
public ListViewsTemplate()
{
base.BindingContext = this.ViewModel = new ListViewsTemplateViewModel();
InitializeComponent();
}
protected override async void OnAppearing()
{
await this.ViewModel.OnAppearing();
base.OnAppearing();
}
}
}
ListViewsTemplateViewModel.cs(视图模型)
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MauiIssues.Models;
using System.Collections.ObjectModel;
namespace MauiIssues.ViewModels
{
public partial class ListViewsTemplateViewModel : ObservableObject
{
public ObservableCollection<ListViewsTemplateModel> Items { get; } = new ();
[RelayCommand]
async Task ItemTapped(ListViewsTemplateModel item)
{
await App.Current.MainPage.DisplayAlert("tapped item", item.Name, "OK");
}
public async Task OnAppearing()
{
this.Items.Add(new ListViewsTemplateModel() { Name = $"Item {this.Items.Count + 1}" });
await Task.CompletedTask;
}
}
}
当您第一次导航到视图时,一切都很好。当您离开并返回视图时,您将获得:
未检测到已安装的组件。
在 System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (Int32 错误代码)
在 WinRT.DelegateExtensions.DynamicInvokeAbi (Delegate del, Object[] invoke_params)
在 ABI。System.Collections.Generic.IVectorMethods1.Add(IObjectReference 对象,T 项)
在 Microsoft.UI.Xaml.Controls.UIElementCollection.Add(UIElement 项)
在 Microsoft.Maui.Handlers.ContentViewHandler.UpdateContent(IContentViewHandler 处理程序)
在 Microsoft.Maui.Handlers.ContentViewHandler.MapContent(IContentViewHandler 处理程序,IContentView 页)
在 Microsoft.Maui.Controls.Element.OnPropertyChanged(字符串属性名)
在 Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty 属性、BindablePropertyContext 上下文、对象值、布尔当前应用、SetValueFlags 属性、布尔静默)
在 Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty 属性、对象值、SetValueFlags 属性、SetValuePrivateFlags privateAttributes)
在 Microsoft.Maui.Controls.BindingExpression.ApplyCore(对象源对象、 绑定对象目标、 绑定属性属性、 布尔值 fromTarget)
在 Microsoft.Maui.Controls.BindingExpression.Apply(对象源对象、 BindableObject 目标、 BindableProperty 属性)
在 Microsoft.Maui.Controls.Binding.d__27.MoveNext()
在 System.Threading.Tasks.Task.<>c.b__128_0 (对象状态)
在 Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.b__0()
--- 从上一个位置---结束堆栈跟踪
at WinRT.ExceptionHelpers.g__Throw|20_0(Int32 小时)
在 ABI。Windows.ApplicationModel.Core.IUnhandledErrorMethods.Propagate (IObjectReference _obj)
在 Windows.ApplicationModel.Core.UnhandledError.Propagate()
在 Microsoft.AppCenter.Utils.ApplicationLifecycleHelperWinUI.<.ctor>b__0_3 (对象发送者,UnhandledErrorDetectedEventArgs eventArgs)
--- 从上一个位置---结束堆栈跟踪
在 Microsoft.AppCenter.Utils.ApplicationLifecycleHelperWinUI.<.ctor>b__0_3 (对象发送者,UnhandledErrorDetectedEventArgs eventArgs)
在 WinRT.EventSource__EventHandler1.Do_Abi_Invoke[TAbi](Void* thisPtr、IntPtr 发送器、TAbi 参数)1.Append(IObjectReference obj, T value) at ABI.System.Collections.Generic.IListMethods
1.EventState.<GetEventInvoke>b__1_0(Object obj, T e) at ABI.System.EventHandler
更新:
为了更清楚起见,它试图实现的功能与母版页在 asp.net 中执行的操作相同。
页面大师
<body>
<div class="main">
<asp:ContentPlaceHolder ID="FilterContent" runat="server" />
<asp:Button ID="btnSearch" runat="server" Text="Search" />
<div class="filterseparator" />
<asp:ContentPlaceHolder ID="GridContent" runat="server" />
<div class="gridseparator" />
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</body>
在这里,每个网页都使用母版页,然后将自己的控件放在 ContentPlaceHolders 中,据我们了解,这是 ContentPresenter 的用途。
答:
请勿将标签放入:uc:ListViewsTemplate
<uc:ListViewsTemplate ItemsSource="{Binding Items}" ItemTapped="{Binding ItemTappedCommand}" VerticalOptions="StartAndExpand" >
<!--<Label BindingContext="{Binding Source={RelativeSource AncestorType={x:Type model:ListViewsTemplateModel}}}"
x:DataType="{x:Type model:ListViewsTemplateModel}"
Text="{Binding Name}" />-->
</uc:ListViewsTemplate>
因为这样一来,您创建的 ContentView(ListViewsTemplate) 将失去其意义。所以,把它放进去:ListViewsTemplate
.....
<DataTemplate>
<Grid>
<Frame>
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{TemplateBinding ItemTapped}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
<!-- Put it here or anywhere else you like -->
<Label x:DataType="{x:Type model:ListViewsTemplateModel}"
Text="{Binding Name}" />
</Frame>
<Rectangle IsVisible="{OnPlatform false, Android=True}" HeightRequest="2" Fill="Black"/>
</Grid>
</DataTemplate>
.....
我测试了它,效果很好。
更新
<DataTemplate>
<Grid>
<Frame>
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{TemplateBinding ItemTapped}" CommandParameter="{Binding .}"/>
</Frame.GestureRecognizers>
<!-- use stacklayout -->
<StackLayout>
<ContentPresenter/>
<Label x:DataType="{x:Type model:ListViewsTemplateModel}"
Text="{Binding Name}"/>
</StackLayout>
</Frame>
<Rectangle IsVisible="{OnPlatform false, Android=True}" HeightRequest="2" Fill="Black"/>
</Grid>
</DataTemplate>
评论
StackLayout
<ContentPresenter/>
<StackLayout>
<CollectionView ItemsSource="{TemplateBinding ItemsSource}">
解决方案 1
删除 : 中的标签:uc:ListViewsTemplate
<uc:ListViewsTemplate ItemsSource="{Binding Items}" ItemTapped="{Binding ItemTappedCommand}" VerticalOptions="StartAndExpand" >
<!--<Label BindingContext="{Binding Source={RelativeSource AncestorType={x:Type model:ListViewsTemplateModel}}}"
x:DataType="{x:Type model:ListViewsTemplateModel}"
Text="{Binding Name}" />-->
</uc:ListViewsTemplate>
在 ListViewsTemplate.xaml 中:
.....
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Frame Grid.Row="0">
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{TemplateBinding ItemTapped}"
CommandParameter="{Binding .}"/>
</Frame.GestureRecognizers>
<StackLayout>
<Label x:DataType="{x:Type model:ListViewsTemplateModel}"
Text="{Binding Name}"/>
</StackLayout>
</Frame>
<Rectangle Grid.Row="1" IsVisible="{OnPlatform false, Android=True}" HeightRequest="2" Fill="Black"/>
</Grid>
</DataTemplate>
.....
这是效果。
解决方案 2创建一个 ContentPage 并将以下代码放入其中:
.....
<ContentPage.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="ContainerTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Frame Grid.Row="0">
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{TemplateBinding Path=Parent.BindingContext.ItemTappedCommand}"
CommandParameter="{Binding Source={RelativeSource AncestorType={x:Type model:ListViewsTemplateModel}}}"/>
</Frame.GestureRecognizers>
<ContentPresenter/>
</Frame>
<Rectangle Grid.Row="1" IsVisible="{OnPlatform false, Android=True}" HeightRequest="2" Fill="Black"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<Frame Padding="10, 0, 10, 0">
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView ControlTemplate="{StaticResource ContainerTemplate}">
<Label x:DataType="{x:Type model:ListViewsTemplateModel}" Text="{Binding Name}"/>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Frame>
.....
.xaml.cs:
public partial class NewPage1 : ContentPage
{
public ListViewsTemplateViewModel ViewModel { get; }
public NewPage1()
{
base.BindingContext = this.ViewModel = new ListViewsTemplateViewModel();
InitializeComponent();
}
protected override async void OnAppearing()
{
await this.ViewModel.OnAppearing();
base.OnAppearing();
}
}
还可以将 ContentPage.Resources 的代码放入 App.xaml 中:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml"/>
<ResourceDictionary Source="Resources/Styles/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- you can also put codes of ContentPage.Resources into here -->
<ControlTemplate x:Key="ContainerTemplate">
......
</ControlTemplate>
</ResourceDictionary>
</Application.Resources>
这是效果。
评论