第八章:代码和XAML协调一致(2)

简介:

x:Name属性

在大多数实际应用程序中,代码隐藏文件需要引用XAML文件中定义的元素。 您在上一章的CodePlusXaml程序中看到了一种方法:如果代码隐藏文件知道XAML文件中定义的可视树的布局,它可以从根元素(页面本身)开始, 找到树中的特定元素。 此过程称为“在树上行走”,可用于定位页面上的特定元素。
通常,更好的方法是为XAML文件中的元素赋予类似于变量名的名称。 要执行此操作,请使用XAML固有的属性,称为Name。 因为前缀x几乎单独用于XAML固有的属性,所以此Name属性通常称为x:Name。
XamlClock项目演示了x:Name的使用。 这是包含两个Label控件的XamlClockPage.xaml文件,名为timeLabel和dateLabel:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlClock.XamlClockPage">
    <StackLayout>
        <Label x:Name="timeLabel"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="EndAndExpand" />
        <Label x:Name="dateLabel"
               HorizontalOptions="Center"
               VerticalOptions="StartAndExpand" />
   </StackLayout>
</ContentPage>

x:Name的规则与C#变量名称的规则相同。 (你很快就会明白为什么。)名称必须以字母或下划线开头,并且只能包含字母,下划线和数字。
与第5章中的时钟程序一样,XamlClock使用Device.StartTimer来触发定期事件以更新时间和日期。 这是XamlClockPage代码隐藏文件:

namespace XamlClock
{
    public partial class XamlClockPage
    {
        public XamlClockPage()
        {
            InitializeComponent();
            Device.StartTimer(TimeSpan.FromSeconds(1), OnTimerTick);
        }
        bool OnTimerTick()
        {
            DateTime dt = DateTime.Now;
            timeLabel.Text = dt.ToString("T");
            dateLabel.Text = dt.ToString("D");
            return true;
        }
    }
}

此计时器回调方法每秒调用一次。 该方法必须返回true才能继续计时器。 如果返回false,则计时器停止,必须通过另一次调用Device.Start?Timer重新启动。
回调方法引用timeLabel和dateLabel,就像它们是普通变量一样,并设置每个变量的Text属性:
201807052108200346
这不是一个视觉上令人印象深刻的时钟,但它绝对是功能性的。
如何使用代码隐藏文件引用用x:Name标识的元素? 这很神奇吗? 当然不是。 当您检查XAML解析器在构建项目时从XAML文件生成的XamlClockPage.xaml.g.cs文件时,该机制非常明显:

namespace XamlClock {
    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    public partial class XamlClockPage : global::Xamarin.Forms.ContentPage {
        [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")]
        private global::Xamarin.Forms.Label timeLabel;
        [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")]
        private global::Xamarin.Forms.Label dateLabel;

        [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "0.0.0.0")]
        private void InitializeComponent() {
            this.LoadFromXaml(typeof(XamlClockPage));
            timeLabel = this.FindByName<global::Xamarin.Forms.Label>("timeLabel");
            dateLabel = this.FindByName<global::Xamarin.Forms.Label>("dateLabel");
        }
    }
}

由于属性和完全限定类型,可能有点难以看到,但是当构建时XAML解析器咀嚼XAML文件时,每个x:Name属性都成为此生成的代码文件中的私有字段。这允许代码隐藏文件中的代码引用这些名称,就像它们是正常字段一样 - 它们肯定是。但是,字段最初为空。仅在运行时调用Initial?izeComponent时,才会通过FindByName方法设置两个字段,该方法在NameScopeExtensions类中定义。如果代码隐藏文件的构造函数尝试在InitializeComponent调用之前引用这两个字段,则它们将具有空值。
这个生成的代码文件还暗示了x的另一个规则:名称值现在非常明显但很少明确说明:名称不能复制代码后面文件中定义的字段或属性的名称。
因为这些是私有字段,所以只能从代码隐藏文件而不是其他类访问它们。如果ContentPage衍生产品需要将公共字段或属性公开给其他类,则必须自己定义它们。
显然,x:名称值在XAML页面中必须是唯一的。如果您在XAML文件中使用OnPlatform作为特定于平台的元素,这有时会成为问题。例如,这是一个XAML文件,它将OnPlatform的iOS,Android和WinPhone属性表示为属性元素,以选择三个Label视图之一:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PlatformSpecificLabels.PlatformSpecificLabelsPage">
    <OnPlatform x:TypeArguments="View">
        <OnPlatform.iOS>
            <Label Text="This is an iOS device"
                HorizontalOptions="Center"
                VerticalOptions="Center" />
        </OnPlatform.iOS>
        <OnPlatform.Android>
            <Label Text="This is an Android device"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
        </OnPlatform.Android>
        <OnPlatform.WinPhone>
            <Label Text="This is an Windows device"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
        </OnPlatform.WinPhone>
    </OnPlatform>
</ContentPag```  

OnPlatform的x:TypeArguments属性必须与目标属性的类型完全匹配。 此OnPlatform元素隐式设置为ContentPage的Content属性,此Content属性的类型为View,因此OnPlatform的x:TypeArguments属性必须指定View。 但是,OnPlatform的属性可以设置为从该类型派生的任何类。 设置为iOS,Android和WinPhone属性的对象实际上可以是不同的类型,只要它们都来自View。
尽管该XAML文件有效,但它并不完全是最佳的。 实例化并初始化所有三个Label视图,但只有一个设置为ContentPage的Content属性。 如果您需要从代码隐藏文件中引用Label并且为每个文件指定相同的名称,则会出现此方法的问题,如下所示:
以下XAML文件不起作用!

         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="PlatformSpecificLabels.PlatformSpecificLabelsPage">
<OnPlatform x:TypeArguments="View">
    <OnPlatform.iOS>
        <Label x:Name="deviceLabel"
               Text="This is an iOS device"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    </OnPlatform.iOS>
    <OnPlatform.Android>
        <Label x:Name="deviceLabel"
               Text="This is an Android device"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    </OnPlatform.Android>
    <OnPlatform.WinPhone>
        <Label x:Name="deviceLabel"
               Text="This is a Windows device"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    </OnPlatform.WinPhone>
</OnPlatform>


这不起作用,因为多个元素不能具有相同的名称。
您可以使用Device.OnPlatform为它们提供不同的名称并处理代码隐藏文件中的三个名称,但更好的解决方案是使特定于平台的标记保持尽可能小。 在此示例中,除Text外,所有Label属性都相同,因此只有Text属性需要特定于平台。 这是PlatformSpecificLabels程序的版本,它包含在本章的示例代码中。 它有一个Label,除了Text属性外,一切都是平台独立的:

         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="PlatformSpecificLabels.PlatformSpecificLabelsPage">
<Label x:Name="deviceLabel"
       HorizontalOptions="Center"
       VerticalOptions="Center">
    <Label.Text>
        <OnPlatform x:TypeArguments="x:String"
                    iOS="This is an iOS device"
                    Android="This is an Android device"
                    WinPhone="This is a Windows device" />
    </Label.Text>
</Label>


这是它的样子:
![201807052120000347](https://yqfile.alicdn.com/ad3d9d49c1d7d4a38c9580f9987b74de4dce71b4.jpeg)
Text属性是Label的content属性,因此您不需要上一个示例中的Label.Text标记。 这也有效:

         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="PlatformSpecificLabels.PlatformSpecificLabelsPage">

      HorizontalOptions="Center"
      VerticalOptions="Center">
    <OnPlatform x:TypeArguments="x:String"
                iOS="This is an iOS device"
                Android="This is an Android device"
                WinPhone="This is a Windows device" />
</Label>

目录
相关文章
|
3月前
|
容器 C# 开发者
XAML语言大揭秘:WPF标记的魅力所在,让你轻松实现界面与逻辑分离,告别复杂代码!
【8月更文挑战第31天】XAML提供了一种直观且易于维护的界面设计方式,使得开发者可以专注于逻辑和业务代码的编写,而无需关心界面细节。通过数据绑定、布局管理和动画效果等特性,XAML可以实现丰富的界面交互和视觉效果。在实际开发过程中,开发者应根据具体需求选择合适的技术方案,以确保应用程序能够满足用户的需求。希望本文的内容能够帮助您在WPF应用程序开发中更好地利用XAML语言。
44 0
|
C#
艾伟_转载:WPF/Silverlight陷阱:XAML自定义控件的嵌套内容无法通过名称访问
为了说明这个问题,假定我们需要实现一个具有特殊功能的按钮控件。编写Xaml文件如下: Button> 对 Code Behind类,唯一的改动是把向导生成的基类从UserControl改成Button: public partial class XamlButton : Button{    ...
1076 0
|
JavaScript Android开发 索引
第八章:代码和XAML协调一致5
点按手势 Xamarin.Forms按钮响应手指点击,但您实际上可以从任何派生自View的类中获取手指点击,包括Label,BoxView和Frame。 这些点击事件不会内置到View类中,但View类会定义名为GestureRecognizers的属性。
1326 0
|
XML JavaScript Android开发
|
JavaScript API Android开发
|
JavaScript Android开发
|
JavaScript Android开发 iOS开发
|
XML JavaScript C#
第七章:XAML vs. code(3)
属性元素语法这里有一些C#与第4章中的FramedText代码相似。在一个语句中,它实例化一个Frame和一个Label,并将Label设置为Frame的Content属性: new Frame { OutlineColor = Color.
910 0