你是不是在需要计数的时候,感到手指不够用?也许你正需要记录你朋友游了几圈,或者是举重了几次。也许你需要记录很长一段事件内,某件事发生了几次。比如,过去我没法统计我的妻子问过我多少次“我是不是看起来胖了?”,现在,有了Tally,我就可以进行统计了。
在这一章中,我们将编写一个名为Tally的应用。每当你点击一次屏幕,计数器都会加一。同时,还有一个reset按钮,用于清空计数器。它会一直保持计数,除非你按下reset按钮,或者卸载掉它。
不管这个应用销售情况如何,我必须承认Tally不是一个吸引人的应用。尽管如此,它足够简单,可以很好地介绍Windows Phone的开发。相较于其他章,本章不太关注应用本身,而更关注在Visual Studio中创建的Windows Phone项目的结构和基本特性。
|
为什么Windows Phone的应用看起来都这么简单? 这是基于艺术的选择。Windows Phone及其应用都被设计来简单明了地表达信息。就好像是机场,车站里面的标志。微软称这种风格为Metro。标准的Metro风格偏好大块地留白,使用简单的单色图标来突出信息。 不同于iPhone,iPhone强调的是明亮,有立体感的视觉效果。iPhone鼓励模拟真实世界的视觉效果(比如记事本应用看起来就像是一个真实的纸质记事本的效果),而Windows Phone则不鼓励刻意地模仿真实世界的视觉效果。相反,除了游戏和一些新奇的应用以外,Windows Phone要求应用看上去更“数字化”。
|
|
为什么大多数WP7应用都使用的是黑底白字? 这同样也是基于艺术的选择。黑色不仅更加时尚,同时也有现实的意义。不少Windows Phone使用了OLED的屏幕。这种屏幕可以有效地利用电池(不需要背光源),电池使用时间依赖于屏幕的颜色和亮度。所以在这种屏幕下,黑底白字比白底黑字更省电。
|
分解一个“Windows Phone Application”的项目
当你在Visual Studio中建立一个新的“Windows Phone Application”项目时,你已经得到了一个完整的应用。它可以直接编译到.xap文件中,或者发布到模拟器/手机上。当然它除了显示一点文字外,什么都不会做。不过,其中已经包含了很多基础结构。在开始编写Tally应用前,我们先理解下,一个新的“Windows Phone Application”的各个组成部分。
|
Visual Studio根据主屏幕上显示控件的不同,提供了一些更复杂的应用模板:databound (list) application,panorama application,和pivot application。基本上本书所有的应用都是基于最基础的“Windows Phone Application”创建的,在上面添加一个databound list,panorama或者pivot控件都是很简单的。 |
关于.xap .xap是由Silverlight引入,在XNA应用也同样得到使用。它仅仅是一个.zip文件。如果你把后缀名改成.zip的话,你就可以像.zip文件一样查看里面的内容。一个.xap文件里面包含了一些文件:编译过后的DLL,清单,图片,可能还有其它一些没有编译进DLL的资源文件。
|
|
应用的清单
WMAppManifest.xml(其中WM部分来至于已经out了的“Windows Mobile”术语)是应用的清单。它向操作系统描述了应用的相关信息:名字,外表,如何开始,允许的功能等等。下面就是VS产生的命名为“Tally”的新项目中的对应文件,位于Properties文件夹中。
WMAppManifest.xmlWMAppManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment"
AppPlatformVersion="7.0">
<App xmlns="" ProductID="{2f711986-cfb4-40d3-9b7d-64aa37faf338}" Title="Tally"
RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal"
Author="Tally author" Description="Sample description" Publisher="Tally">
<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"/>
</Capabilities>
<Tasks>
<DefaultTask Name ="_default" NavigationPage="MainPage.xaml"/>
</Tasks>
<Tokens>
<PrimaryToken TokenID="TallyToken" TaskName="_default">
<TemplateType5>
<BackgroundImageURI IsRelative="true" IsResource="false">
Background.png
</BackgroundImageURI>
<Count>0</Count>
<Title>Tally</Title>
</TemplateType5>
</PrimaryToken>
</Tokens>
</App>
</Deployment>
这个应用的清单是个奇怪的文件,因为在Marketplace审核过程中,大多数设置都会被修改。因此,由Marketplace下载到的版本和你本机的版本将会不一样。
App的元素包括了:ProductID,是一个唯一标识你的应用的GUID;RuntimeType,指出了这是一个Silverlight应用还是XNA应用;Title,用于显示你安装的应用(在app list中或者在Games hub中);剩下的4个属性只是用来在Marketplace中列出你的应用。但是这些值(包括Title)在你提交应用的时候,都会被重写。
Genre影响到你的应用的安装位置。如果是apps.normal则会放置到普通的app list中,如果是apps.games则会放置到Games hub中(Silverlight应用也可以放置到Games hub中)。一个应用只能被安装到其中一个位置。一般开发的时候设置成apps.normal会比较方便,应为模拟器中没有包含Games hub。当你将应用提交到Marketplace的时候,这个值也会根据你在网页上的选择进行重写。
IconPath是关于图标的设置。Tasks是指出你的应用从哪个Silverlight页面开始运行。Tokens中则是关于Tile的信息(当用户把你的应用放在他们手机的首页上的时候就会显示相关信息)。
另一个清单 Visual Studio项目中包含了第二个清单文件AppManifest.xml,同样也在Properties文件夹中。这个是Silverlight的基础结构需要的,你接触不到这个文件。
|
|
功能(Capabilities)
WMAppManifest.xml中最有趣的部分就是关于功能(Capabilities)。其中包含了应用的行为许可,但是用户却不一定希望应用产生这些行为。比如涉及到隐私(用户的位置信息),或者可能产生费用(拨号,发送短信)。Visual Studio中产生的清单包含了所有可用的功能。你可以尝试修改这个列表,看看你的应用的哪些行为会受到限制。不过这没有什么意义,Marketplace的验证程序会自动侦测你的应用需要哪些功能,然后重写一个最小的功能清单。
在Marketplace中,当用户在考虑是否下载你的应用的时候,会被告知你的应用需要获取哪些功能许可。每个功能都有个简单易懂的名字,比如ID_CAP_LOCATION在Marketplace中是“location services”。用户在下载你的应用之前,必须进行这样的许可。这种定位服务就需要用户进行明确地许可。在下载这种应用前,Marketplace会提醒用户是否同意发送他们的位置信息。
|
一旦你的程序已经运行了起来,你就不需要判断是否获得了相应的功能许可(也没有相应的API)。因为,如果你的应用已经在运行了,就意味着它已经获得了所有相关功能许可。这些许可是不能被撤销的。 |
|
ID_CAP_NETWORKING是唯一一个需要进行手动请求的功能 一般而言,除了这个功能以外 ,你都可以让Marketplace的验证程序来处理这个功能列表。Marketplace不能确定你的应用是否会用到手机的网络功能。如果ID_CAP_NETWORKING被列在了你的功能列表中,那么不管你是否会用到网络功能,你都会获得这个许可。反之亦然。
|
用户不需要对所有功能都进行许可,你也不必操心是否某个功能得到了许可。只需要记住下面几点:
如果你的应用已经在运行了,那么这就意味着你的应用已经获得了所有许可。
如果你的应用已经从Marketplace上成功下载,功能清单里面就会包含所有你的应用需要的功能了。(除了上面提到的ID_CAP_NETWORKING)
|
你需要注意限制应用所需要的功能,从而提高应用的竞争力。比如,如果你的小费计算器需要使用手机的数据连接,那么用户很可能就不会购买了。所以,如果你不需要使用网络,记得去掉ID_CAP_NETWORKING。 你最好使用一下Windows Phone Capability Detection Tool(C:\Program Files\Microsoft SDKs\Windows Phone\v7.0\Tools\CapDetect 文件夹下)。它会和Marketplace一样对你的应用进行功能侦测。不过和Marketplace一样,对ID_CAP_NETWORKING的判断都不太准确。
|
|
为什么我修改来功能列表之后,我就不能在真机上调试我的应用了? 这个和讨厌的ID_CAP_NETWORKING有关。没有了ID_CAP_NETWORKIN,调试器也不能和手机通讯了。所以开发的时候要保留它,等正式发布的时候再去掉它。
|
|
我要如何才能在自己的游戏中利用Xbox LIVE的特性? 有些功能是只向移动运营商和手机制造商这种大神开放的,你我这种凡人都是用不了的。ID_CAP_GAMERSERVICES也是这种特殊功能之一。它可以使用Xbox LIVE API,但是只针对获得了微软许可的游戏。如果你想知道自己都错过了什么,你可以使用Visual Studio的Object Browser来查看Microsoft.Xna.Framework.GamerServices程序集,了解Xbox LIVE的功能。除非你注册成为了Xbox LIVE的开发者,或者通过某些特殊的途径使用上Xbox LIVE,否则其中的大多数功能都会抛出NotSupportedException的异常。
|
图片
由Visual Studio创建的项目中已经包含了3张图片:
ApplicationIcon.png-主图标,不管应用被安装到哪里,都会显示这个图标。如果是普通的应用(放在手机的app list中),图标应该是62x62像素以避免缩放。如果是游戏(放在Games hub中),图标应该是173x173像素。
Backgroud.png-Tile图标(173x173),当用户将应用放置在手机的起始页面时显示。为什么一个图标的名字会叫Background呢,因为它是作为tile的背景。在程序清单中设置的标题会自动覆盖到tile的左下角,所以在设计这个图片的时候要为标题预留下空间。
SplashScreenImage.jpg-闪屏图片(480x800)会在应用启动时显示。
这些图片可以是JPEG或者PNG格式。你可以任意修改,同时记得要在你的程序清单中也进行对应的修改。
|
如果你想设计一个符合Windows Phone内置程序风格的图标,它必须使用透明的背景,同时里面的图形应该是: ➔ 白色 ➔ 简单的几何图形 ➔ 如果可能的话,尝试重新组合Windows Phone已经使用过的图形 ➔ 符合我们实际生活的比喻
62x62大小的图标需要留出12像素的白边,换句话说,实际只有图片中央的38x38像素包含内容。173x173的图标则使用73x73的内容,偏上3个像素放置。上方留47像素的空白,下方留出53像素,左右各留50像素。
当你想设计一个特别点的图片时,也许想要缩小留白的大小。一般情况下,Background.png应该和ApplicationIcon.png是一致的,只有大小上的区别。游戏的话,则不用依照上面的设计指导。
设计这样一个图标需要耐心和练习。你可以使用像PAINT.NET这样的工具。同时Wingdings和Webdings字体也会对你的设计有所帮助。
微软并没有给出严格的,或者官方的指导。大多数情况下,只要看上去不错就行了。
|
|
我要如何才能使用用户主题的背景色来作为图标的背景色,就和内置程序一样? 每个tile图标都是放置在以用户主题的背景色作为背景的方块上面。所以你只需要保证你使用的是透明背景的PNG文件就可以了。但不幸的是,在app list中的时候,每个第三方的应用图标都是放置在黑色背景的方块上面的,所以在app list里面就没法达到内置程序一样的效果了。这种情况下,你可以使用一个有背景色的图标。
|
|
Marketplace中的图标和你应用真正的图标有不一样的设计指导 一般鼓励你设计Tile图标时使用透明背景。但是当你上传应用到Marketplace的时候,要避免使用透明背景的图标。在手机的Marketplace中,图标是放置在黑色背景的方块上面的。而在Zune中,默认情况下,图标都是放置在白色背景下。如果是一个标准的Windows Phone风格的图标的话,透明背景里面是白色的图形,就会完全看不见。 所以,你必须选择一个有背景色的图标作为Marketplace中的图标。
|
|
图标和闪屏图片需要将生成操作(Build Action)设置为内容(Content) 当你对这3个图片进行替换时,要确保每个文件的Build Action都被设置为了Content而不是默认的Resources。这样,这些文件就会直接部署到.xap文件中,而不是编译进DLL中。 Copy to Output Directory就不重要了,不管你选什么,最终都会复制到生成的.xap文件中。
|
MainPage.xaml
每个应用都是由一个或多个页面构成的。一个新建的项目生成MainPage这个页面。这个页面定义了用户载入应用之后可以看见什么。由MainPage.xaml(包含了UI)和MainPage.xaml.cs(包含了业务逻辑)两部分组成。
|
记得要在WMAppManifest.xml中引用MainPage.xaml 如果你重命名了MainPage.xaml,记得要在WMAppManifest.xml中修改对应的引用。
|
MainPage.xamlMainPage.xaml
<phone:PhoneApplicationPage
x:Class="Tally.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="page name" Margin="-3,-8,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentGrid" Grid.Row="1">
</Grid>
</Grid>
<!-- Sample code showing usage of ApplicationBar
…
-->
</phone:PhoneApplicationPage>
简单的观察这个文件,我们可以得知:
➔ 这是一个名为MainPage的类(位于Tally命名空间下),派生于PhoneApplicationPage控件。
➔ 它只支持肖像(垂直)方向。
➔ 包含了两个文本块,分别代表应用的名称和页面的标题。
➔ 这个页面使用了Grid和StackPanel控件来进行页面布局。除开两个文本块,其余的内容都被放置在名为ContentGrid的grid中。
➔ 即使只是一个很简单的页面,也包含了许多信息。
我们会进一步深入:
➔ 文件开始使用的XML命名空间。
➔ 通过类似{StaticResource XXX}的方式来调用手机主题相关的资源。
方向是一个有趣同时也很重要的主题,在第三章中会加以讨论。这个页面也提供了关于如何使用应用工具条的方法(被注释在页面的最后),但是这章的Tally是少数几个不需要使用工具条的应用之一。下一章才会涉及到相关内容。
XML命名空间
MainPage.xaml包含了本书会涉及到的绝大多数XML命名空间。尽管他们看上去像是URL,但他们实际上是一些特殊程序集的命名空间。
命名空间 前缀 描述
http://schemas.microsoft.com/ 无 标准的Silverlight命名空间,包含了诸如Gird,
winfx/2006/xaml/presentation Button和TextBlock等元素。
http://schemas.microsoft.com/ x XAML语言的命名空间。包含了诸如Class,
winfx/2006/xaml Name和Key等关键字
clr-namespace:Microsoft.Phone. phone 手机特有的Silverlight控件的命名控件,包含了
Controls;assembly=Microsoft.Phone PhoneApplicationPage等。
clr-namespace:Microsoft.Phone.Shell; shell 这个命名空间是关于手机上不同于基础
assembly=Microsoft.Phone Silverlight元素的部分:status bar
和application bar
http://schemas.microsoft.com/ d 这个命名空间提供设计时的信息,可以为
expression/blend/2008 Expression Blend和Visual Studio提供预览。
http://schemas.openxmlformats.org/ mc 可以标记的兼容性命名空间。可以用来标记其他
markup-compatibility/2006 命名空间或元素可以被忽略。一般在设计时使
用,在运行时会被忽略。
前面的三个命名空间基本上总会用到。shell命名空间只用在页面使用了ApplicationBar类或者status bar(设置SystemTray.IsVisible为True)的时候。status bar是手机的顶部区域,用于显示时间,信号强度,电量等信息。作为开发者,你只能显示或隐藏它。
最后的两个命名空间,会在每个页面里插入mc:Ignorable,d:DesignWidth,和d:DesignHeight,是页面很混乱,所以本书的很多例子都移除了这两个命名空间。这会在Visual Studio的设计视图和Expression Blend产生负面的影响。如果影响到了使用,可以添加回这两个命名空间。当你对页面进行修改的时候,Visual Studio也会自作主张地加入它们。
|
如果你纠结于Visual Studio打开XAML文件时的龟速,同时你也不需要对页面进行预览。那么,你可以考虑修改默认的XAML编辑器。在解决方案资源管理器(Solution Explorer)中,右键点击XAML文件,选择打开方式(Open With),XML(Text)Editor,点击设为默认值(Set as Default),然后点击OK。这样会导致一些问题,比如没有了智能感知,但是速度会快上许多。
|
手机主题资源
不同于硬编码的字体,字号大小和颜色,MainPage.xaml通过StaticResouce来调用手机特有的资源。Windows Phone定义来一些资源,使得应用可以与用户的主题保持协调一致。这些资源不仅包括特别的颜色,刷子,字体,字号和厚度(关于边框和margin/padding),同时还包含一些针对textblock的样式。
在这个页面里使用的资源是:
➔ PhoneFontFamilyNormal — Segoe WP
➔ PhoneFontSizeNormal — 20 px (15 pt)
➔ PhoneForegroundBrush — 一个填充刷子,在黑色主题下是白色,在白色主题下市黑色
➔ PhoneTextNormalStyle — 前面3个的组合:PhoneFontFamilyNormal的FontFamily,PhoneFontSizeNormal的字体大小, 和PhoneForegroundBrush的前景色
➔ PhoneTextTitle1Style — PhoneFontFamilySemiLight的FontFamily (Segoe WP Semilight), PhoneFontSizeExtraExtraLarge的字体大小(72 px, which is 54 pt), 和PhoneForegroundBrush的前景色
|
避免使用硬编码的颜色,除非已经包含进来主题的颜色 你应当尽可能地尊重用户的主题(以及Windows Phone的设计指导)。如果你想完全控制色调,就必须考虑用户主题可能会造成的影响。比如,使用一个硬编码的白色文字,在黑色主题默认背景下看上去没问题。但是如果是在白色主题下,则根本看不见。在黑色图片上使用标准的按钮,在黑色主题下也没问题(因为按钮的文字和边框都是白色的)。但是在白色主题下面几乎就看不见(因为按钮的文字和边框都是黑色的)。
|
|
Outlook总是使用白色主题而IE总是使用的黑色主题,而无视用户设置。我自己的应用也能自由选择主题吗? 基本不能。你当然可以使用自己的颜色,而无视或者覆盖掉主题资源,但你不能修改状态条的颜色,只能隐藏它。
|
|
Segoe WP字体,及其变体,是专门为Windows Phone而生的。在系统和应用的每个地方都在用它。所以,除非有非常明确的目的(比如编写的是游戏或者是个阅读器),使用其他的字体都会看起来很奇怪。
|
MainPage.xaml.cs
MainPage.xaml.csMainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
namespace Tally
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
}
}
|
永远不要删除代码后置文件的构造函数中对InitializeComponent的调用 InitializeComponent将XAML声明的内容和类运行时的实例关联起来。
|
App.xaml和App.xaml.cs
App.xaml是一个特殊的XAML文件,没有定义任何的可见性,但是它定义了一个App的类,可以完成程序级的任务。一般而言,修改这个文件的唯一原因,就是要设置一些程序级的资源(比如用户样式)在它的Application.Resource节点中。
App.xaml.cs引人注目的工作就是处理来程序生命周期里面的事件(Launching,Activated,Deactivated和Closing)。
AssemblyInfo.cs
在本书中,这个文件没有什么价值。它包含了一组属性(设置标题,描述,公司名称,版权声明等等),最终会编译到你的程序集中。因为MarketPlace中的所有信息都不是在这里管理的,所以没有必要设置它们。
尽管如此,AssemblyVersion和AssemblyFileVersion(一般都设置相同的值),可以用来区分程序的不同版本。
修改默认项目创建“Tally”
现在,我们了解来新创建的Visual Studio项目都包含来什么,可以开始修改它来创建Tally了。
首先,我们可以移除所有应用清单中的功能,只暂时保留ID_CAP_NETWORKING用来在手机上测试我们的应用。
<Capabilities>
<!-- None needed -->
<!-- TODO: This is only for debugging on a device: -->
<Capability Name="ID_CAP_NETWORKING" />
</Capabilities>
我们也可以修改这两个图标文件,同时删除闪屏图片。现在就准备开始修改MainPage.xaml和MainPage.xaml.cs了。
更新UI
修改后的MainPage.xaml修改后的MainPage.xaml
<phone:PhoneApplicationPage
x:Class="Tally.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeHuge}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait"
shell:SystemTray.IsVisible="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Row 0: The Header -->
<StackPanel Grid.Row="0" Margin="24,24,0,12">
<TextBlock Text="TALLY" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="tap to count" Margin="-3,-8,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!-- Row 1: The text block containing the count -->
<TextBlock x:Name="CountTextBlock" Grid.Row="1" TextAlignment="Center"
Text="0"/>
<!-- Row 2: The reset button -->
<Button x:Name="ResetButton" Grid.Row="2" Click="ResetButton_Click"
Content="reset"/>
</Grid>
</phone:PhoneApplicationPage>
➔ 不需要的属性都被删掉了,顶部的两个text block也更新了,旧的ContentGrid也被替换成了text block和按钮。grid中加入了第三行用来盛放按钮。
➔ text block和reset按钮分别赋予了CountTextBlock和ResetButton的名字,这样就可以方便地在代码后置文件中引用。
➔ 页面的字体大小修改为PhoneFontSizeHuge(186.667px/140pt)。这个值仅影响到CountTextBlock,其它的text block都使用来自己的样式。ResetButton中的文字没有继承到页面级的文本属性。
➔ 重置按钮有一个Click事件,触发后台声明的ResetButton_Click。
更新代码后置文件
代码后置文件是Tally工作所需要的逻辑部分。
修改后的MainPage.xaml.cs修改后的MainPage.xaml.cs
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using WindowsPhoneApp; // For the Setting class
namespace Tally
{
public partial class MainPage : PhoneApplicationPage
{
int count = 0;
// Remember what the user typed, for future app activations or launches
Setting<int> savedCount = new Setting<int>("SavedCount", 0);
public MainPage()
{
InitializeComponent();
}
// Handle a tap anywhere on the page (other than the Button)
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
this.count++;
}
// Handle a tap on the button
void ResetButton_Click(object sender, RoutedEventArgs e)
{
this.count = 0;
this.CountTextBlock.Text = this.count.ToString("N0");
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// Persist state when leaving for any reason (Deactivated or Closing)
this.savedCount.Value = this.count;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Restore persisted state
this.count = this.savedCount.Value;
this.CountTextBlock.Text = this.count.ToString("N0");
}
}
}
|
为什么Windows Phone会使用鼠标事件(MouseLeftButtonDown)?手机就没有鼠标! 这是一个和桌面版本的Silverlight兼容性有关的问题。尽管确实有touch和mutli-touch这些事件,简单的单个手指的行为,一般还是看作鼠标的事件(比如MouseLeftButtonDown,MouseMove和MouseLeftButtonUp)。这是因为,这些事件工作起来比touch事件要简单。所以很多应用是使用这种听上去很奇怪的事件。把你的手指看作鼠标指针,屏幕看作左键,这样就好理解点了。
|
➔ 一般情况下,一个类如果提供了XXX事件的话,同时也会提供OnXXX方法,供子类来重写。通过重写OnMouseLeftButtonDown,当触碰到页面的任何地方的时候(除了按钮),代码都可以有效地处理页面的MouseLeftButtonDown事件。这正好是我们需要触发计数加一,同时更新TextBlock属性的条件。因为按钮内部处理这个事件来避免特别的行为,所以在触碰按钮的时候,又不会触发这个方法。这样可以防止按钮的触碰,冒泡到页面上。
➔ “N0”是计数转换为字符串时候的格式。表示“按自然数显示(有千位分隔符)”。千位分隔符一般应用于United States English,但是会根据手机的“region & language”设置来自动变化。
➔ 按钮有自己的事件来处理触碰(Click),用来替代原本的MouseLeftButtonDown事件。当触碰的时候,按钮不会被触发;当抬起的时候,才会触发。按钮在按下的时候会高亮自己,当手指离开的时候则恢复同时触发Click事件。这个按钮的Click事件(ResetBuuton_Click)重置了计数,同时更新TextBlock的值。
➔ 在OnNavigatedFrom/OnNavigatedTo方法中,使用的Setting成员(savedCount),提供了一种简单的方法来存储应用的状态。当用于离开应用的时候,记录下计数;当用户回来的时候恢复计数(甚至用户重启手机也可以恢复)。
➔ 记得更新TextBlock。让文本保持随计数变化而变化,是很容易出错的地方,特别是在更大的应用中。使用从属属性(dependency property)会更简单而不易错。为了保持简单,暂时不使用这种方法。
|
为什么Click事件是在手指离开的时候触发,而不是按下的时候触发? 虽然乍一听起来会显得奇怪,但这不论在桌面还是手机平台上都是很标准的行为。好处就在于,这种方式可以给用户一个反悔的机会(比如按到了错误的按钮),只要简单的将手指移到按钮外面,就可以撤销掉。尽管如此,按钮也支持另外两种Click模式。如果你设置按钮的ClickMode为Press,按钮一被按下就会触发Click。如果设置为Hover,则手指滑过它的时候,就会触发Click事件了。你应该尽量避免使用这些模式,这会使用户变得很困惑。
|