【问题标题】:How can I make a Xamarin Template customizable by passing in a type parameter?如何通过传入类型参数来自定义 Xamarin 模板?
【发布时间】:2018-07-31 22:00:27
【问题描述】:

这是我目前想出的方法。然而,这似乎不是一个干净的解决方案。

有没有人对我如何提出更好的解决方案提出任何建议?

<?xml version="1.0" encoding="utf-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:Japanese;assembly=Japanese" 
             x:Class="Japanese.Templates.HeaderTemplate" 
             x:Name="this" HorizontalOptions="FillAndExpand" Orientation="Vertical" Spacing="0" Margin="0">
    <StackLayout IsVisible="{Binding HeaderType, Converter={StaticResource HeaderType1BoolConverter}, Source={x:Reference this}  }" >
        <!-- code -->
    </StackLayout>
    <StackLayout IsVisible="{Binding HeaderType, Converter={StaticResource HeaderType2BoolConverter}, Source={x:Reference this}  }" >
        <!-- code -->
    </StackLayout>
</StackLayout>

在我的银行端 CS:

using System;
using System.Collections.Generic;
using Xamarin.Forms;

namespace Japanese.Templates
{
    public partial class HeaderTemplate : StackLayout
    {
        public HeaderTemplate()
        {
            InitializeComponent();
        }

   public static readonly BindableProperty HeaderTypeProperty =
        BindableProperty.Create(
            nameof(HeaderType),
            typeof(string),
            typeof(DataViewCellTemplate),
            default(string));

    public string HeaderType
    {
        get { return (string)GetValue(HeaderTypeProperty); }
        set { SetValue(HeaderTypeProperty, value); }
    }

    }
}

转换器代码:

public class HeaderType1BoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return object.Equals(value, "1");
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class HeaderType2BoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return object.Equals(value, "2");
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

在调用代码中:

<template:HeaderTemplate Header="Application" HeaderType="1" />

【问题讨论】:

  • 严格根据我看到的代码,不需要值转换器(您可以只从模板中公开布尔属性,即ShowTopContainerShowBottomContainer,这将绑定到@每个StackLayout 的987654327@ 属性)。您是否可以提供更多详细信息,以便其他人更好地了解您要使用模板实现的目标?您是否希望基于 HeaderType 对模板进行其他自定义(除了隐藏/显示 2 个容器)。
  • 你考虑过控制模板吗?
  • @Tiago_nes 你能给我一个例子,说明你所说的控制模板是什么意思,以及它如何适合我的代码。谢谢
  • @gannaway - 我的应用程序需要 3-4 个不同的模板,但模板的外部部分都是相同的。所以我想只拥有一个模板,在 3-4 个不同的 XAML 块中编写代码,并传递一个参数或类似于模板的东西,以便根据参数显示这 3-4 个代码块中的一个。
  • HeaderType 是动态值(在运行时确定)还是在设计时知道?你会在 ListView 中使用这些模板吗?

标签: xamarin xamarin.forms


【解决方案1】:

使用triggers 是一个选项。

例如:您可以添加属性触发器来检查 HeaderType 上的值,并相应地更新自定义控件 HeaderView 中的 Content(或布局)。

请注意,在这种情况下,我们是从 ContentView 而不是 StackLayout 扩展的,假设一次只有一个布局/控件可见。

XAML

<ContentView 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:local="clr-namespace:SampleApp" 
    x:Class="SampleApp.HeaderView">

    <ContentView.Triggers>

        <!-- if header type is 1, use header1 layout -->
        <Trigger TargetType="local:HeaderView" Property="HeaderType" Value="1">
            <Setter Property="Content">
                <Setter.Value>
                    <Label Text="Header1" />
                </Setter.Value>
            </Setter>
        </Trigger>

        <!-- if header type is 2, use header2 layout -->
        <Trigger TargetType="local:HeaderView" Property="HeaderType" Value="2">
            <Setter Property="Content">
                <Setter.Value>
                    <StackLayout>
                        <Label Text="Header2" />
                        <BoxView HeightRequest="1" BackgroundColor="Gray" />
                    </StackLayout>
                </Setter.Value>
            </Setter>
        </Trigger>

        <!-- you can add more layouts here if you need -->

    </ContentView.Triggers>

    <!-- add default content that can be displayed in case of no match -->
    <StackLayout>
        <Label Text="DefaultHeader" />
        <BoxView HeightRequest="1" BackgroundColor="Gray" />
    </StackLayout>    

</ContentView>

代码隐藏

public partial class HeaderView : ContentView
{
    public HeaderView()
    {
        InitializeComponent();

    }

    public static readonly BindableProperty HeaderTypeProperty =
        BindableProperty.Create(
            nameof(HeaderType), typeof(string), typeof(HeaderView),
            defaultValue: default(string));

    public string HeaderType
    {
        get { return (string)GetValue(HeaderTypeProperty); }
        set { SetValue(HeaderTypeProperty, value); }
    }
}

用法

<local:HeaderView HeaderType="1" />

【讨论】:

  • 谢谢 Ada,你能告诉我我可以在主 XAML 的另一个 StackLayout 中使用那个 ContentView 模板吗?
  • 是的,你可以。 &lt;StackLayout&gt;&lt;local:HeaderView HeaderType="1" /&gt;&lt;!-- other XAML controls --&gt;...&lt;/StackLayout&gt;
【解决方案2】:

我相信你正在寻找的是一个 ControlTemplate 这里是一个实现。

App.xaml

<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.App">
    <Application.Resources>
        <ResourceDictionary>
            <ControlTemplate x:Key="Header1Template">
                <StackLayout>
                    <!-- code -->
                </StackLayout>
            </ControlTemplate>
            <ControlTemplate x:Key="Header2Template">
                <StackLayout>
                    <!-- code -->
                </StackLayout>
            </ControlTemplate>
        </ResourceDictionary>
    </Application.Resources>
</Application>

用法(这将使 header1 成为您的默认设置,您可以选择两者中的任何一个)

<ContentView x:Name="contentView" ControlTemplate="{StaticResource Header1Template}">

现在有很多方法可以更改标题,或者通过定义为上面的默认代码或使用可以是按钮单击的操作(如链接上给出的示例)或使用 propertyChange 事件,如果您想依赖用于选择标题的属性,例如:

public int HeaderNumber { get; set; }

protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    base.OnPropertyChanged(propertyName);

    if (propertyName == "HeaderNumber")
    {
        contentView.ControlTemplate = (HeaderNumber == 1) ? Header1Template : Header2Template;
    }
}

请注意,我从未实现过 ControlTemplate,但我认为这可以解决您的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-28
    相关资源
    最近更新 更多