<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Allen Lee's Magic</title><subtitle type="text">这里没有答案，顶多给你几个值得一试的猜想。</subtitle><id>http://feed.cnblogs.com/blog/u/6996/rss</id><updated>2011-12-10T08:25:57Z</updated><author><name>Allen Lee</name><uri>http://www.cnblogs.com/allenlooplee/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/allenlooplee/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/6996/rss"/><entry><id>http://www.cnblogs.com/allenlooplee/archive/2011/12/10/2283356.html</id><title type="text">WP7有约（八）：在ListPicker控件的选择页面上播放铃声</title><summary type="text">WP7有约（八）：在ListPicker控件的选择页面上播放铃声 Written by Allen Lee 上节课我们在ListPicker控件的选择页面上实现了播放图标的效果，随后sjcxyf同学又发现了新的问题：我在项目里面添加了一个MP3文件，然后我在页面加了一个MediaElement控件，我在Image_Tap事件里面添加了播放音乐的代码，但是表现出来的结果是当选择页面弹出来之后点击播放图标不能播放音乐，我试图尝试在选择模板里面加入MediaElement控件来实现，但是最后结果还是一样。 在Silverlight for Windows Phone里，MediaElemen...</summary><published>2011-12-10T08:00:00Z</published><updated>2011-12-10T08:00:00Z</updated><author><name>Allen Lee</name><uri>http://www.cnblogs.com/allenlooplee/</uri></author><link rel="alternate" href="http://www.cnblogs.com/allenlooplee/archive/2011/12/10/2283356.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/allenlooplee/archive/2011/12/10/2283356.html"/><content type="html">&lt;p style="text-align: center;"&gt;&lt;span style="color: #0070c0; font-size: 20pt;"&gt;&lt;strong&gt;WP7有约（八）：在ListPicker控件的选择页面上播放铃声 &lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p style="text-align: right;"&gt;&lt;span style="color: #0070c0;"&gt;&lt;strong&gt;&lt;em&gt;Written by Allen Lee &lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/12/07/2280008.html"&gt;上节课&lt;/a&gt;我们在ListPicker控件的选择页面上实现了播放图标的效果，随后sjcxyf同学又发现了新的问题：&lt;/p&gt;&#xD;
&lt;p style="margin-left: 36pt;"&gt;我在项目里面添加了一个MP3文件，然后我在页面加了一个MediaElement控件，我在Image_Tap事件里面添加了播放音乐的代码，但是表现出来的结果是当选择页面弹出来之后点击播放图标不能播放音乐，我试图尝试在选择模板里面加入MediaElement控件来实现，但是最后结果还是一样。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在Silverlight for Windows Phone里，MediaElement有一个很特别的限制，你必须把它添加到可视化树，否则它不会播放。假设我们在铃声设置页面上添加一个播放按钮，如图1所示，接着在这个按钮的Click事件处理程序里创建MediaElement对象，然后通过它播放预先添加的铃声（这个铃声文件的Build Action属性的值是Content），如代码1所示。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201112/20111210155002299.png" alt="" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;图 1 铃声设置页面 &lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;代码 1 通过MediaElement对象播放铃声&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Button_Click(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, RoutedEventArgs e)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; media = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; MediaElement();&lt;br /&gt;    media.Volume = &lt;span style="color: #800080;"&gt;1&lt;/span&gt;;&lt;br /&gt;    media.Source = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Uri(&amp;ldquo;Windows Logon Sound.wav&amp;rdquo;, UriKind.Relative);&lt;br /&gt;}&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;运行这个应用程序，然后单击播放按钮，你会发现什么声音也没有。但如果我们在创建MediaElement对象之后把它添加到LayoutRoot里，如代码2所示，那么单击播放按钮就能听到声音了，因为此时MediaElement对象已在可视化树上了。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;代码 2 把MediaElement对象添加到LayoutRoot&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Button_Click(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, RoutedEventArgs e)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; media = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; MediaElement();&lt;br /&gt;    LayoutRoot.Children.Add(media);&lt;br /&gt;    media.Volume = &lt;span style="color: #800080;"&gt;1&lt;/span&gt;;&lt;br /&gt;    media.Source = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Uri(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Windows Logon Sound.wav&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;, UriKind.Relative);&lt;br /&gt;}&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这正是为什么我们经常看到别人在XAML里创建MediaElement对象，然后在代码隐藏里调用它的Play方法播放音频。不过，如果你试图把代码2照搬到Image控件的Tap事件处理程序里，你会发现还是听不到声音，为什么呢？&lt;/p&gt;&#xD;
&lt;p&gt;当我们单击ListPicker控件时，它会打开一个新的选择页面，此时，由于铃声设置页面不再可见，不用渲染，Silverlight for Windows Phone会把它从可视化树上摘除，因此，我们在Image控件的Tap事件处理程序里把MediaElement对象添加到铃声设置页面的LayoutRoot里就失去意义了。&lt;/p&gt;&#xD;
&lt;p&gt;那么，如果我们在选择页面的项目模板里创建MediaElement对象又会怎样呢？毫无疑问，这会导致同一个页面存在多个MediaElement对象，而&lt;a href="http://msdn.microsoft.com/en-us/library/ff426928(v=VS.96).aspx"&gt;Silverlight for Windows Phone并不支持这种用法&lt;/a&gt;，因此这条路是行不通的。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;从上面的分析不难看出，MediaElement的最大问题在于它对可视化树的依赖，那么，有没有不依赖于可视化树的解决方案？有的，我们可以借用XNA的SoundEffect：&lt;/p&gt;&#xD;
&lt;p&gt;1) 添加Microsoft.Xna.Framework库的引用，如图2所示。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201112/20111210155003988.png" alt="" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;图 2添加Microsoft.Xna.Framework库的引用&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;2) 添加XNA命名空间的引用，如代码3所示。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;代码 3 XNA命名空间&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt; Microsoft.Xna.Framework;&lt;br /&gt;&lt;span style="color: #0000ff;"&gt;using&lt;/span&gt; Microsoft.Xna.Framework.Audio;&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;3) 把Image控件的Tap事件处理程序改为代码4所示的那样。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;代码 4 通过XNA的SoundEffect播放铃声&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Image_Tap(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, System.Windows.Input.GestureEventArgs e)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;using&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; stream = TitleContainer.OpenStream(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Windows Logon Sound.wav&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;))&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; audio = SoundEffect.FromStream(stream);&lt;br /&gt;        FrameworkDispatcher.Update();&lt;br /&gt;        audio.Play();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    e.Handled = &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;;&lt;br /&gt;}&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;代码4所做的事情很简单，它以流的形式打开铃声文件，然后用这个流对象创建SoundEffect对象，并更新XNA的组件状态，最后播放铃声。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一般情况下，当我们提到Windows Phone应用程序的存储区域时，很多人会第一时间想到独立存储区，不过，Windows Phone应用程序还有另外一个存储区域，这个区域就是应用程序的安装文件夹，和独立存储区不同的是，安装文件夹里面的内容是只读的。当我们在Visual Studio的属性窗口里把内容文件的Build Action属性的值设为Content时，比如前面那个铃声文件，安装程序会把它们部署到安装文件夹里。事实上，Windows Phone SDK 7.1的LINQ to SQL就通过"appdata"和"isostore"在连接字符串里区分这两种区域。&lt;/p&gt;&#xD;
&lt;p&gt;XNA提供的TitleContainer.OpenStream方法可以用来读取安装文件夹里的内容，如果你的应用是从网上下载铃声的，那么这些铃声将会保存在独立存储区里，这意味着代码4里打开文件流的代码应该换成对应的独立存储访问代码。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://files.cnblogs.com/allenlooplee/WindowsPhoneApplication1.zip"&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201112/201112101550038828.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/allenlooplee/aggbug/2283356.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/12/10/2283356.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/allenlooplee/archive/2011/12/07/2280008.html</id><title type="text">WP7有约（七）：实现铃声设置的播放图标的效果</title><summary type="text">WP7有约（七）：实现铃声设置的播放图标的效果 Written by Allen Lee sjcxyf同学通过站内消息提到这样一个问题：我现在想做一个功能就是当ListPicker弹出全屏幕的时候每一项前面是一个播放图片，后面是音乐名称，然后我点击前面的播放的时候播放当前的音乐，不让他选择这一项的值并返回 要选择后面的字之后才返回 就是像Phone 7真机里面设置铃声那种效果怎么做？ 我们来看一个山寨版的铃声设置，如图1所示，无论用户单击铃声名字还是它左边那个播放图标，都会关闭ListPicker页面。sjcxyf同学想要的效果是单击播放图标将会播放对应的铃声，而单击铃声名字才是确认选...</summary><published>2011-12-07T15:10:00Z</published><updated>2011-12-07T15:10:00Z</updated><author><name>Allen Lee</name><uri>http://www.cnblogs.com/allenlooplee/</uri></author><link rel="alternate" href="http://www.cnblogs.com/allenlooplee/archive/2011/12/07/2280008.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/allenlooplee/archive/2011/12/07/2280008.html"/><content type="html">&lt;p style="text-align: center;"&gt;&lt;span style="color: #0070c0; font-size: 20pt;"&gt;&lt;strong&gt;WP7有约（七）：实现铃声设置的播放图标的效果 &lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p style="text-align: right;"&gt;&lt;span style="color: #0070c0;"&gt;&lt;strong&gt;&lt;em&gt;Written by Allen Lee &lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://home.cnblogs.com/u/232531/" target="_blank"&gt;sjcxyf&lt;/a&gt;同学通过站内消息提到这样一个问题：&lt;/p&gt;&#xD;
&lt;p style="margin-left: 36pt;"&gt;我现在想做一个功能就是当ListPicker弹出全屏幕的时候每一项前面是一个播放图片，后面是音乐名称，然后我点击前面的播放的时候播放当前的音乐，不让他选择这一项的值并返回 要选择后面的字之后才返回 就是像Phone 7真机里面设置铃声那种效果怎么做？&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;我们来看一个山寨版的铃声设置，如图1所示，无论用户单击铃声名字还是它左边那个播放图标，都会关闭ListPicker页面。sjcxyf同学想要的效果是单击播放图标将会播放对应的铃声，而单击铃声名字才是确认选择。问题是，即使你单击铃声下面的空白地方，ListPicker页面也会关闭，为什么？&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201112/201112072248078467.png" alt="" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;图 1 山寨版铃声设置 &lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;查看&lt;a href="http://silverlight.codeplex.com/SourceControl/changeset/view/71382#1510234" target="_blank"&gt;ListPickerPage.xaml.cs文件&lt;/a&gt;，我们将会发现ListPicker页面的ListBox控件添加了Tap事件处理程序，如代码1所示。从中我们不难看出，ListPicker页面表现出来的行为是预期的，即在单选模式下，单击ListBox控件里的选项或者空白地方将会关闭ListPicker页面。于是，sjcxyf同学的需求可以重新描述成在用户单击播放图标时防止ListBox控件的事件处理程序被执行，但是，怎样才能做到？&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;代码 1 ListBox控件的Tap事件处理程序 &lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; OnPickerTapped(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, System.Windows.Input.GestureEventArgs e)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; We listen to the tap event because SelectionChanged does not fire if the user picks the already selected item.&lt;br /&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Only close the page in Single Selection mode.&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;    &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (SelectionMode == SelectionMode.Single)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; Commit the value and close&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;        SelectedItem = Picker.SelectedItem;&lt;br /&gt;        ClosePickerPage();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Silverlight支持一种叫做&lt;a href="http://msdn.microsoft.com/en-us/library/cc189018(v=VS.95).aspx" target="_blank"&gt;路由事件（routed event）的概念&lt;/a&gt;，这种事件的触发会从子元素沿着对象树向上传递给各个父元素，直至到达根元素为止。并非所有事件都是路由事件，但Tap事件刚好就是路由事件，换句话说，当用户单击播放图标时，ListBox控件作为播放图标的父元素，虽然不是直接包含的父元素，也能感知传递过来的Tap事件。于是，sjcxyf同学的需求可以重新描述成防止播放图标的Tap事件向上传递给父元素，但是，怎样才能做到？&lt;/p&gt;&#xD;
&lt;p&gt;Tap事件处理程序的第二个参数有一个Handled属性，这个属性正是用来处理这种需求的。假设播放图标是一个Image控件，如代码2所示，我们可以为它添加一个Tap事件处理程序，并在里面把Handled属性设为true，如代码3所示，这样，Silverlight的事件系统将会停止把Tap事件向上传递给各个父元素。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #4f81bd; font-size: 9pt;"&gt;&lt;strong&gt;代码 2 播放图标的XAML代码 &lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000;"&gt;Image &lt;/span&gt;&lt;span style="color: #ff0000;"&gt;Source&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="/play.png"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; Stretch&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="None"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; HorizontalAlignment&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="Left"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; &lt;br /&gt;       VerticalAlignment&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="Center"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; Tap&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="Image_Tap"&lt;/span&gt;&lt;span style="color: #ff0000;"&gt; Margin&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;="0,0,12,0"&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong style="color: #4f81bd; font-size: 12px; line-height: 18px;"&gt;代码 3 播放图标的Tap事件处理程序&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; Image_Tap(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, System.Windows.Input.GestureEventArgs e)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; 添加播放音乐的代码&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;    e.Handled = &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;;&lt;br /&gt;}&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;不过，当用户单击播放图标时，当前选中的铃声也会发生改变，这显然不是Windows Phone 7的铃声设置的做法。我们希望单击播放图标时只是播放铃声，但不改变当前选中的铃声，如果此时按Back键返回，铃声设置将会维持原状。这个效果的实现方式和前面的一样，只不过这次换成了MouseLeftButtonUp事件，因此我把它留给你课后实践一下。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://files.cnblogs.com/allenlooplee/WindowsPhoneApplication1.zip"&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201112/201112072248071532.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/allenlooplee/aggbug/2280008.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/12/07/2280008.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/allenlooplee/archive/2011/11/28/2265729.html</id><title type="text">WP7有约（六）：AppBarUtils使用指南</title><summary type="text">AppBarUtils提供了一组Expression Blend行为，可以实现Application Bar上的按钮和菜单项的绑定，在这篇文章里，我们将会具体看看如何使用这个工具包实现相关的功能。</summary><published>2011-11-28T00:41:00Z</published><updated>2011-11-28T00:41:00Z</updated><author><name>Allen Lee</name><uri>http://www.cnblogs.com/allenlooplee/</uri></author><link rel="alternate" href="http://www.cnblogs.com/allenlooplee/archive/2011/11/28/2265729.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/allenlooplee/archive/2011/11/28/2265729.html"/><content type="html">&lt;p style="text-align: center"&gt;&lt;span style="color:#0070c0; font-size:20pt"&gt;&lt;strong&gt;WP7有约（六）：AppBarUtils使用指南&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: right"&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;&lt;em&gt;Written by Allen Lee&#xD;
&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: right"&gt;&lt;span style="font-family:微软雅黑"&gt;没有你的生活，我开始写小说，好多画面好多灵感，我要把稿费都给你。&#xD;
&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: right"&gt;&lt;span style="font-family:微软雅黑"&gt;&amp;#8211; 周杰伦, Mine Mine&#xD;
&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;这节课的任务&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我们知道，Windows Phone的Application Bar并不支持数据绑定，这意味着我们无法像Silverlight的Button控件那样把Application Bar上的按钮或者菜单项直接绑到视图模型的命令属性。&#xD;
&lt;/p&gt;&lt;p&gt;为了解决这个问题，我们可以借助一些第三方工具包，比如今天我给大家介绍的AppBarUtils，它提供了一组Expression Blend行为，可以实现Application Bar上的按钮和菜单项的绑定。接下来，我们将会具体看看如何使用这个工具包实现相关的功能。&#xD;
&lt;/p&gt;&lt;p&gt;首先，假设我们的应用包含了图1-1所示的Application Bar。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835019334.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 11 Application Bar&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;其中，add按钮和clear菜单项分别绑到视图模型的两个命令属性，它们分别负责把数据添加到页面的ListBox控件里和清空ListBox控件里的内容；sync按钮将会执行视图模型的Sync方法；而statistic菜单项将会打开统计结果页面。&#xD;
&lt;/p&gt;&lt;p&gt;在开始之前，你需要到&lt;a href="http://appbarutils.codeplex.com/"&gt;http://appbarutils.codeplex.com/&lt;/a&gt;下载AppBarUtils的dll，并在项目里引用它。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;按钮和菜单的命令绑定&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;在Expression Blend里单击左边工具栏上的Assets按钮打开Assets窗口，选择Behaviors类别，然后从右边把AppBarItemCommand拖到Objects and Timeline面板的[PhoneApplicationPage]上，如图2-1所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835046430.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 21 添加AppBarItemCommand&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;看到这里，你可能会问，为什么是拖到[PhoneApplicationPage]上呢？因为Application Bar上的按钮和菜单项并非依赖对象，Expression Blend的行为无法和它们关联，而在Windows Phone的应用程序唯一能够访问Application Bar的地方就是页面，所以我们需要把AppBarItemCommand拖到[PhoneApplicationPage]上。&#xD;
&lt;/p&gt;&lt;p&gt;重复上述步骤添加一个AppBarItemCommand。现在我们有两个AppBarItemCommand，分别用于add按钮和clear菜单项。&#xD;
&lt;/p&gt;&lt;p&gt;在Objects and Timeline面板上选中第一个AppBarItemCommand，然后在Properties面板上把Id属性设为add，如图2-2所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835051871.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 22 设置Id属性&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;因为AppBarItemCommand是根据Text属性的值查找Application Bar上的按钮的，所以Id属性的值必须匹配Text属性的值。&#xD;
&lt;/p&gt;&lt;p&gt;接着，切换到XAML模式，把AppBarItemCommand的Command属性绑到视图模型的对应的命令属性，如代码2-1所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 21 设置Command属性的绑定&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="background: #eeece1"&gt;&lt;span style="color:blue; font-family:Consolas; font-size:10pt"&gt;&amp;lt;&lt;span style="color:#a31515"&gt;AppBarUtils&lt;span style="color:blue"&gt;:&lt;span style="color:#a31515"&gt;AppBarItemCommand&lt;span style="color:red"&gt; Id&lt;span style="color:blue"&gt;="add"&lt;span style="color:red"&gt; Command&lt;span style="color:blue"&gt;="{&lt;span style="color:#a31515"&gt;Binding&lt;span style="color:red"&gt; AddCommand&lt;span style="color:blue"&gt;}"/&amp;gt;&#xD;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;看到这里，你可能会问，为什么不直接在Properties面板上设置Command属性的绑定呢？这是因为Expression Blend对Behavior的ICommand类型的属性做了特殊处理，如上面的图2-2所示，这使得Command属性看起来不像普通属性，右边也没有Advanced options按钮，所以无法打开数据绑定对话框。&#xD;
&lt;/p&gt;&lt;p&gt;至于clear菜单项，我们需要在Properties面板上把Id和Type两个属性的值分别设为clear和MenuItem，如图2-3所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/20111128083507659.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 23 设置Id和Type两个属性&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;需要说明的是，Type属性是用来区分Application Bar上的按钮和菜单项这两种类型的，它的默认值是Button，因此，当你在按钮上使用AppBarItemCommand时，你不需要设置这个属性的值。&#xD;
&lt;/p&gt;&lt;p&gt;最后是Command属性的绑定，和前面一样，我们需要切换到XAML模式设置绑定表达式，如代码2-2所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 22 设置Command属性的绑定&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="background: #eeece1"&gt;&lt;span style="color:blue; font-family:Consolas; font-size:10pt"&gt;&amp;lt;&lt;span style="color:#a31515"&gt;AppBarUtils&lt;span style="color:blue"&gt;:&lt;span style="color:#a31515"&gt;AppBarItemCommand&lt;span style="color:red"&gt; Type&lt;span style="color:blue"&gt;="MenuItem"&lt;span style="color:red"&gt; Id&lt;span style="color:blue"&gt;="clear"&lt;span style="color:red"&gt; Command&lt;span style="color:blue"&gt;="{&lt;span style="color:#a31515"&gt;Binding&lt;span style="color:red"&gt; ClearCommand&lt;span style="color:blue"&gt;}"/&amp;gt;&#xD;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;使用Expression Blend SDK的行为&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;你知道吗，Expression Blend SDK提供了许多有用的行为，其中一个是CallMethodAction，它可以用来执行指定对象的指定方法，显然非常适合我们的sync按钮，不过，它需要和Trigger一起工作，而Expression Blend SDK并未提供适用于Application Bar的Trigger，怎么办？这个时候就轮到AppBarItemTrigger出场了。&#xD;
&lt;/p&gt;&lt;p&gt;打开Assets窗口，选择Behaviors类别，然后从右边把CallMethodAction拖到Objects and Timeline面板的[PhoneApplicationPage]上，如图3-1所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835106359.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 31 添加CallMethodAction&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;确保CallMethodAction处于选中状态，在Properties面板上TriggerType右边的New按钮，如图3-2所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/20111128083511753.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 32 单击New按钮&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;在弹出的Select Object对话框里选择AppBarItemTrigger，如图3-3所示，然后单击OK按钮关闭对话框。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835112323.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 33 选择AppBarItemTrigger&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;此时，Trigger的相关属性将会换成AppBarItemTrigger的，把Id属性的值设为sync，如图3-4所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835126194.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 34 AppBarItemTrigger的属性&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;至于CallMethodAction本身的属性，我们只需把TargetObject属性绑到指定对象，然后把MethodName属性的值设为指定方法的名字就行了。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;页面导航行为&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;有了AppBarItemTrigger，我们就可以使用Expression Blend SDK的NavigateToPageAction为Application Bar实现打开页面的操作了，当然，更简单的办法是直接使用AppBarItemNavigation，它的用法和AppBarItemCommand类似，只是Command属性换成了TargetPage属性。&#xD;
&lt;/p&gt;&lt;p&gt;如果你希望在URI里包含查询字符串，而参数的值又是绑到视图模型的属性的，那么，你可以试试NavigateWithQueryStringAction。&#xD;
&lt;/p&gt;&lt;p&gt;打开Assets窗口，选择Behaviors类别，然后从右边把NavigateWithQueryStringAction拖到Objects and Timeline面板的[PhoneApplicationPage]上，如图4-1所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/20111128083515847.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 41 添加NavigateWithQueryStringAction&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;确保NavigateWithQueryStringAction处于选中状态，在Properties面板上Trigger改为AppBarItemTrigger，然后把Id、Type和TargetPage三个属性的值分别改为statistic、MenuItem和/StatisticPage.xaml，如图4-2所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835164303.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 42 设置相关属性&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;假设我们希望包含的查询字符串是"hitcount=XX&amp;amp;timecount=YY"，其中，XX和YY分别来自视图模型的HitCount和TimeCount两个属性，那么，我们可以通过NavigateWithQueryStringAction的Parameters属性进行设置。&#xD;
&lt;/p&gt;&lt;p&gt;在Properties面板上单击Parameters属性右边的Edit items in this collection按钮，在弹出的Parameter Collection Editor对话框里单击Add another item按钮添加一个参数，把Field属性的值设为hitcount，把Value属性绑到视图模型的HitCount属性，如图4-3所示。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835177551.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 43 添加参数&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;用同样的办法添加一个timecount参数，并把它的Value属性绑到视图模型的TimeCount属性。&#xD;
&lt;/p&gt;&lt;p&gt;根据上述设置，NavigateWithQueryStringAction将会为我们创建一个这样的URI：/StatisticPage.xaml?hitcount=9&amp;amp;timecount=13（假设用户单击statistic菜单项时，HitCount和TimeCount两个属性的值分别为9和13）。&#xD;
&lt;/p&gt;&lt;p&gt;由于NavigateWithQueryStringAction是一个TriggerAction，这意味着它的用途并不限于Application Bar，你可以通过Expression Blend SDK的EventTrigger把它用到Silverlight的Button控件（或者其它控件）上。&#xD;
&lt;/p&gt;&lt;p&gt;最后，如果你需要的只是一个简单的后退，你可以使用GoBackAction。和NavigateWithQueryStringAction一样，你可以通过AppbarItemTrigger把它用到Application Bar上，也可以通过EventTrigger把它用到Silverlight的Button控件（或者其它控件）上。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;绑定启用状态和显示文字&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;有些时候，你可能希望Application Bar上的按钮和菜单项可以根据某些条件自动调整启用状态，比如说，当页面的ListBox控件里有内容时，sync按钮才可用，否则，它应该处于禁用状态。&#xD;
&lt;/p&gt;&lt;p&gt;AppBarItemTrigger提供了一个IsEnabled依赖属性，当我们把它绑到视图模型的某个bool类型的属性时，前者会监听后者的更改，然后把修改后的值反映到Application Bar上的按钮或者菜单项的IsEnabled属性。&#xD;
&lt;/p&gt;&lt;p&gt;如果你使用的是AppBarItemCommand，你可以通过ICommand.CanExecute方法的返回值指定启用/禁用状态，并在状态发生更改的时候触发CanExecuteChanged事件，剩下的事情AppBarItemCommand会帮你处理好的。&#xD;
&lt;/p&gt;&lt;p&gt;至于Application Bar上的按钮和菜单项的显示文字，你也可能希望实现绑定，这种需求通常出现在多语言支持的应用里，这个时候，你可以把AppBarItemTrigger的Text依赖属性绑到资源对象的某个属性，前者会监听后者的更改，然后把修改后的值反映到Application Bar上的按钮或者菜单项的Text属性。如果你使用的是AppBarItemCommand或者AppBarItemNavigation，你也可以通过它们的Text依赖属性实现同样的效果。&#xD;
&lt;/p&gt;&lt;p&gt;看到这里，有些同学可能会担心Id和Text两个属性打起架来，放心吧，它们不会的。虽然它们最终都是关联到Application Bar上的按钮或者菜单项的Text属性，但它们发生作用的时间是不同的。Id属性只在行为初始化的时候用来查找Application Bar上的按钮或者菜单项，一旦找到，Id属性就会功成身退了，从此刻开始，Text属性将会派上用场，它会密切关注绑定源，并把更新反映到Application Bar上的按钮或者菜单项的Text属性。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;下课了&amp;#8230;&amp;#8230;&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201111/201111280835199546.png" alt="" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/allenlooplee/aggbug/2265729.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/11/28/2265729.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/allenlooplee/archive/2011/05/27/2059582.html</id><title type="text">WP7有约：一个应用的破蛋过程</title><summary type="text">2011年年度独家巨献，超过半年的写作成果，将近300页的中文教程，全程记录一个WP7应用的开发过程，全新排版，图文并茂，免费下载，不容错失！</summary><published>2011-05-27T02:22:00Z</published><updated>2011-05-27T02:22:00Z</updated><author><name>Allen Lee</name><uri>http://www.cnblogs.com/allenlooplee/</uri></author><link rel="alternate" href="http://www.cnblogs.com/allenlooplee/archive/2011/05/27/2059582.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/allenlooplee/archive/2011/05/27/2059582.html"/><content type="html">&lt;p&gt;不知不觉，《WP7有约》系列文章的发布已经走过半年有多了，它记录了我开发课程表应用时的所思所想，而不仅仅是一个最终结果，喜欢WP7的童鞋，你有木有把相关的东西学到手呢？&lt;/p&gt;&#xD;
&lt;p&gt;经过N天的努力，《WP7有约：一个应用的破蛋过程》电子书终于制作完毕了。我重读了这个系列的所有文章，对每个章节进行了更细的划分，使之结构更合理，从而避免连续阅读。你可以在我的博客里阅读这个系列的文章，也可以把电子书下载到你的电脑阅读，无论你最终采用哪种方式来阅读，我的一个建议是：&lt;u&gt;不要仅仅看书，动作做做看，尝试提出新的需求，然后想办法实现它！&lt;/u&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;table width="480"&gt;&#xD;
     &lt;tbody&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td&gt;&#xD;
             &lt;img alt="" height="116" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GT1.png" width="480" /&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://www.cnblogs.com/allenlooplee/archive/2010/11/17/1879489.html" target="_blank"&gt;&#xD;
             &lt;img alt="创建课程表" height="58" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI11.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://www.cnblogs.com/allenlooplee/archive/2010/12/14/1905184.html" target="_blank"&gt;&#xD;
             &lt;img alt="创建作业本" height="57" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI12.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/01/11/1933165.html" target="_blank"&gt;&#xD;
             &lt;img alt="创建笔记本" height="58" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI13.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/03/24/1993368.html" target="_blank"&gt;&#xD;
             &lt;img alt="把课程、作业和笔记整合到一块" height="57" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI14.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/05/09/2040785.html" target="_blank"&gt;&#xD;
             &lt;img alt="在主页显示下一节课的信息" height="58" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI15.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://files.cnblogs.com/allenlooplee/WP7%E6%9C%89%E7%BA%A6.7z"&gt;&#xD;
             &lt;img alt="下载电子书" height="58" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI16.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;img alt="" height="116" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GT2.png" width="480" /&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://iridescent.codeplex.com/wikipage?title=Overview" target="_blank"&gt;&#xD;
             &lt;img alt="iridescent" height="57" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI21.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td style="padding-top: 10px"&gt;&#xD;
             &lt;a href="http://iridescent.codeplex.com/releases/view/65869" target="_blank"&gt;&#xD;
             &lt;img alt="下载源代码" height="58" src="http://images.cnblogs.com/cnblogs_com/allenlooplee/GI22.png" width="480" /&gt;&lt;/a&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
     &lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p align="center"&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809509622.png" alt="iridescent" /&gt;&lt;/p&gt;&#xD;
&lt;img src="http://www.cnblogs.com/allenlooplee/aggbug/2059582.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/05/27/2059582.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/allenlooplee/archive/2011/05/09/2040785.html</id><title type="text">WP7有约（五）：回到主页</title><summary type="text">WP7有约（五）：回到主页Written by Allen LeeIn this farewell, there is no blood, there is no alibi, cause I've drawn regret from the truth of a thousand lies.– Linkin Park, What I've done最重要的问题 还记得当初我们开发这个应用的目的吗...</summary><published>2011-05-09T00:18:00Z</published><updated>2011-05-09T00:18:00Z</updated><author><name>Allen Lee</name><uri>http://www.cnblogs.com/allenlooplee/</uri></author><link rel="alternate" href="http://www.cnblogs.com/allenlooplee/archive/2011/05/09/2040785.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/allenlooplee/archive/2011/05/09/2040785.html"/><content type="html">&lt;p style="text-align: center"&gt;&lt;span style="color:#0070c0; font-size:20pt"&gt;&lt;strong&gt;WP7有约（五）：回到主页&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: right"&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;&lt;em&gt;Written by Allen Lee&#xD;
&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p style="text-align: right"&gt;&lt;em&gt;In this farewell, there is no blood, there is no alibi, cause I've drawn regret from the truth of a thousand lies.&lt;br /&gt;&amp;#8211; Linkin Park, What I've done&#xD;
&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;最重要的问题&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;还记得当初我们开发这个应用的目的吗？是为了随时随地可以查看课程信息。但你会闲来无事打开课程表查看课程信息吗？我就不会了！那用户要课程表来干嘛？一般情况下，用户查看课程表是为了回答以下问题：&#xD;
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;下一节课是什么？&#xD;
&lt;/li&gt;&lt;li&gt;今天要上哪些课？&#xD;
&lt;/li&gt;&lt;li&gt;明天要上哪些课？&#xD;
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;我希望在主页上提供直接获取上述问题的答案的途径，比如说，我们可以在主页上添加下面这个格子（Tile）：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809077734.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 1&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;它可以清楚地告诉用户下一节课是什么，几点开始，在哪里上。至于第二、三个问题，我们可以在主页上添加下面两个格子：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080908982.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 2&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;值得注意的是，这两个格子的右上角都有一个箭头记号，这是为了提醒用户单击它们可以获取相关信息。我们不可能在主页上罗列今天和明天的所有课程，这样会导致信息泛滥，同时也会加重用户识别有用信息的负担，因此我们在这里提供一些捷径，以便用户更快到达包含所需信息的地方。此外，用户最常打开的页面应该是当前课程的Course Hub了，在那里用户可以完成当前课程最常用的操作，因此我们可以在主页上再添加一个格子，帮助用户直接到达那个页面：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809099521.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 3&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;值得注意的是，与几点上课相比，用户更加关心几点下课，因此当前课程显示的是下课时间而不是上课时间。&#xD;
&lt;/p&gt;&lt;p&gt;我们应该把这些格子放在主页上，以便用户随时了解最重要的信息：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809107404.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 4&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;那么，怎样创建这样的用户界面呢？其实不难，上面的刷新按钮可以直接使用&lt;a href="http://coding4fun.codeplex.com/"&gt;Coding4Fun Windows Phone Toolkit&lt;/a&gt;的RoundButton控件，而下面的四个格子由于内容结构基本相同，很容易让人想到把它们放到一个集合型的控件里，这里将会选择ItemsControl作为它们的容器。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;创建用户界面&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;首先，在主页上添加一个Panorama项，并把标题设为"课程"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809117894.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 5&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;新创建出来的Panorama项默认包含一个Grid，我们可以通过Properties面板上的RowDefinitions属性把它分成上下两行，上面那行用来放置刷新按钮，高度设为自动适应，而下面那行用来放置ItemsControl，高度使用默认设置。&#xD;
&lt;/p&gt;&lt;p&gt;好了之后，从Assets面板上把RoundButton控件拖到Grid里，默认自动放在Grid的第一行（注意，在使用RoundButton控件之前请先引用Coding4Fun.Phone.Control.dll文件）：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809112321.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 6&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;设置ImageSource属性，把图标换成图4里面那个，然后把Content属性的值设为"刷新"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809116748.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 7&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;默认情况下，RoundButton控件的文字是位于图标下方的，而且无法直接通过设置某个属性使它的文字放在图标右边，想要达到图4的效果，我们需要修改它的样式。右击RoundButton控件，选择Edit Template\Edit a Copy进入模板编辑状态，此时，你的Objects and Timeline面板将会变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809129191.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 8&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;确保StackPanel处于选中状态，然后在Properties面板上把Orientation属性改为Horizontal。接着，选中StackPanel下面的ContentBody，并在Properties面板上把字体大小设为PhoneFontSizeMedium。好了之后退出模板编辑状态，此时，你的RoundButton控件应该是这样的：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809127206.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 9&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;看到这里，你可能会问，为什么要在模板编辑状态里设置字体大小呢？这是因为直接修改RoundButton控件的字体大小不起作用，我认为这是一个bug。我在CodePlex上&lt;a href="http://coding4fun.codeplex.com/workitem/6470"&gt;提了这个问题&lt;/a&gt;，第二天他们就发布了&lt;a href="http://coding4fun.codeplex.com/SourceControl/changeset/changes/64877"&gt;修正的版本&lt;/a&gt;，动作好快！&#xD;
&lt;/p&gt;&lt;p&gt;接着，在里面添加一个ItemsControl，在Properties面板上把它的Row属性设为1，并使它充满Grid的第二行。由于ItemsControl默认使用StackPanel来排列它的子元素，这会导致格子从上到下排成一列，要使格子按照图4所示的那样排列，我们需要把StackPanel替换成WrapPanel。右击ItemsControl，选择Edit Additional Templates\Edit Layout of Items (ItemsPanel)\Create Empty：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809136060.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 10&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;在弹出的Create ItemsPanelTemplate Resource对话框里输入模板名字，然后单击OK关闭对话框：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809137946.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 11&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;此时，你的Objects and Timeline面板将会变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809146484.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 12&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;把里面的StackPanel删掉，然后从Assets面板上把WrapPanel拖到里面：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809145023.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 13&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;好了之后退出模板编辑状态。&#xD;
&lt;/p&gt;&lt;p&gt;接下来，我们将会定制每个格子的内容结构。再次右击ItemsControl，选择Edit Additional Templates\Edit Generated Items (ItemTemplate)\Create Empty，然后在弹出的Create DateTemplate Resource对话框里输入模板名字，然后单击OK关闭对话框：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809154957.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 14&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;此时，你会看到屏幕中间有个空白的Grid：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809159940.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 15&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;由于这次我们没有使用设计时数据，数据模板的设计将会比较考究想象力，不过我相信你能跟得上的。确保Grid出于选中状态，把它的Width和Height两个属性都设为190（这个数值是经过多次微调得到的最佳结果），并把它的背景色设为PhoneAccentBrush：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809158827.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 16&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;然后把Grid的Margin设成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809153811.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 17&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接着，在Grid里添加相应的控件，使它变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809161760.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 18&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;此时，你的Objects and Timeline面板应该是这样的：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809177234.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 19&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;值得提醒的是，在调整控件的大小和位置时，你可以暂时为控件硬编码一些内容，比如上图的"当前课程"和课程信息。好了之后退出模板编辑状态。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;实现内部逻辑&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;现在是时候为那些格子创建对应的ViewModel类了，不过，在开始之前我们需要搞清楚这些格子的工作方式：&#xD;
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;每个格子都有一个标题，标题内容不会改变。&#xD;
&lt;/li&gt;&lt;li&gt;每个格子都有一组信息，用户可以通过单击刷新按钮更新这些信息。&#xD;
&lt;/li&gt;&lt;li&gt;如果用户单击格子时将会执行某个操作，那么格子右上角需要显示箭头图标，否则不显示该图标。&#xD;
&lt;/li&gt;&lt;li&gt;"明天课程"有两个状态：明天有课和明天没课，如果明天有课，则显示课程总数，并显示箭头图标，单击将会打开课程表并显示明天的课程；否则显示"明天没课"，且不显示箭头图标，单击也不执行任何操作。&#xD;
&lt;/li&gt;&lt;li&gt;"今天课程"有三个状态：今天没课、今天有课和已经放学，如果今天没课，则显示"今天没课"，且不显示箭头图标，单击不执行任何操作；如果今天有课，并且还有课程剩余，则显示剩余课程的数目，否则显示"放学了"，这两种状态都会显示箭头图标，单击将会打开课程表并显示今天课程。&#xD;
&lt;/li&gt;&lt;li&gt;"下一节课"也有三个状态：今天没课、今天有课和已经放学，如果今天没课，则显示"今天没课"；如果今天有课，并且还有课程剩余，则显示下一节课，否则显示"放学了"，这个格子不显示箭头图标，单击也不执行任何操作。&#xD;
&lt;/li&gt;&lt;li&gt;"当前课程"有五个状态：今天没课、还没上课、正在上课、课间休息和已经放学，仅当它处于正在上课状态时才显示箭头图标，此时单击将会打开当前课程的Course Hub，处于其它状态时只显示状态名称，且单击不执行任何操作。&#xD;
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;噢，看起来有点复杂哦，不过不用担心，我们一起有步骤地地把它们实现了。&#xD;
&lt;/p&gt;&lt;p&gt;首先，在ViewModels文件夹里创建一个TileViewModelBase抽象类，并让它继承NotificationObject类：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809179153.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 1&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这里将会放置被四个格子的ViewModel类共用的代码，那么，哪些代码是共用的呢？根据上面的讨论，每个格子都需要提供一组信息，里面可能包含课程信息或者格子的状态信息，我们可以把这些信息放在一个集合里，并在构造函数里初始化这个集合：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809172500.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 2&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;其次，每个格子都有一个标题，但这个标题的内容不会改变，因此我们可以通过自动属性来实现：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809185532.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 3&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这个属性的初始化需要在每个格子的ViewModel类的构造函数里进行，因为每个格子的标题都不同。至于格子右上角的图标，我们只需提供一个布尔型属性，指示是否显示这个图标就行了，不过，由于这个图标的显示和格子的状态紧密相连，我们需要为这个布尔型属性提供通知机制，以便在格子的状态发生改变时可以通知用户界面做出相应的调整：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809182467.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 4&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;此外，格子还需要支持单击操作和刷新操作：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809189087.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 5&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;由于每个格子在计算应该显示的信息时都要获取今天或者明天的全部课程，我们可以把这部分代码放在基类里：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080918482.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 6&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;有了这个基类，我们就可以分别为那些格子创建对应的ViewModel类了。&#xD;
&lt;/p&gt;&lt;p&gt;我们先挑一个简单的来试一下，根据前面的讨论，"明天课程"最简单，因此我们先为它创建ViewModel类。在ViewModels文件夹里创建一个TomorrowCoursesTileViewModel类，并让它继承TileViewModelBase抽象类：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809185466.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 7&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;当用户单击这个格子将会打开课程表并显示明天的课程，为了执行这个操作我们需要知道明天是星期几，因此我在里面添加了一个_day私有字段，用来存放这个信息。接着，我们需要在构造函数里初始化相关的属性：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809181529.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 8&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;其中，打开课程表的代码将会放在（1）里，而初始化格子的代码则会放在（2）里。看到这里，你可能会问，如何在ViewModel类里打开一个页面？一直以来，我们都是在一个页面里通过NavigationService的Navigate方法打开另一个页面，而在ViewModel类里，NavigationService是访问不了的，怎么办？要解决这个问题，我们得先搞清楚Windows Phone的页面导航模型是怎样的，Windows Phone的导航模型是由一个PhoneApplicationFrame和一个或多个PhoneApplicationPage共同组成的，前者提供与导航相关的事件和Navigate方法，而后者则包含应用的内容，它们共享着同一个NavigationService。虽然在ViewModel类里无法访问页面的资源，但是我们可以通过App类获取当前应用的PhoneApplicationFrame对象，继而调用它的Navigate方法：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809192052.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 9&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;事实上，MVVM模式认为ViewModel层不应该依赖于View层的任何东西，更好的做法应该是创建一个INavigationService接口把导航服务隔离开来，然后在ViewModel类里通过依赖注入的方式来获取和访问导航服务，如果你打算为你的ViewModel类创建单元测试，那么这种隔离就更加有必要了。不过，我不想在这里把事情弄得太复杂，因此采用代码9这种简单直接的做法。此外，需要说明的是，仅当格子右上角显示箭头图标时单击格子才会执行相应操作，因此在初始化TileCommand属性的时候我们需要告诉它根据ShowNextIcon属性的值来判断是否允许执行相应的操作。而刷新方面，我们只需要看看明天是否有课，然后根据情况更新相关的信息就行了：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809192335.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 10&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;需要说明的是，GetChineseDayName方法是一个扩展方法，用来生成与DayOfWeek枚举值对应的中文星期名称，事实上，它是从上节课的CourseHubViewModel类里提取出来的。&#xD;
&lt;/p&gt;&lt;p&gt;接着，我们来看看"今天课程"，它和"明天课程"没有太大区别，我们只是需要多处理一个状态，以及把显示课程总数改为显示剩余课程的数目。在ViewModels文件夹里创建一个TodayCoursesTileViewModel类，并让它继承TileViewModelBase抽象类：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809196970.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 11&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;刷新的时候，如果今天的课程数目为0，当然是显示"今天没课"了：&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;&#xD;
			&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809205300.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 12&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;否则，看看是否所有课程都结束了，如果是就显示"放学了"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080920284.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 13&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;否则，显示剩余课程的数目：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080920807.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 14&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;需要说明的是，这里把剩余课程定义为还没开始的课程，已经开始但还没下课的不包含在里面。此外，Course类的StartTime和EndTime两个属性的值是通过SL for WP Toolkit的TimePicker设置的，由于它只关心时间部分，因此把年、月和日的值都设为1了，而DateTime.Now包含了今天的年、月和日，会对后面的比较造成影响，所以我另外创建了一个thisTime。&#xD;
&lt;/p&gt;&lt;p&gt;不过，要让打开的课程表正确显示我们想要的课程，还得修改一下课程表的代码。打开CourseTimetablePage.xaml.cs文件，在构造函数里把Loaded事件处理程序的代码改成下面这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080920774.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 15&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;当用户打开课程表时，默认显示今天的课程，如果查询字符串里面包含了day参数，则显示该参数指定的那天课程。需要说明的是，GetDayOfWeekValue是一个扩展方法，用来生成与中文星期名称对应的DayOfWeek枚举值，本质上它是GetChineseDayName方法的反向实现。&#xD;
&lt;/p&gt;&lt;p&gt;接着，我们来看看"下一节课"。在ViewModels文件夹里创建一个NextCourseTileViewModel类，并让它继承TileViewModelBase抽象类：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809216838.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 16&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;根据前面的讨论，这个格子不显示箭头图标，单击也不执行任何操作，因此我们把ShowNextIcon属性的值设为false，把TileCommand属性初始化为"空"命令。看到这里，你可能会问，为什么要这样初始化TileCommand属性呢？因为将来设置数据绑定的时候，我们是在格子的模板里统一设置的，如果某个格子的TileCommand属性为null，那么将来用户单击这个格子时可能会抛出NullReferenceException。刷新的时候，如果今天的课程数目为0就显示"今天没课"了，如果所有课程都结束了就显示"放学了"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809218756.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 17&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;否则，我们需要从剩余课程里找出下一节课。如果此时正在上最后一节课呢？前面我们把剩余课程定义为还没开始的课程，那么现在的状态既没有剩余课程又不是已经放学，怎么办？这是一个临界状态，我们可以增加一个状态来描述这种情况，如果现在是最后一节课，那么我们可以显示"最后一节了"，否则显示下一节课的相关信息：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809218723.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 18&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;最后，我们来看看"当前课程"，这是四个格子里面最麻烦的，也是状态最多的。在ViewModels文件夹里创建一个CurrentCourseTileViewModel类，并让它继承TileViewModelBase抽象类：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809226182.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 19&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;刷新的时候，如果今天的课程数目为0就显示"今天没课"了，如果所有课程都没开始就显示"还没上课"，如果所有课程都结束了就显示"放学了"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809222005.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 20&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;否则，看看此时是不是正在上课，如果是就显示课程信息，如果不是就显示"课间休息"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809229148.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 21&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;看到这里，你可能会问，每次刷新格子都要通过LINQ做很多次遍历，这样会不会降低应用的效率？嗯，是的，目前的做法不是很好，比较好的做法是获取某天的课程，然后对它们进行排序，并缓存到一个集合里，接着从前往后做一次遍历，看看当前时间在哪个位置，从而判断格子处于什么状态。但即使这样，每次刷新还是需要一次遍历，更好的做法应该是每次找到当前时间的位置，就用一个变量做个记号，有点像书签一样，下次刷新时就从这个位置开始往后遍历，这样的话，理想状态下一天就只需做一次遍历了。不过，所有这些都是以一个完整且稳定的课程表为前提的，如果你打算让你的课程表支持临时调课、插课或者和远程服务器进行同步，那么这些优化措施可能会让事情变得糟糕。我不建议太早进行性能优化，因为这样可能会导致设计僵化，我无法告诉你什么时候适合优化性能，你必须根据自己的设计和计划作出决定。但有一点我是可以肯定的，如果性能问题超出了用户的容忍程度，那么无论如何我们都要采取行动了，&lt;span style="text-decoration:underline"&gt;因为用户无法直接看到你的设计有多灵活，却非常清楚他们内心此刻的负面感受。&lt;/span&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;现在，打开MainViewModel.cs文件，它是我们创建这个项目的时候Expression Blend自动为主页创建的ViewModel类，不过里面有很多我们不需要的代码，在继续之前先把它们清理掉，但保留INotifyPropertyChanged接口的实现代码。好了之后，在里面创建下面两个属性：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809234447.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 22&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接着，在构造函数里初始化这两个属性：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809231590.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 23&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;初始化完这两个属性之后别忘了"刷新"一下哦，否则主页上的那些格子不会显示任何东西的。至此，ViewModel类全部创建完毕了，接下来，我们将会把它们关联到对应的View上。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;把ViewModel连接到View上&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;如果你是在Visual Studio里写代码的，那么现在是时候回到Expression Blend了。确保主页上的ItemsControl处于选中状态，在Properties面板上单击ItemsSource属性右边的小正方形，选择Custom Expression，然后在弹出的Custom Expression对话框里输入"{Binding TileViewModels}"。接着，右击ItemsControl，选择Edit Additional Templates\Edit Generated Items (ItemTemplate)\Edit Current进入模板编辑状态，根据下表用同样的办法设置TextBlock和ItemsControl的数据绑定：&#xD;
&lt;/p&gt;&lt;div&gt;&lt;table style="border-collapse:collapse" border="0"&gt;&lt;colgroup&gt;&lt;col style="width:144px"&gt;&lt;col style="width:144px"&gt;&lt;col style="width:144px"&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr style="background: #9bbb59"&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  solid #b3cc82 1.0pt; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;&lt;span style="color:white"&gt;&lt;strong&gt;控件&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  solid #b3cc82 1.0pt; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;&lt;span style="color:white"&gt;&lt;strong&gt;属性&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  solid #b3cc82 1.0pt; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;&lt;span style="color:white"&gt;&lt;strong&gt;绑定表达式&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="background: #e6eed5"&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;TextBlock&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;Text&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;{Binding Title}&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;ItemsControl&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;ItemsSource&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;{Binding Contents}&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;表 1&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;至于格子右上角的箭头图标，我们需要把它的Visibility属性绑到ShowNextIcon属性，不过，它们的类型并不一样，因此我们需要使用转换器。在Properties面板上单击Visibility属性右边的小正方形，选择Data Binding，在弹出的Create Data Binding对话框里选中Data Context选项卡，然后勾选Use a custom path expression，并在旁边输入ShowNextIcon：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080923477.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 20&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接着，单击Value converter右边的省略号按钮：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809231001.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 21&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;然后在弹出的Add Value Converter对话框里选择Coding4Fun.Phone.Controls.Converters下面的BooleanToVisibilityConverter：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809248950.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 22&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;好了之后单击OK关闭对话框。&#xD;
&lt;/p&gt;&lt;p&gt;接下来，我们将会把命令对象绑定到相关控件上。首先是格子的单击操作，每个格子最外层是一个Grid，它没有Click事件，因此我选择了MouseLeftButtonUp事件来代替，我希望把它关联到TileViewModelBase的TileCommand属性，怎么关联？还记得&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/03/24/1993368.html"&gt;上节课&lt;/a&gt;介绍的EventToCommand吗？现在又轮到它出场了！从Assets面板上把EventToCommand拖到Grid上，此时，你的Objects and Timeline面板将会变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809255296.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 23&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;确保EventToCommand处于选中状态，在Properties面板上把EventName属性设为MouseLeftButtonUp，并把Command属性的绑定表达式设为"{Binding TileCommand}"。好了之后退出模板编辑状态，用同样的办法给RoundButton控件添加EventToCommand，然后把Command属性的绑定表达式设为"{Binding RefreshTilesCommand}"。当我们把EventToCommand拖到一个控件上时，EventName属性会被自动设为目标控件的默认事件，因为Click事件刚好是RoundButton控件的默认事件，所以我们无需对这个属性做任何修改。&#xD;
&lt;/p&gt;&lt;p&gt;终于可以看效果了，哎呀，我等这个时候等到花儿也谢了，事不宜迟了，按F5吧：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809261326.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 24&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;从上图可以看到，今天是星期天，没课，明天是星期一，有三节课，单击"明天课程"将会打开课程表，并显示星期一的课程：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809269591.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 25&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;到了星期一早上，如果我们在所有课程开始之前打开应用，主页上的格子就会变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809273113.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 26&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;过了一会，在第一节课开始之后刷新一下，主页上的格子就会变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809281410.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 27&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;单击"当前课程"将会打开Course Hub，并显示当前课程的相关信息，包括课程概况、今天笔记和今天作业：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809295979.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 28&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;到了下午放学之后再打开应用，主页上的格子就会变成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809296785.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 29&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;非常好！不过，有一个小小的地方还需要改进一下，目前每个格子里的内容和标题的字体大小是一样的，这意味着它们是同等重要的，但事实上用户更加关注内容而不是标题，因此我们应该增加内容的字体大小，从而更加突出内容的重要性，事实上，通过不同的字体大小区分不同内容的重要性是Metro的设计原则之一。嗯，这个改进的实现就留给你当课后作业吧。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;最近的作业完成得怎样？&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;通过作业本，用户可以清楚地看到每天有什么作业，哪些已经完成，哪些还没完成，但如果用户希望直观地了解每天的作业量有多少，已经完成的占多少，还没完成的又占多少，每天的作业量是否均匀、能否跟得上呢？前面那组是微观层面的问题，而后面那组是宏观层面的问题，要回答宏观层面的问题，我们需要借助统计工具，比如说，我们可以通过下面这个图表回顾过去五天的作业情况：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809305324.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 30&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;从上图不难看出，最近作业量增大了，还没完成的作业比重也在增大，是时候找找原因了。不过，查明这个原因不是我们的任务，实现这个功能才是我们的任务。那么，如何实现这个功能？&#xD;
&lt;/p&gt;&lt;p&gt;实现这个功能需要从两个方面入手，第一，实现一个算法计算统计结果，第二，使用一个控件显示统计结果。我们先来看看第一个方面，这个算法的基本思路是先找出过去五天的作业，然后把它们分成已完成和未完成两组，接着分别对每组进行统计，计算每天的作业有多少。换句话说，我们的统计结果将会产生两组二维数据，我们可以通过两个集合来保存它们：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809303654.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 24&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这两组数据的计算过程基本上是一样的：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809319128.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 31&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;唯一的区别在于一个要筛选出已完成的作业，另一个要筛选出未完成的作业，这个"变数"可以通过方法的参数隔离开来。根据图31，我们可以创建一个ComputeStatistic方法：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809319652.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 25&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我们可以通过方法的参数指定计算哪组数据，此外，在执行任何具体计算代码之前，我们需要知道过去五天的日期是什么。看到这里，你可能会问，每次调用这个方法都要创建这样一个集合会不会很低效？是的，你可以把它保存在一个属性里，然后在构造函数里对它进行初始化，这样每次调用这个方法时只需访问那个属性就行了，不过，如果你决定这样做，你需要额外考虑一个问题：当用户在使用的过程中时间跨越了零点，应用需要重新根据新的"今天"重新创建这个集合，诚然，这种情况发生的几率不大，但若放任不管，它就会成为一个潜在的bug。接下来，执行图31所示的五个计算步骤：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809311570.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 26&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;看到这里，你可能会问，如果现有的作业不足五天呢，比如说一开始没有任何作业？这种情况下，我们应该为没有作业的那些天按顺序"补零"，以便横坐标可以正常显示过去五天：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809322094.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 27&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;有了ComputeStatistic方法，我们就可以实际计算代码24那两个属性的值了：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080932665.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 28&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;那么，什么时候调用这个方法呢？我们知道，作业的统计数据不会因为时间的流逝而自动改变，因为作业的新建和编辑都需要用户手动完成，而主页上面也没有提供任何直接操作的途径，所以我们无需在主页上为这个统计图表提供一个单独的刷新按钮，只需在页面加载的时候刷新一下就行了：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809329553.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 29&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接下来，我们将会把统计结果以统计图表的方式呈现出来。&#xD;
&lt;/p&gt;&lt;p&gt;这里选择&lt;a href="http://www.mindscapehq.com/products/phone-elements/controls/wp7-stacked-bar-chart"&gt;Mindscape Phone Elements的Stacked Bar Chart&lt;/a&gt;，当然，你也可以使用其它熟悉的图标控件。&lt;a href="http://assets.mindscape.co.nz/Downloads/PhoneElementsTrial.msi"&gt;下载&lt;/a&gt;并添加Mindscape Phone Elements的类库的引用，然后在主页上添加一个Panorama项，并把标题设为"作业"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809333075.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 32&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接着，从Assets面板上把Chart控件拖到Grid里，并使之充满整个Grid：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809339977.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 33&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我们的统计图表包含两组统计数据，因此我们需要在Chart控件里添加两个StackedBarSeries控件，此时，你的Objects and Timeline面板应该是这样的：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809348242.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 34&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这两个StackedBarSeries控件将会分别绑到FinishedStatistic和UnfinishedStatistic两个属性。怎么绑呢？选中第一个StackedBarSeries控件，在Properties面板上按照下表设置相关属性的绑定表达式/值：&#xD;
&lt;/p&gt;&lt;div&gt;&lt;table style="border-collapse:collapse" border="0"&gt;&lt;colgroup&gt;&lt;col style="width:144px"&gt;&lt;col style="width:192px"&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr style="background: #9bbb59"&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  solid #b3cc82 1.0pt; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;&lt;span style="color:white"&gt;&lt;strong&gt;属性&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  solid #b3cc82 1.0pt; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;&lt;span style="color:white"&gt;&lt;strong&gt;绑定表达式&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="background: #e6eed5"&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;ItemsSource&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;{Binding FinishedStatistic}&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;XBinding&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;{Binding Key}&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="background: #e6eed5"&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;YBinding&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;{Binding Value}&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid #b3cc82 1.0pt; border-bottom:  solid #b3cc82 1.0pt; border-right:  none"&gt;&lt;p&gt;SeriesBrush&lt;/p&gt;&lt;/td&gt;&lt;td style="padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid #b3cc82 1.0pt; border-right:  solid #b3cc82 1.0pt"&gt;&lt;p&gt;#FFDB843D&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;表 2&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;需要注意的是，ItemsSource属性的类型是IList，这正是为什么我把代码24的两个属性的类型声明为IList，当然，你也可以把它们声明为List&amp;lt;KeyValuePair&amp;lt;string, int&amp;gt;&amp;gt;。至于第二个StackedBarSeries控件，你只需把ItemsSource的绑定表达式设为"{Binding UnfinishedStatistic}"，并为SeriesBrush属性换一种颜色就行了。&#xD;
&lt;/p&gt;&lt;p&gt;现在，按F5看看效果：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809354828.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 35&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;进入作业本添加一些作业，然后回到主页看看：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809352777.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 36&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;如果作业本里保存了过去五天的作业数据，那么统计图表将会显示成这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080936552.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 37&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;目前，我们是硬性规定显示过去五天的统计结果，这个设定显然无法满足所有用户，比如说，有些用户希望查看过去七天甚至过去十天的统计结果，这个时候可以考虑在应用设置里给出选项。如果你决定给予用户这个选择权，那么你就需要考虑在保证统计图表清晰可辨的情况下最多能够显示多少天的统计结果，你需要给定一个选择上限，同时向用户解释这样做的原因。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;哪些内容是重点？&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;教育的一个重要方面是不断强调，这主要表现为重要的内容经常重复出现。当我们的笔记本记满了笔记，并且通过标签做好分类，我们很自然就会问，哪些标签的出现频率最高，因为这些拥有这些标签的笔记通常都是课堂重点/考试要点。这个时候又轮到统计工具出场了，我们可以通过饼图列出七个出现频率最高的标签，剩下的统一归入"其它"类别，像这样：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809376026.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 38&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;为什么是七个呢？这和大脑的短期记忆容量有关，长期研究表明，这个容量是7&amp;#177;2。事实上，当我们在设计Windows Phone应用的用户界面时，我们应该把短期记忆容量考虑进去，不要在上面放置太多东西，以免用户产生心理饱和。&#xD;
&lt;/p&gt;&lt;p&gt;事不宜迟了，我们动手吧！创建一个Windows Phone Page，并把它命名为TagStatisticPage.xaml：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809384988.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 39&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;然后把应用程序标题和页面标题分别改为"笔记本"和"标签统计"：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809398303.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 40&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接着，从Assets面板上把PieChart控件拖到页面的ContentPanel里，并使之充满整个ContentPanel：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809393253.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 41&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我们的饼图包含了一组统计数据，因此我们需要在PieChart控件里添加一个PieSeries控件，此时，你的Objects and Timeline面板应该是这样的：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080940712.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 42&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;接下来，我们需要为这个页面创建一个ViewModel类。&#xD;
&lt;/p&gt;&lt;p&gt;在ViewModels文件夹里创建一个TagStatisticViewModel类，并在里面创建一个TagStatistic属性：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809409283.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 30&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我们将会在构造函数里初始化这个属性，由于标签统计是针对每个课程而不是所有课程来做的，因此我们需要通过构造函数的参数传递课程名称：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/20110509080940679.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 31&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这个属性的计算过程并不复杂：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809414168.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 43&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;前五个计算步骤可以通过LINQ轻松实现：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809416087.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 32&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;至于最后一步，我们需要分情况处理，如果统计结果包含的分组在七个或者以内，那么我们只需直接显示统计结果；如果超过七个，那么我们需要把剩余的分组聚合到"其它"分类：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809414102.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 33&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;创建好ViewModel类之后，我们就可以把它关联到TagStatisticPage页了。&#xD;
&lt;/p&gt;&lt;p&gt;打开TagStatisticPage.xaml.cs文件，在里面创建一个OnNavigatedTo方法：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809422989.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 34&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;这里假设查询字符串里面包含了一个名为coursename的参数。&#xD;
&lt;/p&gt;&lt;p&gt;最后，我们需要在NoteBookPage页里添加一个Application Bar菜单项，用于打开TagStatisticPage页：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809427973.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 44&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;并在它的Click事件处理程序里添加相关代码：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809428463.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;代码 35&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;值得注意的是，如果课程表没有任何课程，那么我们就不应该打开TagStatisticPage页了，不过，我们需要通过消息框向用户说明一下。&#xD;
&lt;/p&gt;&lt;p&gt;现在，按F5，然后进入笔记本，打开TagStatisticPage页：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809435017.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 45&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;嗯，很好！不过，有几个地方需要调整一下：&#xD;
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;我希望统计图表上方能够显示课程名称；&#xD;
&lt;/li&gt;&lt;li&gt;目前统计图表是从三点位置顺时针展开的，我希望改成从零点位置顺时针展开；&#xD;
&lt;/li&gt;&lt;li&gt;我希望统计图表的配色方案和图38的一样。&#xD;
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;第一个问题很容易解决，我们只需在TagStatisticViewModel类里创建一个Title属性，并在构造函数里把它初始化为课程名称，然后把它绑到PieChart控件的Title属性就行了。第二个问题也很好解决，我们只需把PieSeries控件的StartAngle属性的值改为270就行了。至于第三个问题，我们需要设置PieSeries控件的Brushes属性，按顺序添加相应颜色的SolidColorBrush对象，你可以直接写XAML，也可以通过Blend的Brush Collection Editor来设置：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809443281.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 46&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;我采用的是第二种做法，因为它允许我通过取色器直接从图38上获取颜色，当然，如果你有自己的配色方案，并且知道那些颜色的HTML代码，那么直接写XAML也很方便。改好之后重新运行，然后打开TagStatisticPage页：&#xD;
&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809457327.png" alt="" /&gt;&#xD;
	&lt;/p&gt;&lt;p&gt;&lt;span style="color:#4f81bd; font-size:9pt"&gt;&lt;strong&gt;图 47&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;对比图45，图47给人一种相容有序的感觉，这在很大程度上得益于配色方案的选择，这些颜色都是基于同一色相的，因此能够给人一种相容的感觉，同时，这些颜色根据明度（明亮程度）从小到大顺时针排列，因此给人一种有序的感觉，此外，不同明度能使颜色产生不同的"重量"，明度较低的颜色给人感觉较重，因此用在出现频率较高的标签上，而明度较高的颜色给人感觉较轻，因此用在出现频率较低的标签上，这样用户可以直观地感知内容的重要程度。&lt;span style="text-decoration:underline"&gt;一个好的应用除了能够帮助用户解决相关的问题，还应该照顾到用户的无意识需求&lt;/span&gt;，这不但对于用户来说是一件好事，对于开发者来说也是一件好事，最自然的操作是凭直觉的操作，最自然的选择是符合直觉的选择，这是一种无需经过思考的条件反射，当用户的无意识需求被照顾到了，他们也会不经意地选择你的应用，他们甚至很难解释清楚为什么就要选择你的应用。&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;你一直想要的东西&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;从《WP7有约》系列的第一篇文章发布至今，我收到了无数封索要代码的邮件，但都一一回绝了。如果你在学习这些文章的过程中遇到任何问题，我都乐意为你解答，你可以在文章下面留下评论，我会尽快回复你，这样做的好处是其他人遇到相同/类似问题的同学也可以看到这些交流内容，当然，我也欢迎你通过其它方式和我交流。如果你的问题确实很难描述清楚，你也可以把你的代码发给我，我帮你看看到底哪里出了问题，我之所以愿意花费这个时间是因为你认真对待了这些文章的学习。为什么我会这么肯定呢？因为这个系列的所有代码都是以图片形式呈现的，这导致你无法简单地把它们复制粘贴到你的工程里，你必须亲自输入一遍，这是故意的，因为仅仅看一遍文章虽然可以让你知道那么一回事儿，但这些知识只会暂存在大脑的短期记忆里，过一段时间就会变得模糊，而亲自动手做一遍练习却可以帮助你把知识转存到大脑的长期记忆里，同时，动手的过程中会遇到各种各样的问题，这些问题可以激发你的思考，这对巩固你学到的知识是很有帮助的，因此，当你拿着亲自输入的代码来问我问题时，我也会认真地回答你。&#xD;
&lt;/p&gt;&lt;p&gt;当然，前来索要代码的同学并不都是为了学习这些文章，每个人都有自己的想法和目的，这我能理解，因此，在这个最后的时刻，没错，本文是这个系列的最终回了，我把代码开源出来，你可以到&lt;a href="http://iridescent.codeplex.com/"&gt;iridescent.codeplex.com&lt;/a&gt;下载，这样，你就可以：&#xD;
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;编译代码，然后安装到你的Windows Phone上使用，当然，前提是你的手机已经成功越狱；&#xD;
&lt;/li&gt;&lt;li&gt;提取里面的某些代码，然后用到你自己的项目里；&#xD;
&lt;/li&gt;&lt;li&gt;根据你的需求对应用进行定制，比如去掉一些你不需要的功能，增加/增强一些你所需要的功能；&#xD;
&lt;/li&gt;&lt;li&gt;甚至，你可以把你的定制版本作为免费/付费应用发布到Marketplace上。&#xD;
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;事实上，我也鼓励你根据自己的需求对这个应用进行定制，在这个过程里，你已经学到的知识将会得到巩固，因为你需要充分理解这些知识才能找到恰当的切入点，同时，为了实现新的需求，你会去学习新的知识，并在实践的过程中把它们整合到现有的知识体系里，此外，你的定制版本还可以帮助你以及有类似需求的潜在用户，实乃一箭三雕！&#xD;
&lt;/p&gt;&lt;p&gt;除了代码，另一个被提及最多的要求就是提供这些文章的打包下载。会有的，我会把这些文章整合起来，添加目录，适当排版，然后生成PDF提供下载，不过，这需要一些时间，敬请耐心等待。&#xD;
&lt;/p&gt;&lt;p&gt;最后，终于到了真正的最后，感谢各位一直以来的支持，特别感谢PRO为本系列文章精心打造了一个WP7手机外壳，以及他在图片合成方面为本系列文章提供的帮助！&#xD;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0070c0"&gt;&lt;strong&gt;下课了&amp;#8230;&amp;#8230;&#xD;
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://images.cnblogs.com/cnblogs_com/allenlooplee/201105/201105090809509622.png" alt="" /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/allenlooplee/aggbug/2040785.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/allenlooplee/archive/2011/05/09/2040785.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
