forms - 如何为 Xamarin 表单 TemplatedView 使用 TemplateSelector?
问题描述
我需要为 TemplatedView 中的 ControlTemplate 使用 TemplateSelector。有没有什么方法可以处理ControlTemplateSelector,类似于DataTemplateSelector 是怎么做的?
<ContentPage.Resources>
<ResourceDictionary>
<data:MediaTemplateSelector x:Key="headerTemplateSelector"
VideoTemplate="{StaticResource VideoHeaderTemplate}"
ImageTemplate="{StaticResource ImageHeaderTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<TemplatedView ControlTemplate="{StaticResource headerTemplateSelector}"/>
</ContentPage.Content>
解决方案
ControlTemplate 和 DataTemplate 以不同的方式工作。
我从 ControlTemplates 开始尝试做一些类似于 DataTemplateSelector 的事情,但在页面级别定义项目外观,而不是在 ListView 或 CollectionView 等集合中的项目。一开始很困惑,因为它的工作方式完全不同……首先:忘记 DataTempateSelector 的概念,与 ControlTemplates 无关。
我将提供的代码有一些西班牙语的属性/类名,希望你无论如何都能理解这个想法。首先,假设您的 ViewModel 上有一个属性,需要根据相同或其他 ViewModel 属性以不同的方式呈现。
首先,定义一个 ContentView 类,稍后您将把它作为页面的一部分(可能是 TemplatedView,但是,当我尝试将 templatedView 直接放在页面上时出现异常,因此,我使用 ContentView) :
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local1="clr-namespace:MrWaiter.Resources;assembly=MrWaiter"
x:Class="MrWaiter.Views.ComandaControlTemplate"
ControlTemplate="{StaticResource ComandaPropia}">
<ContentView.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="ComandaPropia">
<Frame BackgroundColor="{x:StaticResource Key=pLight}" CornerRadius="10" HasShadow="True" HeightRequest="32"
Padding="0" Margin="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MostrarComandasCommand}" >
</TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
<Button FontFamily="{StaticResource MaterialFontFamily}"
HorizontalOptions="End" x:Name="botonExpandir"
Text="{x:Static local1:IconFont.Play}" BackgroundColor="Transparent"
Rotation="-90" FontSize="32" TextColor="White"
WidthRequest="55"
Margin="0,0,0,0"
Padding="0,0,0,0"
Command="{TemplateBinding MaximizeCommand}"
VerticalOptions="CenterAndExpand"
>
</Button>
</StackLayout>
</Frame>
</ControlTemplate>
<ControlTemplate x:Key="ComandaInvitaA" >
<Frame BackgroundColor="DarkGray" CornerRadius="10" HasShadow="True" HeightRequest="32"
Padding="0" Margin="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MostrarComandasCommand}" >
</TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
<Button FontFamily="{StaticResource MaterialFontFamily}"
HorizontalOptions="End" x:Name="botonExpandir" x:FieldModifier="Public"
Text="{x:Static local1:IconFont.Play}" BackgroundColor="Transparent"
Rotation="-90" FontSize="32" TextColor="White"
WidthRequest="55"
Margin="0,0,0,0"
Padding="0,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MaximizeCommand}"
VerticalOptions="CenterAndExpand"
>
</Button>
</StackLayout>
</Frame>
</ControlTemplate>
<ControlTemplate x:Key="ComandaInvitadaPor">
<Frame BackgroundColor="{x:StaticResource Key=sColor}" CornerRadius="10" HasShadow="True" HeightRequest="32"
Padding="0" Margin="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MostrarComandasCommand}" >
</TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
<Switch IsToggled="{Binding MostrarDetallado}"
IsVisible="False"
OnColor="{StaticResource Key=sLight}"
ThumbColor="{StaticResource Key=sDark}"
></Switch>
<Button FontFamily="{StaticResource MaterialFontFamily}"
HorizontalOptions="End" x:Name="botonExpandir"
Text="{x:Static local1:IconFont.Play}" BackgroundColor="Transparent"
Rotation="-90" FontSize="32" TextColor="White"
WidthRequest="55"
Margin="0,0,0,0"
Padding="0,0,0,0"
Command="{TemplateBinding MaximizeCommand}"
VerticalOptions="CenterAndExpand"
>
</Button>
</StackLayout>
</Frame>
</ControlTemplate>
</ResourceDictionary>
</ContentView.Resources>
</ContentView>
重要的是 contentView 的这个属性,它定义了一种 ControlTemplate 默认值:
ControlTemplate="{StaticResource ComandaPropia}"
并注意如何在 ControlTemplates 中定义数据绑定,使用 TemplatedBindings 而不是 Bindings:
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
在后面的 ContentView 代码上,您必须创建一个 BindableProperty 以允许在 View 上的属性和 ViewModel 上的属性之间进行 DataBinding(也许可以使用 DataTriggers 完成,但我正在学习 Xamaring,我想使用 BindableProperties 进行练习)。那个Property,会负责选择ControlTemplate,注意Method HandleTipoComandaPropertyChanged,这里我们在propertyChanged上选择ControlTemplate:
public partial class ComandaControlTemplate : ContentView
{
public static BindableProperty TipoComandaProperty = BindableProperty.Create(
propertyName: "TipoComanda",
returnType: typeof(Controls.TipoComandaEnum),
declaringType: typeof(ComandaControlTemplate),
defaultValue: Controls.TipoComandaEnum.Propia,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: HandleTipoComandaPropertyChanged);
private static void HandleTipoComandaPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var targetView = (ComandaControlTemplate)bindable;
if (targetView != null)
{
if ((Controls.TipoComandaEnum)newValue != (Controls.TipoComandaEnum)oldValue)
{
if ((Controls.TipoComandaEnum)newValue== Controls.TipoComandaEnum.Propia)
targetView.ControlTemplate =(ControlTemplate)targetView.Resources["ComandaPropia"];
else if ((Controls.TipoComandaEnum)newValue == Controls.TipoComandaEnum.Invitada)
targetView.ControlTemplate = (ControlTemplate)targetView.Resources["ComandaInvitadaPor"];
else if ((Controls.TipoComandaEnum)newValue == Controls.TipoComandaEnum.Invitacion)
}
}
}
public Controls.TipoComandaEnum TipoComanda
{
get
{
return Controls.TipoComandaEnum)base.GetValue(TipoComandaProperty);
}
set
{
base.SetValue(TipoComandaProperty, value);
}
}
public ComandaControlTemplate()
{
InitializeComponent();
}
}
最后,在您的页面中,根据需要放置模板化的 ContentView,并将我们在 View 中创建的 BindableProperty 绑定到 ViewModel 上的属性,该属性将确定所显示的 ControlTemplate:
<views:ComandaControlTemplate TipoComanda="{Binding BindingContext.TipoComanda}"></views:ComandaControlTemplate>
请注意,TipoComanda 是我们在 templatedView 上创建的 BindableProperty,它是 ViewModel 上的等效属性。
注意:这是关于我们如何使用 ControlTemplates 的一般想法,它可以工作,但也许有更好的方法来完成。
推荐阅读
- node.js - Express js - 从本地网络上的 expressjs 服务器下载静态文件速度很慢,因为互联网带宽
- php - 是否有自动获取用户电子邮件的 php 代码?
- java - 对于具有不同输入参数和不同返回类型的多个实现的通用方法,使用什么设计模式?
- f# - F# 库结构
- react-native - 未显示有关欧盟国家的同意对话,但仍会收到测试广告
- c++ - 如何测试 boost::bimap "right.find" 的结果是否被发现?
- javascript - 如何调整小型设备的引导模式(横向和纵向)
- python - 如果条件为带有数字和字符串的数组
- mininet - 使用 RYU 控制器的 SDN mininet 中的目标主机无法访问
- json - 如何将地图列表从颤振发送到 webapi