首页 > 解决方案 > 如何为 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>

标签: formsxamarincontroltemplate

解决方案


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 的一般想法,它可以工作,但也许有更好的方法来完成。


推荐阅读