制作磁贴
- 要求使用标准的处理XML DOM方式创建动态磁贴
- 要求采用Adaptive Tile (覆盖至少small、medium、wide)
- 实现效果:要求每添加一条项目,磁贴能进行更新,并且更新的内容循环展示(1-2-3-4-5-1-2-3-4……)
- (Bonus:为磁贴添加背景图片)
设置磁贴
双击项目列表中的Package.appxmanifest文件,然后将想要显示的图片放到相应的图标里面就可以,像素一定要吻合,我是直接从百度图片上,按照像素大小筛选图片,随便选的,就不用格外的图片处理。
磁贴的更新
在MainPage.xaml.cs中加入下面函数
private void UpdatePrimaryTile(string input,string input2)
{
var xmlDoc = TileService.CreateTiles(new PrimaryTile(input,input2));
var updater = TileUpdateManager.CreateTileUpdaterForApplication();
TileNotification notification = new TileNotification(xmlDoc);
updater.Update(notification);
}
里面有TileService.CreateTiles
这个函数,主要用来创建一个xml文件,然后将文件放到TileNotification文件里update就可以实现磁贴的更新了。
TileService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using first_project.Models;
using System.Xml.Linq;
namespace first_project.Services
{
class TileService
{
public static Windows.Data.Xml.Dom.XmlDocument CreateTiles(PrimaryTile primaryTile)
{
XDocument xDoc = new XDocument(
new XElement("tile", new XAttribute("version", 3),
new XElement("visual",
// Small Tile
new XElement("binding", new XAttribute("branding", primaryTile.branding), new XAttribute("displayName", primaryTile.appName), new XAttribute("template", "TileSmall"),
new XElement("image", new XAttribute("placement", "background"), new XAttribute("src", "Assets/1.scale-200.png")),
new XElement("group",
new XElement("subgroup",
new XElement("text", primaryTile.time, new XAttribute("hint-style", "caption")),
new XElement("text", primaryTile.message, new XAttribute("hint-style", "captionsubtle"), new XAttribute("hint-wrap", true), new XAttribute("hint-maxLines", 3))
)
)
),
// Wide Tile
new XElement("binding", new XAttribute("branding", primaryTile.branding), new XAttribute("displayName", primaryTile.appName), new XAttribute("template", "TileWide"),
new XElement("image", new XAttribute("placement", "background"), new XAttribute("src", "Assets/3.png")),
new XElement("group",
new XElement("subgroup",
new XElement("text", primaryTile.time, new XAttribute("hint-style", "caption")),
new XElement("text", primaryTile.message, new XAttribute("hint-style", "captionsubtle"), new XAttribute("hint-wrap", true), new XAttribute("hint-maxLines", 3))
),
new XElement("subgroup", new XAttribute("hint-weight", 15),
new XElement("image", new XAttribute("placement", "inline"), new XAttribute("src", "Assets/StoreLogo.png"))
)
)
),
// Medium Tile
new XElement("binding", new XAttribute("branding", primaryTile.branding), new XAttribute("displayName", primaryTile.appName), new XAttribute("template", "TileMedium"),
new XElement("image", new XAttribute("placement", "background"), new XAttribute("src", "Assets/2.png")),
new XElement("group",
new XElement("subgroup",
new XElement("text", primaryTile.time, new XAttribute("hint-style", "caption")),
new XElement("text", primaryTile.message, new XAttribute("hint-style", "captionsubtle"), new XAttribute("hint-wrap", true), new XAttribute("hint-maxLines", 3))
)
)
)
)
)
);
Windows.Data.Xml.Dom.XmlDocument xmlDoc = new Windows.Data.Xml.Dom.XmlDocument();
xmlDoc.LoadXml(xDoc.ToString());
return xmlDoc;
}
}
}
这个就是上文提到的TileService.cs类,用的是c#的方式创建xml,当然可以直接写xml,有关这个文件基本上是不会报错的,就是说即使不小心写错了,也只能肉眼debug,所以这个文件我几乎没改,因为我之前绑定的不是PrimaryTile
类,而是ListItem类,这样就可以将ListItem的数据显示在磁贴上,但是无论怎么做都没有反应,我估计可能还是xml文件的问题,于是我尽量不修改官方文档中的xml文件。
PrimaryTile .cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace first_project.Models
{
public class PrimaryTile
{
public string time { get; set; } = "ok";
public string message { get; set; } = "nothing";
public string message2 { get; set; } = " At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident.";
public string branding { get; set; } = "name";
public string appName { get; set; } = "HoL";
public PrimaryTile(string input,string input2) {
time = input;
message = input2;
}
}
}
这个就是不得已写的PrimaryTile 类,其中的input和input2,我传入的是ListItem中的title和description,所以也算实现绑定了。
磁贴的循环
首先在App.xaml.cs的构造函数中加入下面两句,第一句清空,第二句建立循环队列。
TileUpdateManager.CreateTileUpdaterForApplication().Clear();
TileUpdateManager.CreateTileUpdaterForApplication().EnableNotificationQueue(true);
在MainPage.xaml.cs中加入下面函数
private void circulationUpdate()
{
TileUpdateManager.CreateTileUpdaterForApplication().Clear();
for (int i = 0;i< ((App)App.Current).ViewModel.AllItems.Count(); i++)
{
UpdatePrimaryTile(((App)App.Current).ViewModel.AllItems[i].title, ((App)App.Current).ViewModel.AllItems[i].description);
}
}
函数作用是,一开始清空,然后将所有的ListItem的title和description用UpdatePrimaryTile生成xml文件,这样的话就可以实现循环显示,这个循环队列应该是有上限的,但是没测试过,至少五个是没问题的,可以满足要求。
Bouns,磁贴设置背景图片
就一句话new XElement("image", new XAttribute("placement", "background"), new XAttribute("src", "Assets/1.scale-200.png")),
上面的TileService.cs
类中已经有了,我是每个背景图片就是原本的磁贴样式。
实现App-to-App communication
- 在MenuFlyoutItem中增加Share选项,点击后相应条目能以邮件方式进行共享(不要求动态共享图片)
- 动态共享图片(虽然没要求)
MenuFlyoutItem中增加Share选项
在MainPage.xmal中,将下面代码放到ListItem内容中的后面,那么每一行就会多出一个设置按钮,点击之后会出现share文字。
<AppBarButton Grid.Column="3" Icon="Setting" IsCompact="True">
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="Share" Click="MenuFlyoutItem_Click"/>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
以邮件方式共享
在MainPage.xaml.cs中
两个函数中的结尾分别加一句话
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
......
DataTransferManager.GetForCurrentView().DataRequested -= OnShareDataRequested;
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
......
DataTransferManager.GetForCurrentView().DataRequested += OnShareDataRequested;
}
MenuFlyoutItem_Click
当点击share的时候调用这个函数,selectedItem就是点击share所在的那个ListItem,我把它设置为MainPage.xaml.cs文件的全局变量。
private void MenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
MenuFlyoutItem se = sender as MenuFlyoutItem;
var dc = se.DataContext as ListItem;
selectedItem = dc;
DataTransferManager.ShowShareUI();
}
OnShareDataRequested
这个函数就是传送的包,为包赋值,如果不加入图片的话就这样可以了。
public void OnShareDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var dp = args.Request.Data;
var deferral = args.Request.GetDeferral();
dp.Properties.Title = selectedItem.title;
dp.Properties.Description = selectedItem.description;
dp.SetWebLink(new Uri("http://seattletimes.com/ABPub/2006/01/10/2002732410.jpg"));
deferral.Complete();
}
动态共享图片
这个实现起来还是有点难度的,下面是PPT里面的方法,这个方法似乎连创建静态的图片都成问题,至少我没成功。
async void OnShareDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var dp = args.Request.Data;
var deferral = args.Request.GetDeferral();
var photoFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/needle.jpg"));
dp.Properties.Title = "Space Needle";
dp.Properties.Description = "The Space Needle in Seattle, WA";
dp.SetStorageItems(new List<StorageFile> { photoFile });
dp.SetWebLink(new Uri("http://seattletimes.com/ABPub/2006/01/10/2002732410.jpg"));
deferral.Complete();
}
我的方法
在MainPage.xaml.cs中加入一个全局变量public StorageFile imageFile;
,然后在Select_Click
中StorageFile file = await openPicker.PickSingleFileAsync();
如果file != null
,则imageFile = file;
这样在选择的时候就会得到imageFile文件。
OnShareDataRequested
public void OnShareDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var dp = args.Request.Data;
var deferral = args.Request.GetDeferral();
dp.Properties.Title = selectedItem.title;
dp.Properties.Description = selectedItem.description;
try
{
dp.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageFile));
}
catch
{
dp.SetBitmap(RandomAccessStreamReference.CreateFromUri(selectedItem.src.UriSource));
}
dp.SetWebLink(new Uri("http://seattletimes.com/ABPub/2006/01/10/2002732410.jpg"));
deferral.Complete();
}
因为有两种可能,一种直接分享,没有点击选择图片按钮,那么imageFile是空,就会异常,所以直接用选择的selectedItem.src.UriSource
,为什么不直接用selectedItem.src.UriSource
呢,这就是奇怪的地方,只有不选的时候这个值才不是空,一旦选择图片了,这个值就是空值,无论是传到NewPage,还是点击的时候查看,放在监视器里面看都是空值,所以当选择了之后换一种写法,将CreateFromUri
换成CreateFromFile
,然后传入imageFile。
图片的bug
最后发现有一点小bug,在我第一次选择了图片之后,重启项目发现,不是默认的图片,而是选择了的图片,这个时候如果再选择图片,则try和catch中的值都为空,所以就会报错。
经过排查是上次的项目有问题,当页面是新打开的时候,没有清空FutureAccessList
里面的图片,导致会遗留到下一次打开,以下是新的OnNavigatedTo
函数,在MainPage.xaml.cs中。
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.New)
{
ApplicationData.Current.LocalSettings.Values.Remove("MainPage");
ApplicationData.Current.LocalSettings.Values["TempImage"] = null;
}
if (ApplicationData.Current.LocalSettings.Values["TempImage"] != null)
{
StorageFile tempimg;
tempimg = await StorageApplicationPermissions.FutureAccessList.GetFileAsync((string)ApplicationData.Current.LocalSettings.Values["TempImage"]);
IRandomAccessStream ir = await tempimg.OpenAsync(FileAccessMode.Read);
BitmapImage bi = new BitmapImage();
await bi.SetSourceAsync(ir);
NewImage.Source = bi;
ApplicationData.Current.LocalSettings.Values["TempImage"] = null;
}
{
if (ApplicationData.Current.LocalSettings.Values.ContainsKey("MainPage"))
{
var composite = ApplicationData.Current.LocalSettings.Values["MainPage"] as ApplicationDataCompositeValue;
TitleBlock.Text = (string)composite["Title"];
DetailBlock.Text = (string)composite["Details"];
Date.Date = (DateTimeOffset)composite["Date"];
((App)App.Current).ViewModel.AllItems[0].Completed = (bool)composite["Visible"];
ApplicationData.Current.LocalSettings.Values.Remove("MainPage");
}
}
DataTransferManager.GetForCurrentView().DataRequested += OnShareDataRequested;
}