使用UI control创建动态的live tile (Windows 8.1 XAML/VB.net)

        Windows store app可以通过live tile向用户展示动态信息,但是如果您想您的应用程序能够以丰富和多样化的UI展示信息时,那么标准的live tile模板就很难能达到您的要求了。

        本篇博客将介绍如何通过user controls来定义您的live tile的布局,您只需要将这个user control 以bitmap的形式保存,然后再添加live tile的内容即可。这样除了Windows 8中标准的live tile布局,您也可以得到如下效果:

        虽然这也许不是您见过的最具吸引力的live tile,但是它的可取之处是它是由一个完全可定制的user control 以image的方式来呈现的。您可以更改上面图片中的任何一个UI element,这样可以动态呈现您的应用程序的状态。

        接下来, 我们将讨论如何创建一个这样的live tile,在文章的结尾处您可以下载该project的完整代码。

主要步骤

        创建自定义的live tile分为以下三个步骤:

  1. 为您的live tile的布局创建一个user control
  2. 将这个user control以bitmap的形式呈现
  3. 在image上更新您的live tile的内容

注意事项

        在创建这个live tile之前,您需要确认您的系统是Windows 8.1,并且已经安装visual studio 的2013版本(包括visual studio express),因为本文中的示例不能在Windows 8.0上正常运行。

        在visual studio 2013中新建一个项目例如: Visual Basic -> Windows store app -> BlankApp (XAML).

第一步:创建User Control

        您的live tile将呈现什么样内容是没有限制的,我们会在以下的例子当中创建两个UI element(一个宽屏的tile和一个方屏的tile)。

        创建两个user controls的步骤如下:

  1. 在解决方案资源管理器中右键单击该项目的名称,选择添加 -> 新项目
  2. 选择user control
  3. 键入一个名称(分别为WideTileTemplate.xaml 和 MediumTileTemplate.xaml)
  4. 单击添加

  

        向您的user control中添加什么UI内容取决于您所希望的live tile的布局。你可以参考下面的代码或者您也可以自定义您的User control。

注意: 在使用下面的代码之前您需要在您的project的根目录下添加一张名为“'image.png” 的图片:选择添加 -> 现有项目,然后浏览并选择您计算机上的 .png格式的文件。(请注意, 为确保您的图片能够正常的显示, 请将图片更名为“'image.png”)。

此外,我们需要为这个control定义合适的大小,比如将宽屏的tile的大小定义为691 x 336像素,将方屏的tile的大小定义为336x336像素。

宽屏的tile的user control的定义如下:

 <UserControl x:Class="TileSamplerApp.WideTileTemplate" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml " 
 xmlns:local="using:TileSamplerApp"
 xmlns:d="https://schemas.microsoft.com/expression/blend/2008 " xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006 "
 mc:Ignorable="d"
 d:DesignHeight="336" d:DesignWidth="691">
 
 <Canvas x:Name="LayoutRoot" Width="691" Height="336" Background="Yellow">
 <Image Source="image.png" HorizontalAlignment="Left" Height="316" VerticalAlignment="Top" 
 Width="332" Canvas.Left="10" Canvas.Top="10"/>
 <TextBlock Text="Hello World" HorizontalAlignment="Left" Height="39" TextWrapping="Wrap" VerticalAlignment="Top" Width="266" FontSize="26" Foreground="Black" FontWeight="Bold" Canvas.Left="347" Canvas.Top="46"/> 
 <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Some more text!" VerticalAlignment="Top" Width="277" RenderTransformOrigin="0.5,0.5" Canvas.Left="347" Canvas.Top="159" FontSize="26" Height="30" Foreground="#FFE81C1C"/>
 <TextBlock Text="37" x:Name="Number" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Top" Height="97" Width="122" FontSize="72" TextAlignment="Center" Canvas.Left="559" Canvas.Top="229" Foreground="#FF42105F"/>
 </Canvas>
 </UserControl>
 

方屏tile的user control的定义如下:

 <UserControl
 x:Class="TileSamplerApp.MediumTileTemplate"
 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation "
 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml "
 xmlns:local="using:TileSamplerApp"
 xmlns:d="https://schemas.microsoft.com/expression/blend/2008 "
 xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006 "
 mc:Ignorable="d"
 d:DesignHeight="336" d:DesignWidth="336">
 
 <Canvas x:Name="LayoutRoot" Width="336" Height="336" Background="Yellow">
 <Image Source="image.png" HorizontalAlignment="Left" Height="167" Width="178"
 VerticalAlignment="Top" Canvas.Left="27" Canvas.Top="70"/>
 <TextBlock Text="Hello World" HorizontalAlignment="Left" Height="39" TextWrapping="Wrap" VerticalAlignment="Top" Width="266" FontSize="26" Foreground="Black" FontWeight="Bold" Canvas.Left="36"
 Canvas.Top="10"/>
 <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Some more text!"
 VerticalAlignment="Top" Width="277" RenderTransformOrigin="0.5,0.5" Canvas.Left="36" Canvas.Top="270"
 FontSize="26" Height="30" Foreground="#FFE81C1C"/>
 <TextBlock Text="37" x:Name="Number" HorizontalAlignment="Center" TextWrapping="Wrap"
 VerticalAlignment="Top" Height="97" Width="122" FontSize="72" TextAlignment="Center" Canvas.Left="210"
 Canvas.Top="102" Foreground="#FF42105F"/>
 </Canvas>
 </UserControl>
 

将这两个user control添加到MainPage.xaml页面

  1. 双击资源管理器中的MainPage.xaml,以便在designer设计器中打开它。
  2. 然后用下面的代码替换XAML页面的内容:
 <Page x:Class="TileSamplerApp.MainPage"
 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml "
 xmlns:local="using:TileSamplerApp" xmlns:d="https://schemas.microsoft.com/expression/blend/2008 " xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006 "
 mc:Ignorable="d">
 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
 <local:WideTileTemplate x:Name="WideTile" HorizontalAlignment="Left" VerticalAlignment="Top"/>
 <local:MediumTileTemplate x:Name="MediumTile" HorizontalAlignment="Right" VerticalAlignment="Bottom"/> <Button Content="Create Tile" HorizontalAlignment="Left" Margin="170,606,0,0" VerticalAlignment="Top" Height="116" Width="210" Click="Button_Click"/>
 </Grid>
 </Page>
 

这样会向MainPage页面添加两个controls,设计器的样式如下所示:

回顾

        现在您项目的根目录下已包含以下文件:

        一个名为“image.png”的图片文件

        一个名为WideTileTemplate.xaml的user control

        一个名为MediumTileTemplate.xaml的user control

        一个包含两个user controls的MainPage.xaml页面

第二步:将user controls 以bitmap的形式呈现并保存

        现在您已经成功的创建了您的user control 并可以在页面上显示,那么下一步您需要做的是将user control以image的形式呈现并将它们保存在您的应用程序的storage 文件夹下。

        BitmapRender创建了一个像素数据流,这个流组成了您的用户控件。这个数据流被编码过了(在本文中是.png格式),并且存储在一个文件里。

        对于上面所示步骤,我们将创建两个方法。第一个是将组成user control的像素转换成数据流,另一个是将数据流保存到文件中。

        您需要将上述步骤的代码放在MainPage.xaml.vb页面。

        在对user control 进行编码并将其存储到文件之前,您需要在您的page页面添加一些命名空间。将下面的代码放在MainPage.xaml.vb页面的最顶端。这些代码能使我们在 MainPage.xaml.vb页面访问和使用某些Visual Basic的API和功能。

  Imports Windows.Storage
 Imports Windows.Graphics.Imaging
 Imports Windows.Storage.Streams
 Imports Windows.UI.Notifications
 Imports NotificationsExtensions.TileContent
 

        安装Notifications Extensions

        您需要添加Notifications Extensions

  1. 在visual studio窗口顶部的菜单栏中选择:TOOLS  -> Library Package Manager -> Package Manager Console.
  2. 在底部的Package Manager  Console窗口会出现PM -> 提示符,您可以键入:Install-Package NotificationsExtensions.WinRT
  3. 按enter键

        在MainPage.xaml.vb中, 以下代码用作实现将一个user control 以bitmap的形式呈现。

 Public Async Function GenerateImageToFile(fileName As String, uiContent As FrameworkElement) As Task
 Dim file As StorageFile = Await ApplicationData.Current.LocalFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting)
 If file IsNot Nothing Then
 CachedFileManager.DeferUpdates(file)
 Using stream = Await file.OpenAsync(FileAccessMode.ReadWrite)
 Dim b = Await CaptureToStreamAsync(uiContent, stream, BitmapEncoder.PngEncoderId)
 End Using
 Dim status = Await CachedFileManager.CompleteUpdatesAsync(file)
 End If
 End Function
 
 Private Async Function CaptureToStreamAsync(uielement As FrameworkElement, stream As IRandomAccessStream, encoderId As Guid) As Task(Of RenderTargetBitmap)
 Dim renderTargetBitmap = New RenderTargetBitmap()
 Await renderTargetBitmap.RenderAsync(uielement)
 Dim pixels = Await renderTargetBitmap.GetPixelsAsync()
 Dim logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi
 Dim encoder = Await BitmapEncoder.CreateAsync(encoderId, stream)
 encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, CUInt(renderTargetBitmap.PixelWidth), CUInt(renderTargetBitmap.PixelHeight), logicalDpi, logicalDpi, pixels.ToArray())
 Await encoder.FlushAsync()
 Return renderTargetBitmap
 End Function
 

让我们来逐步理解这些代码:

 Public Async Function GenerateImageToFile(fileName As String, uiContent As FrameworkElement) As Task

 GenerateImageToFile方法是以一个文件名和user control作为输入参数,然后创建一个.png格式的image文件

 Dim file As StorageFile = Await ApplicationData.Current.LocalFolder.CreateFileAsync(fileName , CreationCollisionOption.ReplaceExisting)

这个方法是根据fileName这个输入参数创建一个名为fileName的文件

 If file IsNot Nothing Then CachedFileManager.DeferUpdates(file)

这个方法检查该文件是否存在,如果存在的话那么系统将停止对它进行更新和修改

 Using stream = Await file.OpenAsync(FileAccessMode.ReadWrite)
 Dim b = Await CaptureToStreamAsync(uiContent, stream, BitmapEncoder.PngEncoderId)
 End Using
 

这个代码块是先打开一个文件并将数据流存储在这个文件中。其中第二行代码是通过使用CaptureToStreamAsync方法将数据流进行编码(即将user control转换为.png格式的图像)

 Dim status = Await CachedFileManager.CompleteUpdatesAsync(file)
 End If
 

上面的代码是确保该方法直到将数据流完全更新到file中才真正结束。

 Private Async Function CaptureToStreamAsync(uielement As FrameworkElement, stream As IRandomAccessStream, encoderId As Guid) As Task(Of RenderTargetBitmap)

        CaptureToStreamAsync方法根据user control的像素数据流将其转换为正确的图像格式。这个方法将一个framework element、数据流、一个编码器的encoderId作为输入,并将该framework element转化为图像。该函数的返回值是一个RenderTargetBitmap类型,随后我们将会为其保存为图像文件。

 Dim renderTargetBitmap = New RenderTargetBitmap()
 Await renderTargetBitmap.RenderAsync(uielement)
 

这两行代码是将图像数据刷新到RenderTargetBitmap对象以完成bitmap的创建。

        下面我们需要做的是根据上面创建好的image来定义live tile。

步骤三:创建live tile

         现在我们来创建一个新的tile并更新应用程序的原有的tile。此示例将会创建宽屏和方屏这两种类型的tile。

注意: 您需要在您的应用程序清单中添加一个默认的宽屏tile以便您能够创建宽屏的tile。默认情况下,应用程序仅仅支持中型的和小型的tile。步骤如下:

  1. 创建一个310*150像素的.png格式的图片
  2. 在项目资源管理器中,双击Package.appxmanifest 文件
  3. 选择Visual Assets选项卡
  4. 在左侧栏中选择310*150的徽标
  5. 在Scaled Assets中选择 310x150 图片文件来填充这个徽标.

现在你可以在您的项目中启用宽屏的tile了。

你可以将以下代码添加到您的MainPage.xaml.vb页面, 创建宽型和中型的live tile:

 Public Sub SetLiveTileImage(wideImageFileName As String, mediumImageFileName As String) TileUpdateManager.CreateTileUpdaterForApplication.Clear()
 Dim tileContent As NotificationsExtensions.TileContent.ITileWideImage = TileContentFactory.CreateTileWideImage()
 Dim squareTileContent = NotificationsExtensions.TileContent.TileContentFactory.CreateTileSquareImage() tileContent.Image.Src = "ms-appdata:///local/" & wideImageFileName squareTileContent.Image.Src = "ms-appdata:///local/" & mediumImageFileName tileContent.SquareContent = squareTileContent tileContent.Branding = TileBranding.None
 Dim MyTileNotification As TileNotification = tileContent.CreateNotification() TileUpdateManager.CreateTileUpdaterForApplication.Update(MyTileNotification)
 End Sub
 

注意: 不同的tile 模板有不同的信息,但是我们创建一个仅包含图片的最基本的live tile。

最后一步:将代码整合在一起

         所有的工作您已经准备好了,但是你现在要做的是运行这些代码来创建一个live tile。在本例中我们将live tile的更新工作放在一个Button的click事件中。但是在您的应用程序中,您需要根据相应的逻辑来更新live tile,比如一个游戏的进度发生变化或者您的应用程序中产生了一个新的消息。

        您可以向您的MainPage.xaml页面添加一个button控件,将下面的代码粘贴到您的user control代码的后面:

 <Button Content="Create Tile" HorizontalAlignment="Left" Margin="170,606,0,0" VerticalAlignment="Top" Height="116" Width="210" Click="Button_Click" />

现在您可以向Button click 事件添加事件处理程序了,您可以直接打开MainPage.xaml.vb页面并向其中添加Button_Click方法,或者在设计器中双击Button,这样您可以自动创建Button_Click方法:

 Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
 Await GenerateImageToFile("wideTile.png", WideTile)
 Await GenerateImageToFile("mediumTile.png", MediumTile)
 SetLiveTileImage("wideTile.png", "mediumTile.png")
 End Sub
 

您可以尝试运行上面的代码,如果您有两台监视器,可以让其中一台运行开始菜单项,另一台运行该项目,当点击button的时候,您就可以在菜单项中看到tile更新。

        该示例说明如何自定义一个live tile 的UI,如果您感兴趣, 我们还可以尝试, 比如:

         ●向page页面添加一个TextBox,并将其中的Text填充到live tile中

         ●确保您页面中的某些control即使不可见也能保证live tile正确呈现

         ●通过FilePicker从Picture Library选择图片并填充到live tile中

         ●尝试各种不同的UI布局

问题答疑

        如果您的tile并没有及时更新。这可能与windows限制tile更新的次数有关。可以尝试快速重启机器解决该问题。

下载

下载整个项目