文中带有[ ]的中文内容是我的个人注解,可以选择性食用,该章节已经施工完毕
第 1 部分。 XAML 入门
- 2019/09/30
在 Xamarin.Forms 应用程序中,XAML 主要用于定义页面的视觉内容,并与 c # 代码隐藏文件一起工作。
代码隐藏文件提供标记的代码支持。 这两个文件共同构成了一个新的类定义,其中包括子视图和属性初始化。 在 XAML 文件中,类和属性通过 XML 元素和属性进行引用,并建立标记和代码之间的链接。
创建解决方案
若要开始编辑第一个 XAML 文件,请使用 Visual Studio 或 Visual Studio for Mac 创建新的 Xamarin.Forms 解决方案。 (选择下面与您的环境相对应的选项卡。 )
在 Windows 中,启动 Visual Studio 2019,然后在 “开始” 窗口中单击 " 创建新项目 " 以创建新项目:
在“创建新项目”窗口中,在“项目类型”下拉菜单中选择“移动”,选择“移动应用(Xamarin.Forms)”,然后单击“下一步”按钮:
在 " 配置新项目 " 窗口中,将 " 项目名称 " 设置为 " XamlSamples (或任何偏好) ,并单击" 创建 "按钮。
在 " 新建跨平台应用 " 对话框中,单击 " 空白",然后单击 “确定” 按钮:
解决方案中创建了四个项目: XamlSamples .NET Standard 库、 XamlSamples、 XamlSamples 和通用 Windows 平台解决方案 XamlSamples。
在创建 XamlSamples 解决方案后,你可能需要通过选择各种平台项目作为解决方案启动项目,并在手机模拟器或实际设备上生成和部署由项目模板创建的简单应用程序,来测试你的开发环境。
除非你需要编写特定于平台的代码,否则各平台共有的 XamlSamples .NET Standard 库项目足够你大部分时候使用了。 这些文章不会超出该项目范围[这一句话翻译我没读懂,原文是These articles will not venture outside of that project. ]。
XAML 文件的解析
XamlSamples .NET Standard 库中是一对具有以下名称的文件:
-
App.xaml xaml文件;
与
-
App.xaml.cs,它是与 xaml 文件关联的 c # 代码隐藏 文件。
需要单击 app.xaml文件 旁边的箭头才能查看该c # 文件。
App.xaml和 App.xaml.cs的 组成了从Application
类派生而来的 App
的类 。 其他大多数包含XAML文件的其他类都构成了从ContentPage派生的类 ; 这些文件使用 XAML 来定义整页的可视内容 [XAML做界面,C#做逻辑]。 这适用于 XamlSamples 项目中的其他两个文件:
- MAINPAGE. xaml xaml 文件
- MainPage.xaml.cs,c # 代码隐藏文件。
尽管[不同版本的xamarin.forms生成的]格式可能稍有不同,但 MainPage 文件看起来像下文:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin Forms!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</ContentPage>
这两个 XML 命名空间 (xmlns
) 声明引用 uri、第一个看起来是 Xamarin 网站上的,第二个看起来是微软的。 不过读者不需要检查这些 Uri 所指向的内容。 他们没有任何内容[我试了,乱码或拒绝访问]。 它们只是 Xamarin 和 Microsoft 拥有的 Uri,它们基本上充当版本标识符。
第一个 XML 命名空间声明意味着 XAML 文件中没有前缀定义的标签将引用Xamarin.Forms中的类 ,例如 ContentPage
。 第二个命名空间声明定义了一个前缀 x
。 这适用于 XAML 自身固有的几个元素和属性,以及其他 XAML 实现所支持的属性。 但是,根据在 URI 中嵌入的年份,这些元素和属性略有不同。 Xamarin.Forms 支持 2009 XAML 规范,但并不支持所有。
local
命名空间声明允许访问 .NET Standard 库项目中的其他类。
在第一个标签的末尾, x
前缀用于名为Class
的属性 。 由于使用 x
前缀对于 XAML 命名空间几乎是通用的,因此 xaml 属性,例如 Class
, 几乎总被指定为 x:Class
。
x:Class
属性指定了一个完全限定的 .net 类名:XamlSamples
命名空间中的类MainPage
。 这意味着,此 XAML 文件定义了派生自ContentPage
[x:class出现的地方]的XamlSamples
命名空间中的一个新类 MainPage
,
x:Class
属性只能出现在 XAML 文件的根元素中,以定义派生的 c # 类。 这是 XAML 文件中定义的唯一一个新类。 XAML 文件中显示的所有其他内容只是从现有的类进行实例化并进行了初始化。
MainPage.xaml.cs 文件与下文类似 (除了未使用的using
指令):
using Xamarin.Forms;
namespace XamlSamples
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
}
MainPage
类派生自 ContentPage
,但注意 partial
类定义。 这表明MainPage
类应有其他partial
类定义 ,但在什么地方呢? InitializeComponent
是什么方法 ?
当 Visual Studio 生成项目时,它会分析 XAML 文件以生成 c # 代码文件。 如果你查看 XamlSamples\XamlSamples\obj\Debug 目录,你会找到名为 XamlSamples.MainPage.xaml.g.cs 的文件。"g"
代表生成的。 这是MainPage
的其他partial
类定义 ,其中包含了MainPage
类构造方法调用的 InitializeComponent
方法的定义 。 然后,可以将这两个partial MainPage
类定义组合在一起。 XAML 文件或二进制形式的 XAML 文件将嵌入到可执行文件中,具体取决于是否编译了 XAML文件。
在运行时,特定平台项目中的代码 会调用LoadApplication
方法,并传递给在 .NET Standard 库中的新实例 App
类。 App
类构造函数实例化了 MainPage
类。 MainPage
类的构造函数调用 InitializeComponent
,然后InitializeComponent
调用 LoadFromXaml
从 .NET Standard 库中提取 XAML 文件 (或其已编译的二进制) 。 LoadFromXaml
初始化在 XAML 文件中定义的所有对象,将它们一起连接到父-子关系,将代码中定义的事件处理程序附加到 XAML 文件中设置的事件,并将生成的对象树设置为页面内容。
尽管通常不需要花费大量时间在生成代码文件上[原文generated code files,代指上文的g.cs那类文件吧],但有时会在生成的文件中的代码上引发运行时异常,因此你应该熟悉它们。
编译和运行此程序时, Label
元素会显示在页面的中心,如 XAML 所示:
对于更有趣的视觉对象,你只需了解更有趣的 XAML。
添加新的 XAML 页
若要将其他基于 XAML 的 ContentPage
类添加到项目中,请选择 " XamlSamples " .NET Standard 库项目,右键单击,然后选择 " 添加 > 新建项 …"。在 " 添加新项 " 对话框中,选择 " Visual c # 项" > Xamarin.Forms > 内容页 (不是Content Page (c # ),这将创建一个只有代码的页面页或 内容视图。 为页面提供名称,例如, HelloXamlPage:
HelloXamlPage 和代码隐藏文件 HelloXamlPage.xaml.cs两个文件被添加到项目中。
设置页面内容
编辑 HelloXamlPage 文件,以便仅有 ContentPage
和 ContentPage.Content
两个标签:[其实就是把StackLayout删掉]
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.HelloXamlPage">
<ContentPage.Content>
</ContentPage.Content>
</ContentPage>
ContentPage.Content
标记是 XAML 的唯一语法的一部分。 最初,它们可能看起来是无效的 XML,但它们是合法的。 句点不是 XML 中的特殊字符。
ContentPage.Content
标记被称为 "属性元素 " 标记。 Content
是ContentPage
的属性 ,通常设置为单一视图或具有子视图的布局。 通常,属性将成为 XAML 中的属性,但很难为复杂对象设置Content
属性。 出于此原因,该属性表示为一个 XML 元素,该元素由类名和以句点分隔的属性名称组成。 现在, Content
可以在ContentPage.Content
标签之间设置属性 ,如下所示:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.HelloXamlPage"
Title="Hello XAML Page">
<ContentPage.Content>
<Label Text="Hello, XAML!"
VerticalOptions="Center"
HorizontalTextAlignment="Center"
Rotation="-15"
IsVisible="true"
FontSize="Large"
FontAttributes="Bold"
TextColor="Blue" />
</ContentPage.Content>
</ContentPage>
另请注意,在根标记上设置了一个Title
属性。
此时,类、属性和 XML 之间的关系应该是显而易见的: Xamarin.Forms 类 (如 ContentPage
或 Label
) 作为 XML 元素出现在 XAML 文件中。 该类的属性(包括的 Title
ContentPage
属性 Label
)通常显示为 XML 属性。
[换句话说就是 XAML->类(XML元素 也就是一个标签)->类的属性(XML元素的属性 也就是标签里的属性)]
有很多快捷方式可用于设置这些属性的值。 某些属性是基本数据类型:例如, Title
和 Text
属性的类型为类型 String
, Rotation
为 Double
, IsVisible
( 默认情况下为true
,此处设置为了插图)的类型为 Boolean
。
HorizontalTextAlignment
属性的类型为 TextAlignment
,它是一个枚举。 对于任何枚举类型的属性,只需提供一个成员名称。
但对于更复杂类型的属性,转换器用于分析 XAML。 转换器是 Xamarin.Forms 派生自类TypeConverter
。 很多是公共类,但某些不是公共类。 对于此特定的 XAML 文件,其中几个类在幕后扮演角色:
LayoutOptionsConverter
对于VerticalOptions
属性FontSizeConverter
对于FontSize
属性ColorTypeConverter
对于TextColor
属性
这些转换器控制属性设置的允许语法。
ThicknessTypeConverter
可以处理用逗号分隔的一个、两个或四个数字。 如果提供一个数字,则应用于所有四个边。 如果有两个数字,则第一个是左对齐和右填充,第二个数字为顶部和底部。 如果有四个数字,四个数字按左、上、右和下顺序排列。
LayoutOptionsConverter
可以将LayoutOptions
结构的公共静态字段的名称转换为LayoutOptions
类型的值 。
FontSizeConverter
可以处理 NamedSize
成员或用数字表示的字体大小。
[科普 RGB就是红绿蓝 三通道的混合值, 一般是6位16进制,每两位一种通道]
ColorTypeConverter
接受公共静态字段Color
结构的名称或十六进制 RGB 值 ,带或不带 alpha 通道(前面带有数字符号 ( # )) 。 下面是没有 alpha 通道的语法:
TextColor="#rrggbb"
其中的每个小字母都是一个十六进制数字。 下面介绍如何包括 alpha 通道:[前面两个aa就是用来写alpha通道值的]
TextColor="#aarrggbb"
对于 alpha 通道,请记住,FF 是完全不透明的,00是完全透明的。
另外两种格式允许您为每个通道仅指定一个十六进制数字:
TextColor="#rgb"` `TextColor="#argb"
在这些情况下,将重复每一个通道的数字以形成RGB值。 例如,#CF3 是 RGB 颜色 “CC-FF-33”。
页面导航
运行 XamlSamples 程序时, MainPage
会显示。 若要查看新的 HelloXamlPage
可以将其设置为 App.xaml.cs 文件中的新启动页面,或导航到MainPage
中的新页面 。
若要实现导航,请先更改 App.xaml.cs 构造函数中的代码,以便 NavigationPage
创建对象:
C#复制
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
在 MainPage.xaml.cs 构造函数中,可以创建一个简单的 Button
,并使用事件处理程序导航到 HelloXamlPage
:
public MainPage()
{
InitializeComponent();
Button button = new Button
{
Text = "Navigate!",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};
button.Clicked += async (sender, args) =>
{
await Navigation.PushAsync(new HelloXamlPage());
};
Content = button;
}
设置 Content
属性将替换 XAML 文件中Content
属性的设置。 在编译和部署该程序的新版本时,屏幕上会出现一个按钮。 按下它将导航到 HelloXamlPage
。 下面是 iPhone、Android 和 UWP 上的结果页:
您可以使用 iOS 上的 " < 后退 " 按钮导航回MainPage
,使用页面顶部的左箭头或 Android 上电话的底部后退按钮,或使用 Windows 10 上页面顶部的向左键。
请随意对 XAML 进行试验,以提供不同的呈现方式 Label
。 如果需要将任何 Unicode 字符嵌入到文本中,则可以使用标准 XML 语法。 例如,若要将问候语置于智能引号中,请使用: [两串乱码就是左右引号的UNICODE]
<Label Text="“Hello, XAML!”" … />
如下所示:
XAML 和代码交互
HelloXamlPage 示例只包含 Label
页上的一个,但这种情况非常罕见。 大多数 ContentPage
派生类型将 Content
属性设置为某种形式的布局,例如 StackLayout
。 Children
的属性 StackLayout
定义为类型 IList<View>
,但实际上是ElementCollection<View>
类型的对象 ,并且可以使用多个视图或其他布局填充该集合。 在 XAML 中,这些父子关系与普通 XML 层次结构建立在一起。 下面是一个名为 XamlPlusCodePage 的新页的 XAML 文件:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<StackLayout>
<Slider VerticalOptions="CenterAndExpand" />
<Label Text="A simple Label"
Font="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Click Me!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
此 XAML 文件在语法上完成了,下图是效果:
但是,你可能会认为这个界面的功能不充足。 也许该让一个Label
显示当前 Slider
的 值,并且Button
可能会在 被点击时执行一些操作。
如 第4部分所示。数据绑定基础知识 Slider
使用Label
来显示值的工作 可以完全在 XAML 中使用数据绑定来实现。 但首先查看代码[指的是通过C#]来解决问题非常有用。 尽管如此,响应 Button
单击事件无疑也需要代码。 这意味着XamlPlusCodePage.cs
必须包含 Slider
的ValueChanged
事件的处理程序 ,以及Button
事件的Clicked
处理程序 。 我们来添加它们:
namespace XamlSamples
{
public partial class XamlPlusCodePage
{
public XamlPlusCodePage()
{
InitializeComponent();
}
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
}
void OnButtonClicked(object sender, EventArgs args)
{
}
}
}
这些事件处理程序不需要是public
的。
返回到 XAML 文件, Slider
和 Button
标记需要包括 ValueChanged
Clicked
引用这些处理程序的和事件的特性:
XAML复制
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<StackLayout>
<Slider VerticalOptions="CenterAndExpand"
ValueChanged="OnSliderValueChanged" />
<Label Text="A simple Label"
Font="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Click Me!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout>
</ContentPage>
请注意,为事件分配处理程序的语法与为属性赋值的语法相同。
如果Slider
的 ValueChanged
事件的处理程序 将使用 Label
显示当前值,则该处理程序需要从代码中引用该对象。 Label
需要一个使用特性指定的名称 x:Name
。[你总得给这个label一个名字,就好像int a; 的a 一样,label只是一个元素,不能被调用]
[修改一下xaml文件的label标签,直接替换就好]
<Label x:Name="valueLabel"
Text="A simple Label"
Font="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
x
属性的前缀 x:Name
指示此属性是 XAML 的内部属性。
分配给该属性的名称 x:Name
具有与 c # 变量名称相同的规则。 例如,它必须以字母或下划线开头并且不包含嵌入的空格。
现在, ValueChanged
事件处理程序可以将设置 Label
为显示新 Slider
值。 新值可从事件参数获取:
C#复制
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
valueLabel.Text = args.NewValue.ToString("F3");
}
或者,处理程序可以 从sender
参数获取生成此事件的对象Slider
,并从该对象获取 Value
属性:
C#复制
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}
首次运行该程序时, Label
不会显示 Slider
该值,因为尚未触发 ValueChanged
事件。 但之后操作 Slider
都会导致显示值:
现在为提供 Button
。 让我们 通过使用按钮的显示警报来模拟对Clicked
事件的响应 。 事件处理程序可以安全地将 sender
参数强制转换为 Button
,然后访问其属性:
[题外话,你不太需要关心async和await是什么意思,这是个异步配套关键字]
C#复制
async void OnButtonClicked(object sender, EventArgs args)
{
Button button = (Button)sender;
await DisplayAlert("Clicked!",
"The button labeled '" + button.Text + "' has been clicked",
"OK");
}
此方法定义为 async
,因为该 DisplayAlert
方法是异步的,并且应以 await
运算符开头,后者将在方法完成时返回。 由于此方法 Button
从参数中获取触发事件 sender
,因此同一处理程序可用于多个按钮[意思就是说XAML里定义很多个不同的Button,但是都可以用这个方法来当事件处理函数,都加上一句Clicked="OnButtonClicked"就好了]。
你已看到,在 XAML 中定义的对象可能会激发在代码隐藏文件中进行处理的事件,并且代码隐藏文件可以使用x:Name
属性指定的名称访问在 XAML 中定义的对象 。 这是C#代码和 XAML 交互的两种基本方法。
可以通过检查新生成的 XamlPlusCode.xaml.g.cs 文件 来搜集 XAML 工作方式的其他深入了解,其中现在包含分配给任何 x:Name
属性作为私有字段的任何名称。 下面是该文件的简化版本:
public partial class XamlPlusCodePage : ContentPage {
private Label valueLabel;
private void InitializeComponent() {
this.LoadFromXaml(typeof(XamlPlusCodePage));
valueLabel = this.FindByName<Label>("valueLabel");
}
}
此字段的声明允许在 XamlPlusCodePage
区域下的分部类文件中的任意位置自由使用该变量。 在运行时,将在分析 XAML 后分配该字段。 这意味着, valueLabel
在XamlPlusCodePage
构造函数在调用开始时是null
但当InitializeComponent
被调用时开始有效 。
在将 InitializeComponent
控制权返回给构造函数后,该页面的视觉对象的构造方式与在代码中实例化和初始化相同。 XAML 文件不再扮演类中的任何角色。 您可以使用任何所需的方式在页面上操作这些对象,例如,通过将视图添加到 StackLayout
,或将页面的 Content
属性设置为其他内容。 您可以通过检查 Content
页的属性和布局集合中的项来 “遍历树” Children
。 您可以为以这种方式访问的视图设置属性,或以动态方式向它们分配事件处理程序。
欢迎。 这是您的页面,而 XAML 只是生成其内容的工具。
摘要
本文介绍了 XAML 文件和代码文件如何影响类定义以及 XAML 和代码文件如何交互。 但 XAML 还具有自己独特的语法功能,使其能够以非常灵活的方式使用。 你可以在第2部分中开始浏览这些项 。基本 XAML 语法。