我们有许多显示模型列表的视图,在每个视图中,点击一个项目会显示一个操作表。
一切运行良好,但我们希望利用 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(委托 del,Object[] invoke_params) 在 ABI.System.Collections.Generic.IVectorMethods
1.Append(IObjectReference obj, T value) at ABI.System.Collections.Generic.IListMethods
1.Add(IObjectReference obj, T item)
在 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(String propertyName)
在 Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty 属性、BindablePropertyContext 上下文、对象值、布尔值 currentApplying、SetValueFlags 属性、布尔值静默)
在 Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty 属性、对象值、SetValueFlags 属性、SetValuePrivateFlags privateAttributes)
在 Microsoft.Maui.Controls.BindingExpression.ApplyCore(对象源对象,BindableObject 目标,BindableProperty 属性,布尔值 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()
--- 先前位置的堆栈跟踪结束 ---
在 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.EventState.<GetEventInvoke>b__1_0(Object obj, T e) at ABI.System.EventHandler
1.Do_Abi_Invoke[TAbi](Void* thisPtr, IntPtr 发送者, TAbi args)
请勿将标签放入
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>
.....
我测试过,效果很好。