1.2-XAML语法基础:标记扩展{}
标记扩展,使属性值可以引用其他源的值或对象,比如引用资源字典、引用其它控件的属性值、绑定ViewModel类属性等。标记扩展的语法有大括号{}和尖括号<>两种方式,但x:Array比较特殊,只能使用尖括号。下个章节,深入学习标记扩展后,将能够理解两者区别。为方便学习和记忆,将标记扩展归纳为以下几类:
- 引用资源:StaticResource、DynamicResource
- 引用元素:x:Reference,RelativeSource
- 引用静态成员:x:Static
- 特殊值:x:Array、x:Null、x:Type
- 数据绑定:Binding
- 其它:跨平台OnPlatform/OnIdiom、字体图标FontImage、主题样式AppThemeBinding、模板绑定TemplateBinding
一、引用资源:StaticResource、DynamicResource
每个继承自VisualElement的对象,都有一个Resources属性,属性值为Dictionary<string,object>类型的集合对象,这些集合对象可作为资源,提供给控件树中的子元素使用。
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Resources>
<Color x:Key="PrimaryColor">Black</Color>
</ContentPage.Resources>
<StackLayout>
<Label Text="HelloWorld" TextColor="{StaticResource PrimaryColor}" />
</StackLayout>
</ContentPage>
代码解读:
6-8行:在ContentPage的Resources属性上,创建一个Dictionary<string,object>集合元素,其中键为“PrimaryColor”,值为Color类型的对象。Color类型有多个重载构造函数,传入字符串颜色名称,为其中一种创建对象的方式。
10行:通过StaticResource标记扩展,使TextColor属性的值,引用key为“PrimaryColor”的字典资源。框架会从元素自身的Resources属性开始,延着控件树向上查找匹配,如果找到,则返回其值,查找终止。如果未找到,会一直延伸到应用的根元素App.xaml的Application为止。如果在Application的Resources属性中都未找到,则会返回一个XamlParseException编译异常。
补充说明:
①定义Resources属性的代码中,隐藏了内容属性,完整代码应是:<ContentPage.Resources>…</ContentPage.Resources>
②StaticResource和DynamicResource的使用方法一致,两者唯一的区别在于,如果在运行时修改引用的数据源,StaticResource不会更新,而DynamicResource会实时更新
③资源字典理论上可以放任何对象资源,但一般用于创建样式、颜色、控件模板、数据模板、转换器的对象资源
二、引用元素/控件:x:Reference,RelativeSource
进行属性绑定时,除了MVVM模式中,绑定ViewModel的属性,有时还会绑定当前页面或指定页面的元素/控件的属性值,通过x:Reference或RelativeSource来设置绑定源。
1. x:Reference,直接通过元素名称(使用x:Name属性定义)来引用
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<StackLayout Padding="10">
<Slider
x:Name="slider"
Maximum="100"
VerticalOptions="Center" />
<Label Text="{Binding Value, Source={x:Reference slider}}" />
<Label
BindingContext="{x:Reference slider}"
Rotation="{Binding Value}"
Text="HelloWord" />
</StackLayout>
</ContentPage>
代码解读:
9行:通过x:Name属性,给Slider对象定义一个变量名称。
13行:直接定义Binding属性绑定,将属性Text的值,与slider对象的Value属性绑定。其中Binding的属性Source,通过x:Reference扩展标记,引用slider对象。
16-17行:先定义Label元素的绑定上下文BindingContext为slider,此处的BindingContext可以认为就是绑定的Source。定义好绑定上下文后,可以直接通过Binding绑定上下文拥有的属性。
补充说明:
①包含绑定源和路径的完整写法是{Binding Path=Value, Source={x:Reference slider}},上例子Path省略了
2. RelativeSource,从当前元素开始,延着控件树,以相对位置的方式,查子父或子元素
//绑定源设置为自己
<BoxView
HeightRequest="200"
WidthRequest="{Binding Source={RelativeSource Self}, Path=HeightRequest}"
Color="Red" />
//绑定源设置为上级
//AncestorType定向在控制树上向上找哪种类型,AncestorLevel定义找几层
//下例结果为Red
<StackLayout Padding="10" BackgroundColor="Red">
<StackLayout Padding="10" BackgroundColor="Yellow">
<StackLayout Padding="10" BackgroundColor="{Binding Source={RelativeSource AncestorType={x:Type StackLayout}, AncestorLevel=2}, Path=BackgroundColor}" />
</StackLayout>
</StackLayout>
三、引用静态成员:x:Static
引用C#定义的静态成员、枚举、常量等
//先定义一个C#静态类
public static class AppConstants
{
public static double Title1FontSize = 40;
public static double Title2FontSize = 32;
public static double Title3FontSize = 28;
public static double ContentFontSize = 16;
}
//在XAML中引用静态成员
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp7">
<StackLayout Padding="10">
<Label Text="This is Title" FontSize="{x:Static local:AppConstants.Title1FontSize}"/>
<Label Text="This is Content" FontSize="{x:Static local:AppConstants.ContentFontSize}"/>
</StackLayout>
</ContentPage>
代码解读:
15行:引入命名空间MauiApp7(案例项目名称),并起别名为local
18-19行:通过x:Static引入静态成员,访问方式【命名空间别名:类.属性】
四、特殊值:x:Type、x:Array、x:Null
1. x:Type,等效于C#的typeof()
//点击按钮,执行新增Lable元素命令,命令参数为Lable类型
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp7">
<StackLayout x:Name="stackLayout" Padding="10">
<Button
Command="{Binding CreateLableCommand}"
CommandParameter="{x:Type Label}"
Text="新增Lable元素" />
</StackLayout>
</ContentPage>
//后台代码为
public partial class MainPage : ContentPage
{
public ICommand CreateLableCommand { get; private set; }
public MainPage()
{
InitializeComponent();
CreateLableCommand = new Command<Type>((Type viewType) => {
var label = (Label)Activator.CreateInstance(viewType);
label.Text = "这是新创建的Label";
stackLayout.Add(label);
});
BindingContext= this;
}
}
代码解读:
10-11行:按钮绑定CreateLableCommand命令,命令的参数为Lable类型,相当于typeof(Lable)
30行:绑定上下文设置为当前页面,这样Binding才可以找到在后台代码中定义的CreateLableCommand命令
25-29行:创建一个命令回调,并赋值给命令属性
26行:根据传入的Type参数,创建对象,并强转为Lable类型。
28行:stackLayout的内容属性为集合类型,Add方法添加label对象。完整写法应该是stackLayout.Children.Add(label),省略了Children内容属性。
2. x:Array,将数组赋值给属性,结合x:Type使用,定义数组类型。
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp7">
<ListView Margin="10">
<ListView.ItemsSource>
<x:Array Type="{x:Type Color}">
<Color>Red</Color>
<Color>Green</Color>
<Color>Yellow</Color> <Color>Aqua</Color>
<Color>Black</Color>
<Color>Blue</Color>
<Color>Fuchsia</Color>
<Color>Gray</Color>
<Color>Green</Color>
<Color>Lime</Color>
<Color>Maroon</Color>
<Color>Navy</Color>
<Color>Olive</Color>
<Color>Pink</Color>
<Color>Purple</Color>
<Color>Red</Color>
<Color>Silver</Color>
<Color>Teal</Color>
<Color>White</Color>
<Color>Yellow</Color>
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<BoxView HeightRequest="20"
WidthRequest="{Binding Source={RelativeSource Mode=Self}, Path=WidthRequest}" Margin="3" Color="{Binding}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
代码解读:
8-14行:ListView的ItemsSource(数据源)属性值为Color类型的数组
15-21行:ListView的ItemsTemplate(数据项模板)属性值为DataTemplate
注:BoxView 必须设置宽高,否则显示不出色彩
3. x:Null,将属性值设置为空null
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp7">
<ContentPage.Resources>
<Style TargetType="Label">
<Setter Property="BackgroundColor" Value="Red" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Label Text="背景色使用Style定义的样式" />
<Label BackgroundColor="{x:Null}" Text="背景色不使用Style定义的样式" />
</StackLayout>
</ContentPage>
代码解读:
8-10行:在页面资源字典里,定义一个样式,目标元素为Label,设置背景色属性为红色,未指定Key,当前页面的所有Lable都会隐式应用这个Style。隐式指,不用通过StaticResource标记扩展,所有Lable都会使用这个样式。
15行:将背景色属性设置为空值,相当于不使用隐式设置的背景色样式。
五、数据绑定:Binding
Binding是XAML中最重要的核心概念,后续章节再详述,本章节只介绍最简单的使用。
//绑定最基本的要素:源、目标和路径
//源通过BindingContext属性,将slider设置为源
//目标是Label的属性Rotation
//绑定路径为slider的Value属性值
<ContentPage
x:Class="MauiApp7.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp7">
<StackLayout Padding="10,0">
<Label
BindingContext="{x:Reference Name=slider}"
Rotation="{Binding Path=Value}"
Text="TEXT" />
<Slider
x:Name="slider"
Maximum="360" />
</StackLayout>
</ContentPage>
//以上代码也可以使用以下方式,直接设置Source
<StackLayout Padding="10,0">
<Label
Rotation="{Binding Source={x:Reference slider}, Path=Value}"
Text="TEXT" />
<Slider
x:Name="slider"
Maximum="360"/>
</StackLayout>
六、其它:跨平台OnPlatform/OnIdiom、字体图标FontImage、主题样式AppThemeBinding
1、跨平台OnPlatform/OnIdiom
//BoxView在不同平台上有不同的宽高和颜色
//下例中使用了简写,完整写法为{OnPlatform Default=Yellow...}
//平台类型包括:Default、iOS、Android、MacCatalyst、Tizen、WinUI
<BoxView Color="{OnPlatform Yellow, iOS=Red, Android=Green}"
WidthRequest="{OnPlatform 250, iOS=200, Android=300}"
HeightRequest="{OnPlatform 250, iOS=200, Android=300}"/>
//BoxView在不同设备上有不同的宽高和颜色
//设置类型包括:Default、Phone、Tablet、Desktop、TV、Watch
<BoxView Color="{OnIdiom Yellow, Phone=Red, Desktop=Blue}"
WidthRequest="{OnIdiom 100, Phone=200, Desktop=400}"
HeightRequest="{OnIdiom 100, Phone=200, Desktop=400}"/>
//OnPlatform和OnIdiom使用尖括号来表达。繁琐很多。标记扩展简化了属性赋值
<BoxView>
<BoxView.Color>
<OnPlatform x:TypeArguments="Color">
<On Platform="iOS" Value="Red"/>
<On Platform="Android" Value="Green"/>
</OnPlatform>
</BoxView.Color>
</BoxView>
2、主题样式AppThemeBinding
MAUI可以设置Light和Dark两个主题,可以设置属性在不同主题下的属性值,有些类似于跨平台或设备的设置
//Label文字颜色,根据不同主题显示为不同颜色
//主题设置详见后续章节
<Label Text="Light主题时为绿色,Dark主题时为红色"
TextColor="{AppThemeBinding Light=Green, Dark=Red}" />
3、字体图标FontImage
//【】为字体图标unicode值,和HTML一样,在XAML里,需要使用&#方式
<Image BackgroundColor="#D1D1D1" Source="{FontImage , FontFamily=Ionicons, Size=44}" />
//以上字体图标,使用尖括号表达
<Image BackgroundColor="#D1D1D1">
<Image.Source>
<FontImageSource Glyph=""
FontFamily="Ionicons"
Size="44" />
</Image.Source>
</Image>
//引用字体的基本步骤:
//第一步:通过将字体拖到项目的 Resources\Fonts 文件夹中,并设置ItemGroup
<ItemGroup>
<MauiFont Include="Resources\Fonts\*" />
</ItemGroup>
//第二步:注册字体MauiProgram.cs
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});