前言

Windows Phone 7 是一个崭新的移动电话操作系统,在应用程序的开发上跟先前也有些许的不同;首先在应用程序的开发上,不再支持使用 Native code 做为开发使用的语言,只支持 managed code 的开发,而开发上的支持的 framework 为 Silverlight 以及 XNA,两种 framework 主要的差异是 XNA 是 Loop-based 的应用程序,Silverlight 是 event-driven 且以 XAML 作为接口基础的应用程序。

接下来笔者将会介绍 Silverlight 在 Windows Phone 7 上的开发,本篇是系列中的第一集,希望藉由本篇能让各位了解在 Silverlight 上开发的一些基础,也希望能够藉由本篇让您对于 Silverlight 能有初步的认识,以及了解 Silverlight 的开发是多么有趣的。

议程

首先以下是在本篇中,会为各位介绍的项目

  • 开发工具/环境需求
  • 开发资源
  • 硬件概观
  • 项目的基本档案结构
  • XAML ?
  • Application Bar
  • Textbox – InputScope
  • Screen Orientation
  • 容器控件

接下来便开始本篇的介绍

开发工具/环境需求

  • Development Tool

  • Environment requirement

    • 只支持 Vista with SP2 以及 Windows 7 的操作系统
      *请注意 Starter 的版本是不受支持的

    • 3GB 以上的硬盘容量

    • 2GB 以上的 RAM

    • 能够支持 DirectX 10 以及 WDDM 1.1 的显示适配器

    • 更多的相关信息可以参考下面位置
      http://msdn.microsoft.com/en-us/library/ff402530(VS.92).aspx

  • Silverlight for Windows Phone 是 Base on Silverlight 3,再加上对于装置互动使用上的相关功能 (例如 Launcher、chooser),且是以 OOB 的方式运作

开发资源

在应用程序的开发上,微软也提供了相当多的开发资源,笔者在这边列出一些常用的开发相关资源给各位参考

  • UI Design guide

    UI Design guide 中提供了接口设计的准则以及在接口设计上的参考数据,相当的重要,您可以到下面的位置下载 PDF 档案
    http://go.microsoft.com/fwlink/?LinkID=183218

  • Design resource for Windows Phone

    Design resource 的页面您可以找到 Design template for Windows Phone 7 以及其他的参考资源;而 Design template for Windows Phone 7 提供了 photoshop 的源文件,可做为开发/设计上的参考使用

  • Windows Phone 7 Jump Start

    Jump Start 是由国外的 MVP 所制作,对于 Windows Phone 开发上的一系列课程,您也可以前往参考

  • Application Bar icon for Windows Phone 7

    在 Windows Phone 7 的开发上,微软提供了一系列的 Icon 可以做开发使用,安装好开发工具之后,您可以在下面的位置找到

    64 bits OS
    C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Icons

    32 bits OS
    C:\Program Files\Microsoft SDKs\Windows Phone\v7.0\Icons

  • MSDN 文档库

    如果您是开发人员,相信对于 MSDN 文档库并不陌生,MSDN 中有大量的开发资源以及文件可供参考以及学习

  • MSDN 论坛

    在 MSDN 论坛中也提供了 Windows Phone 7 专属的讨论版,也欢迎各位朋友到上面进行相关的讨论

硬件概观

在以往 Windows Mobile 5.x/6.x 操作系统甚至早期的 Smart phone 时期,在硬件的规格以及制作上,都是由硬件厂商自行实作,并且修改相关的 Driver,非常的自由,但也因此造成了硬件规格上种类的繁多;而在应用程序的开发上也可能会遭遇在某机型上运作正常,而另外的机型运作不正常甚至无法运作的情形。

而到了 Windows Phone 7,在软件开发上,统一使用 managed code 进行开发,而硬件上也做了一定程度上的统一,相信能够很有效的减少开发上的成本以及开发上的兼容问题。以下简单的跟各位介绍硬件上的规格概观

  • 屏幕分辨率为 WVGA (800 * 480)
  • 电容式,以及支持最多 4 点的触控屏幕
  • 数位镜头 (camera)
  • A-GPS、light sensor、方向侦测、加速度 sensor…
  • 支援 wi-fi、3G、GPRS…
  • 256MB RAM (以上)、8GB storage (以上)
  • 有 3 个通用硬件按键 (返回/首页/搜寻)
  • More… (MSDN)

项目的基本档案结构

当一个新的项目建立之后,在项目中主要会有下列这些档案

Propertys\WMAppmanifest.xml

WMappManifest.xml 这个档案是记录了应用程序的相关属性描述,以及定义应用程序的功能性;如果您打开这个档案来看,会大致像下面这样的内容 (笔者仅节录部分内容)

<?xml version="1.0" encoding="utf-8"?>

<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.0">
  <App xmlns="" ProductID="{feff9099-9381-47fc-a91c-f64afe941b60}" Title="InputScopeDemo" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal"  Author="InputScopeDemo author" Description="Sample description" Publisher="InputScopeDemo">
    <IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
    <Capabilities>
      <Capability Name="ID_CAP_GAMERSERVICES"/>
      <Capability Name="ID_CAP_IDENTITY_DEVICE"/>
      <Capability Name="ID_CAP_IDENTITY_USER"/>
      <Capability Name="ID_CAP_LOCATION"/>
      <Capability Name="ID_CAP_MEDIALIB"/>
      <Capability Name="ID_CAP_MICROPHONE"/>
      <Capability Name="ID_CAP_NETWORKING"/>
      <Capability Name="ID_CAP_PHONEDIALER"/>
      <Capability Name="ID_CAP_PUSH_NOTIFICATION"/>
      <Capability Name="ID_CAP_SENSORS"/>
      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT"/>

相信您也可以概略的看出这个档案的用途,在一开始 (App 标签的部分) 是项目相关的属性,相关的说明如下

  • ProductID:代表应用程序的 GUID 字符串
  • Title:项目的默认名称,这里的文字也会显示在应用程序列表列表
  • RuntimeType:设定应用程序是 Silverlight 或是 XNA 的类型
  • Version:应用程序的版本编号
  • Genre:当应用程序为 Silverlight 时会为 apps.normal,apps.game 则为 XNA
  • Author:作者名称
  • Description:应用程序的描述 (说明)
  • Publisher:这个值预设会是项目的名称,当您的应用程序有使用到 Push 的相关功能,这个值是一定要有的
*注:这些属性您也可以在项目的属性页面中找到

接下来是 <Capabilities> 相关的区块,在这个区块中则是描述了应用能够使用的功能性,例如能不能使用网络的功能或是存取媒体柜 (Media library) 的内容;在一般的情形下,我们是不需要去修改到这个部分的,假设当你移除了某些功能,例如说移除了 WebBrowser 的部分

<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />

那么当你在程序代码中有使用到 WebBrowser 相关的功能时,程序便会出错了,而 unhandle exception 在 Silverlight for Windows Phone 中是会直接关闭应用程序的,所以你看到的情形就会是『疑?我的程序怎么自动消失了』。

再往下看会看到Task的标签,一般来说会像是下面这样

<Tasks>
  <DefaultTask  Name ="_default" NavigationPage="MainPage.xaml"/>
</Tasks>

这个是应用程序内部使用的,一般来说也不会去修改到,不过可以留意一下 NavigationPage 的部分,假设你的起始页面 (第一个显示的 Page) 名称不是 MainPage 的话,可以在这边做对应的修改。

再往下是 Token 的部分,例如下面

<Tokens>
  <PrimaryToken TokenID="TestToken" TaskName="_default">
    <TemplateType5>
      <BackgroundImageURI IsRelative="true" IsResource="false">Background.png</BackgroundImageURI>
      <Count>0</Count>
      <Title>Test</Title>
    </TemplateType5>
  </PrimaryToken>
</Tokens>

这边是设定当你的应用程序加到 Tile (也就是首页的地方) 之后相关的设定;要特别注意 TaskName 这个地方的设定是在Tile点下你的应用程序之后,会去启动应用程序的相关设定,跟上面提到 <Tasks> 中 Name 的属性是有相对应的关系的。

BackgroundImageURI 是当你的应用程序锚定到首页 (Pin to Start) 之后的背景图案,用下面这张图来看会比较清楚

而 <Count> 的标签是做甚么用途的呢?也直接来看一下图的效果,下面是 Count 设定为 0 跟 Count 设定为 5 时的分别

不过使用上较不会直接订死在配置文件中,这个部分陆续在系列中谈到 Push Notifications 的章节时会再谈到。

App.xaml

App.xaml 与 App.xaml.cs 这两个档案,预设包含了 Lifecycle (应用程序生命周期) 的相关事件处理,包含初次启动、失去焦点 (通常发生在使用者启动另外一个应用程序) 等等的相关处理;这部分在这系列中谈到生命周期的部分会再跟各位说明。而其中也可以去定义一些全局的资源 (resource) 或是全局的方法来使用,这部分就看您的应用程序如何去定义了。

MainPage.xaml

预设的起始页面档案

ApplicationIcon.png (63 * 63 pixels)

应用程序行表中使用的图形,如果没有指定则会缩小 Background 的图形作为列表的应用程序图形

Background.png (173 * 173 pixels)

  • 作为 Tile (位于首页上应用程序的链接) 上的应用程序的背景图案,支持 jpg、png 文件格式
  • 过大或是太小的图形将会被自动缩放

SplashScreenImage.jpg (480 * 800 pixels)

  • 应用程序启动后,Mainpage.xaml 加载完成之前的等待画面
  • 名称不能变动,编译选项必须为内容 (content)
*注:有关图示相关的部分您可以参考应用程序图标指南 (MSDN) 的说明

XAML ?

XAML在 Silverlight 的开发中占了举足轻重的地位,而 XAML 是什么呢?XAML 是基于 XML 发展出的一种描述语言;XML 本身的结构性非常的强,而 XAML 是基于这种特性,发展出来专门用于描述 Silverlight/WPF 的接口语言,相关的特性笔者大略归类出下列几点给各位参考

  • eXtensible Application Markup Language
  • 区分大小写
  • 基于 XML 发展出的宣告式语言
  • 用来设计 (设定) 显示的接口外观
  • 非常灵活、弹性且威力强大的设计方式
  • 设计工具的支持
    Visual Studio / Design / Blend
  • Xmlns ?

而在使用 XAML 时,常常会看到 xmlns 这样的关键词,这是什么意思呢?xmlns 使用上的感觉就像是引入命名空间;就像是 C# 中的 using xxxxx…或是 VB 中的 Imports xxxxx…;利用 xmlns 引入相关的命名空间后,在后续的 XAML 中就可以使用相关的控件或其命名空间中的类别;例如以 Application Bar 的使用 (在下节中会谈到使用的细节) 来说,必须要引入下面的命名空间

xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

xmlns 前置词表示后面的动作是要引入命名空间,而『:』后面接着的 shell 则是别名,等于代表了『=』号之后的 CLR 类别;引入了命名空间后,才能利用以下的方式在 XAML 中使用 Application Bar

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
            <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

了解了 XAML 的特性之后,我们来看一个简单的 Demo,看看在 Silverlight / XAML 中能够做到做到什么样子的效果;在这个 Demo 中,共有 3 个页面,第一个页面是 Button 的外观样式,笔者简单修改了一些 Button 的设定,便可以发现感觉就会大大的不同;而第二个页面是对于 Button 控件套用上转向效果,例如下面展示的样子

从这页面可以看出在 Silverlight 中,对于控件的设计是相当具有弹性的,在以往要将 button 的背景设定为渐层色得要花不少功夫才能达成;而现在,你只要简单的设定背景属性就行了 (较复杂的图形要利用 Blend 来设计控件的外观会比较方便);甚至改变 Button 的外观,方向的位置都是很轻松就能完成的。

例如一个具有渐层背景色的按钮,在 XAML 中会大概像这样

<Button Content="Button" Margin="8,100,227,0" Height="84" VerticalAlignment="Top" Foreground="Black">
    <Button.Background>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF0010FF"/>
                <GradientStop Color="White" Offset="0.664"/>
                <GradientStop Color="#FF6D73D2" Offset="1"/>
        </LinearGradientBrush>
     </Button.Background>
</Button>

从这个例子中,可以清楚的看出结构性带来的好处,首先最外层是 Button 控件的卷标描述,而中间包住的部分便是设定 Background 的属性了。

而第三个页面示范了可以对于按钮上附加一些动画的效果,对于应用程序的操作与互动上能够更有趣、丰富。而在动画的制作上,利用设计工具会是比较好的选择,能够预览效果以及比较直觉;而在动画的处理上 XAML 跟程序代码是可以相辅相成的,比如说利用 XAML 定义了一个 StoryBoard

<Storyboard x:Name="Storyboard1">
    <DoubleAnimation Duration="0:0:0.4" To="13" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" d:IsOptimized="True"/>
</Storyboard>

这个 StoryBoard 中定义了动画的变更是一个 Double 的值变化 (DoubleAnimation),以及动画的总时间 (Duraction 的部分),以及受影响的属性 (RenderTransform),但没有明确指定目标的元素,因此,我们可以在程序代码中设定元素名称,以及呼叫 StoryBoard 执行,就可以看到动画效果 (当然要直接在 XAML 中指定元素名称也是可以的),例如下面程序代码

private void button2_Click(object sender, RoutedEventArgs e)
{
    (Storyboard1.Children[0] as DoubleAnimation).From = 0;
    (Storyboard1.Children[0] as DoubleAnimation).To = 150;
    Storyboard.SetTargetName( Storyboard1,  "button1");
    Storyboard1.Begin();
}

而直接在 XAML 上指定元素名称的话会像下面这样子

<Storyboard x:Name="Storyboard1">
    <DoubleAnimation Duration="0:0:0.4" To="13" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" AutoReverse="True" Storyboard.TargetName="button1" d:IsOptimized="True"/>
</Storyboard>

程序代码也会简化如下

private void button2_Click(object sender, RoutedEventArgs e)
{
    Storyboard1.Stop();
            
    //(Storyboard1.Children[0] as DoubleAnimation).From = 0;
    //(Storyboard1.Children[0] as DoubleAnimation).To = 150;
    //Storyboard.SetTargetName( Storyboard1,  "button1");
    Storyboard1.Begin();

}

是不是很有趣呢?

Application Bar

Application Bar 是位于屏幕下面的功能列,其中提供了 IconButton 以及 MenuItem 两种控件可以使用,例如下图,左图是一般情形下 Application Bar 的外观,而右图是点下 Application Bar 之后展开项目的外观

在 Application Bar 的使用上,IconButton 一个页面上最多能够使用到四个,如果您要自定义这些图案的话,您也可以参考一下 UI Design Guide 的说明。而使用时跟一般按钮 (button) 相当的类似,最常需要处理的就是 click 事件了,例如下面的方式

private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
    textBox1.Text = (sender as ApplicationBarIconButton).Text;
}

MenuItem 的部分也是类似的处理方式,处理 click 事件的程序代码大致像下面这样

private void ApplicationBarMenuItem_Click(object sender, EventArgs e)
{
    textBox1.Text = (sender as ApplicationBarMenuItem).Text;
}

而 Application Bar 本身也有一些属性供程序开发使用,例如说,可不可以让 Application Bar 消失呢?可以的,只要设定 Visible 的属性就可以了,像是下面这段程序代码,是当 checkbox 被点击时,同步去设定 Visible 属性

void checkBox1_Click(object sender, RoutedEventArgs e)
{
    this.ApplicationBar.IsVisible = checkBox1.IsChecked.Value;
}

另外,还有一个很重要的地方需要注意;在 Application Bar 的属性中,可以利用 Opacity 的属性去设定透明度,设定透明效果之后,除了可以看到背景图案之外,不透明 (Opacity=1) 与具透明效果 (Opacity != 1) 这两种状态在『实际可使用的画面大小』是不一样的;笔者在这个例子中,是在 ContentPanel 的 sizechanged 事件中去抓取目前可用画面的大小

void ContentPanel_SizeChanged(object sender, SizeChangedEventArgs e)
{
    textBox1.Text = ContentPanel.ActualHeight.ToString();
}

挂载相关事件后,您可以看到再 Application Bar 的 Opacity=1 时,可用的大小是 535,而一改变 Opacity 的值时,可用空间马上就会变成 607 了,这个部分要留意一下。

Textbox – InputScope

文字输入框也是常会用到的控件之一,主要的用途便是文字数据的输入,而在装置上提供的是输入接口是触控屏幕,屏幕的大小也是受到硬件的限制,因此输入文字数据并不是那边的方便;InputSpcop 便是增加输入时的方便性而产生的;例如说,如果我们的文字框是用来输入数字用的,可以预先将输入用的软件键盘设定为数字型态,这样便可以方便使用者输入

而输入 email 位置时常常用到 .com、@ 等关键词,也可以预先指定软件键盘的输入方式为 email

其他还有输入电话号码用的软件键盘以及一般文字输入使用的软件键盘

透过预先设定的方式,就能提供给使用者更好的使用经验,以及方便使用者进行输入了;那么要怎么样来达到这个功能呢?主要就是要设定 Textbox 的 IputScope 属性,例如数字键盘的设定方式会像下面这样

numberBox.InputScope = new InputScope()
{
    Names = { new InputScopeName() { NameValue = InputScopeNameValue.Number } }
};

其余的输入方式笔者也一并列出给各位参考

emailBox.InputScope = new InputScope()
{
    Names = { new InputScopeName() { NameValue = InputScopeNameValue.EmailNameOrAddress } }
};

textBox.InputScope = new InputScope()
{
    Names = { new InputScopeName() { NameValue = InputScopeNameValue.Text } }
};

telBox.InputScope = new InputScope()
{
    Names = { new InputScopeName() { NameValue = InputScopeNameValue.TelephoneNumber } }
};

这样很简单的就能预先设定软件输入键盘的输入模式了;请记得,如果您使用了输入框 (textbox),而输入框所输入的是特定的数据 (例如 email),记得要设定 InputScope 属性,小小的动作,但是对于使用经验来说却是大大的不同呢。

Screen Orientation

在现在的装置中,都会具有方向侦测的 sensor,在装置的方向变化时都会做出相对的变化动作;那么对于应用程序的开发,应该要注意的部分约略有以下几点

  • 当 Windows Phone 转向时,装置会自动处理屏幕的方向变化
  • 当要从程序代码中指定显示方向时,要一并设定 SupportOrientation 的属性

接下来就来看看程序代码的部分;首先您可以在程序代码中指定您应用程序支持的屏幕方向,设定的方式会像下面这样

//應用程式只支援直立的顯示樣式
this.SupportedOrientations = SupportedPageOrientation.Portrait; 
//應用程式只支援橫向的顯示模式
this.SupportedOrientations = SupportedPageOrientation.Landscape; 
//應用程式同時支援兩種顯示模式,要注意 Layout 設定
this.SupportedOrientations = SupportedPageOrientation

如果不想在程序代码中设定,那么 XAML 一样是可以办得到的,您可以在页面的最上方 (<phone:PhoneApplicationPage> 卷标的属性中) 看到如下的程序代码

SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"

而当设定为 SupportedPageOrientation.PortraitOrLandscape (横向与直向同时支持,那么当屏幕方面改变的时候,就会自动将画面转向,要注意的是屏幕方向改变时,可用空间也会改变,Layout 的设定就很重要了 (有关 Layout 的部分在下节的容器控件中会提到)。

那么如果应用程序只支持横向的显示,那该怎么做设定呢?首先就是设定支持的方向,之后跟着设定屏幕的显示方向了,例如

this.SupportedOrientations = SupportedPageOrientation.Landscape;
this.Orientation = PageOrientation.Landscape;

要特别注意的是当您设定应用程序是两种方向都支持时 (SupportedPageOrientation.PortraitOrLandscape),这个时候去指定屏幕的方向是不会有作用的,因为这个时候是交由硬件的 sensor 来决定该如何显示画面的。实际效果可以参考下图

容器控件

在 Silverlight 中,常常会看到 Content 这个属性,其用途就是设定该元素的内容;而 Content 是 Object 的型态,也就是说 Content 要放什么东西进去都是可以的,如果想要的话,在 Button 里面装进一个 Listbox 也是办的到的。而 Content 有个限制,就是只能够放进一个东西,您的第一个反应可能会跟笔者一样,『那这东西能用吗?一点都不好用吧?』,容器便是因为这个原因存在的,而且利用各个容器的特性,也可以协助我们在画面的配置上能够达到自动调整的功能。接下来就跟各位介绍一下容器的使用方式。

Canvas

Canvas 跟以往在 Windows Form 开发上的使用经验相当的类似,它也是利用绝对寻址的方式来摆放控件,看到这边想当然就是要利用 Left 跟 Top 属性去设定了;但是要设定属性时,疑?怎么控件没有这两个属性呢?这是因为 Left 与 Top 是依存属性 (DependencyProperty) 的缘故,依存属性在后续会再谈到,目前在容器的使用上,您可以先想象成『设定相对于容器的 Left』这样子的感觉,所以设定的程序代码看起来大概像是这样子

Button newButton = new Button();
newButton.Width = 100;
newButton.Height = 100;
newButton.Content = "AAA";
//設定控制項的位置
Canvas.SetLeft(newButton, Convert.ToDouble(txtLeft.Text));
Canvas.SetTop(newButton, Convert.ToDouble(txtTop.Text));
//將新的控制項加入到容器中
ContentPanel.Children.Add(newButton);

结果可以参考下图,这边笔者是将 Canvas 设定了背景色,这样比较看得出 Canvas 的范围。

StackPanel

StackPanel 是一个会自动帮您排版的一个容器;依照预设的行为,StackPanel 会以『直』的方式来帮您排列控件,并且每一行只放一个控件,比如说下面的样子是在 StackPanel 上新增多个 Button 之后产生的样子

而 StackPanel 有个属性是用来设定控件的摆置方向的,另如下面的程序代码可以将方向由原本预设的直向改变为横向

ContentPanel.Orientation = System.Windows.Controls.Orientation.Horizontal;

效果会向下面这样

从这个地方您也可以看出 Silverlight 在自动排版的功能是很强大的。而不知道您有没有发现,当控件超出范围的时候,范围之外的控件是完全没办法使用的,尝试在 StackPanel 中去滑动,也没有效果,怎么会这样?这是由于 StackPanel 本身不像 ListBox 有直接支持 Scroll 的功能,不过解决的方式也很容易,我们只要用一个 ScrollViewer 去包住我们的 StackPanel 就可以了,修改之后的 XAML 会像下面这样

<ScrollViewer Grid.Row="1" Margin="12,0,12,0">
    <StackPanel x:Name="ContentPanel" >
        <Button Content="Add" Name="btnAdd" Click="btnAdd_Click" />
    </StackPanel>
</ScrollViewer>

这个就可以在画面上做到像 ListBox 一样的效果了,是不是很有趣呢。

WarpPanel

WarpPanel 是包含在 Toolkit 当中的控件,所以如果要使用这个容器的话要先安装 Toolkit,安装好之后就可以把这个容器加到工具箱中,加入时首先要在工具箱中新增项目

之后找到 WarpPanel 的项目

加入之后便可以在应用程序中去使用了;而 XAML 的部分会像下面这样,在 xmlns 的地方会多引入一个 toolkit 的命名空间

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

而 ContentPanel 部分的 XAML 修改成下面这个样子

<!--ContentPanel - place additional content here-->
<toolkit:WrapPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Button Content="Add" Height="113" Name="btnAdd" Width="131" Click="btnAdd_Click" />
</toolkit:WrapPanel>

执行之后的效果会像下面这样

从这边我们可以看出,WarpPanel 对于控件的排列跟 StackPanel 是相当类似的,只是它是以横向的方式来摆放控件而一列排满之后就移动到下一列。而与 StackPanel 相同,它一样有 Orientation 的属性可以设定,例如改成直向的排列方式

ContentPanel.Orientation = System.Windows.Controls.Orientation.Vertical;

之后效果就会变成像下面这样

Grid

Grid 这个容器是当专案开启时,预设的 ContentPanel 类型;Grid 提供的功能是最多的,但设定相对也较复杂一些;先来看看要怎么去增加 Grid 的列与栏的设定;要设定 Grid 的列与栏有很多设定方式,可以利用程序代码来增加,也可以利用 XAML;而在 Visual Studio 开发环境中,也可以直接去做设定,例如先点选了 Grid 之后,在 Grid 的上方以及左侧会出现蓝色的区域,像下图

当鼠标移动到这个区域之后会多出一条横线 (上方就会出现直线),这个时候点下鼠标左键就可以新增一列出来了;例如下图是新增三列之后的效果

新增好了之后来看看 XAML 的变化;你会发现在 XAML 的部分多了这些设定

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="80*" />
        <RowDefinition Height="80*" />
        <RowDefinition Height="80*" />
        <RowDefinition Height="353*" />
    </Grid.RowDefinitions>
</Grid>

这些设定是什么意思呢?首先在 <Grid.RowDefinitions> 的部分表示这里设定的是列的集合,而下面 <RowDefinition> 的地方就是定义每一个列的特性 (行为) 了;而在 Height 的属性这边,设定了像 80* 这样的数值,80 很容易理解,但是『*』是什么意思?『*』感觉上有点像是百分比的味道,以这个例子来说,80* 代表这个列最小必须要有 80,而实际的宽度会是

Grid 的总高度 * (此列的最小高度/具有 * 列的最小高度总和)

也就是

Grid 的总高度 * ( 80 / (80+80+80+353))

而如果没有 * 号例如说

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="80" />
        <RowDefinition Height="80*" />
        <RowDefinition Height="80*" />
        <RowDefinition Height="353*" />
    </Grid.RowDefinitions>
</Grid>

这样子的设定方式表示第一列固定高度是 80,而第二列呢?第二列最小的高度是 80,而实际高度会是

(Grid 的总高度 - 80) * ( 80 / (80+80+353))

再看一个例子,如果是下面这样呢?

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="80" />
        <RowDefinition Height="80" />
        <RowDefinition Height="80" />
        <RowDefinition Height="353*" />
    </Grid.RowDefinitions>
</Grid>

这样的方式表示第一列、第二列、第三列的高度都是 80,而第四列呢?第四列的最小高度是 353,而可用空间大于 353 时,第四列会填满剩余的空间。我现在利用这个例子,我们实际的放上控件来看看效果。

首先修改 XAML 的部分再每一列上放上一个按钮,像下面这样

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="80" />
        <RowDefinition Height="80" />
        <RowDefinition Height="80" />
        <RowDefinition Height="353*" />
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Content="Button1" Height="76" HorizontalAlignment="Center"  Name="button1" VerticalAlignment="Center" />
    <Button Grid.Row="1" Content="Button2" Height="76" HorizontalAlignment="Center"  Name="button2" VerticalAlignment="Center" />
    <Button Grid.Row="2" Content="Button3" Height="76" HorizontalAlignment="Center"  Name="button3" VerticalAlignment="Center" />
    <Button Grid.Row="3" Content="Button4" Height="76" HorizontalAlignment="Center"  Name="button4" VerticalAlignment="Center" />
</Grid>

在这边,我们可以看出,与 Canvas 一样的方式,要设定按钮位于哪一列的时候,是利用 Grid.Row=”1” 这样的方式去做设定的;而 button 的设定部分笔者这边加上了 HorizontalAligment 与 VerticalAlignment 的设定,分别设定在摆放 button 时是以置中的方式,下面是直式画面与横式时的样子

可以看出具有『*』号的第四列,其高度是会随着可用空间自动调整的。

接下来我们看一下 LayoutRoot 这个 Grid,LayoutRoot 这个 Gird 是建立项目时会自动产生的,是最外圈的 Grid 控件,包含了 Title 文字 ContentPanel 等等项目在其中,我们看一下下面这段

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

Auto ?设定成 Auto 是什么意思呢?当值设定成 Auto 时,表示该列的高度取决于在这一列中内容元素的大小,比如 Auto 这一列放进一个 Textblock,那么 Textblock 的 Height 是 100,那么这一列的高度也会是 100 了,它会去自动做版面上的调整。

最后还有一个 RowSpan / ColumnSpan 的属性可以做利用;例如说,当你想要跨越两个 Row (或是Column) 时,便可以利用这个属性,在 XAML 的部分看起来会像是下面这样

<Button Grid.Row="2" Grid.RowSpan="2" Content="Button3" Height="76" HorizontalAlignment="Center"  Name="button3" VerticalAlignment="Center" />

这样设定之后,就表示说 button3 这个控件是摆放在 Grid 的第 3 列 (列的号码是从 0 开始),而 button3 在做画面配置的时候是跨越第 3 列以及第四列的,所以依照 HorizontalAligment 等设置的不同,您会发现配置的位置也会跟着不一样了。

结语

容器使用的时候也可以做复杂的交叉运用,像是 Grid 可以分两列,第一列中摆放一个 StackPanel 进去,都是可以做的到的,端看您如何去做画面的配置与设计了;但有一点要留意,大量的元素相对来做也必须付出较大的执行成本 (要花时间去解译、画面显示等动作),所以设计画面时也必须要留意,在效能以及画面中取得平衡,不然缓慢的执行速度是会造成反效果的。

到此您可以看出 Silverlight+XAML 这样的应用程序设计模型,是非常强大而且具有弹性的,现在您不用等着第三方的组件,您可以轻易地设计出属于您自己的组件 (控件) 来使用!

第一集就跟各位介绍到这边了;马上动手来制作您的第一个 Hello Windows Phone 7 应用程序吧!

[范例档案下载]