使用 Windows10 自定义交互消息通知
消息通知是最常用的应用功能之一了,但是由于平台的差异,IOS Android 以及 Windows 都有其特殊性,Android开发者在国内常常都是使用三方的一些推送服务,或者是使用自建的服务器为应用提供推送服务,方式多种多样这里就不多阐述了,对比在国内可以使用的两个推送系统 IOS 和 Windows 对比来说,经历了 WindowsPhone 7 / 7.5 /8 / 8.1 /10 五代系统的迭代以后相比 IOS 系统的推送消息的种类来说更加丰富且更加人性化,今天给大家介绍一下如何在 Windows 10 推送消息中显示图片并且实现消息快速回复。
由于 Windows 10 Universal 应用架构这个功能可以实现一次开发可以实现在手机和平板上统统兼容。
由上图看到左侧是 Windows10 UWP 应用在Windows Phone上收到消息通知的样子,右侧的图片是应用在 Windows 10 收到推送消息时的样子。
都是由4个部分组成的:
消息主题:消息发送者照片 + 消息主题与消息文字内容。
消息图片:如果消息发送者消息中包含图片可以在消息预览中直接将图片显示出来。
消息快速回复:如果是即时消息,消息的接收者可以在不启动应用的情况下直接在 吐司消息 或 通知中心中直接回复消息。
消息快速反馈/行动(这个不知道怎么翻译更确切):这里是可以让用户通过按钮的形式对消息进行一个标记或者是一个简短的回复,例如图片中的点赞或打开应用查看详情。
接下来我分享以下这个Toast消息的的XML结构来简单分析以下:
<?xml version="1.0"?>
<toast launch="action=viewConversation conversationId=384928">
<visual>
<binding template="ToastGeneric">
<text>Andrew sent you a picture</text>
<text>Check this out, Happy Canyon in Utah!</text>
<image src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-81-permanent/2727.happycanyon1_5B00_1_5D00_.jpg"/>
<image src="ms-appdata:///local/Andrew.jpg" placement="appLogoOverride" hint-crop="circle"/>
</binding>
</visual>
<actions>
<input id="tbReply" type="text" placeHolderContent="Type a response"/>
<action content="Reply" arguments="action=reply&conversationId=384928" activationType="background" imageUri="Assets/Reply.png" hint-inputId="tbReply"/>
<action content="Like" arguments="action=like&conversationId=384928" activationType="background"/>
<action content="View" arguments="action=viewImage&imageUrl=http%3A%2F%2Fblogs.msdn.com%2Fcfs-filesystemfile.ashx%2F__key%2Fcommunityserver-blogs-components-weblogfiles%2F00-00-01-71-81-permanent%2F2727.happycanyon1_5B00_1_5D00_.jpg"/>
</actions>
</toast>
通过代码我们可以清晰的看到这条消息主要分为两部分
visual binding 部分中主要用来描述消息标题和消息文字内容,其中还包括了两张图片分别是用户头像和消息图片,通过两个 image 标签可以理解到实际上消息内容是一个网络图片,而发送者的头像则是一张应用的本地图片(通过ms-appdata:///local可以看出来)。
其次Action结点下的内容就是一个输入框和三个按钮了,很明显在 content属性中标明了他们的作用,并且请大家注意一下argument属性标明的内容会传入应用用来标记通知或者会话的ID,activationType是用来标记这个按钮触发事件以后,的代码执行位置,Background是一个后台任务(这样就意味着应用可以不启动就可以执行一些操作)。
接着我们来看一下这个应用的声明文件(package.appxmanifest)中声明了这个应用支持 background tasks 并且task类型是一个 System event
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTaskComponent.ToastNotificationBackgroundTask">
<BackgroundTasks>
<Task Type="systemEvent" />
</BackgroundTasks>
</Extension>
</Extensions>
接着我们看看如何在后台任务中处理这个消息通知。首先通过 details.Argument来获取到之前在XML中设置的消息参数,通过swith case按钮的conten属性中的内容来进行逻辑判断从而执行不同的应用程序逻辑。
public async void Run(IBackgroundTaskInstance taskInstance)
{
// Get a deferral since we're executing async code
var deferral = taskInstance.GetDeferral(); try
{
// If it's a toast notification action
if (taskInstance.TriggerDetails is ToastNotificationActionTriggerDetail)
{
// Get the toast activation details
var details = taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail; // Deserialize the arguments received from the toast activation
QueryString args = QueryString.Parse(details.Argument); // Depending on what action was taken...
switch (args["action"])
{
// User clicked the reply button (doing a quick reply)
case "reply":
await HandleReply(details, args);
break; // User clicked the like button
case "like":
await HandleLike(details, args);
break; default:
throw new NotImplementedException();
}
} // Otherwise handle other background activations
else
throw new NotImplementedException();
} finally
{
// And finally release the deferral since we're done
deferral.Complete();
}
}
在实现代码中可以再次获取更详细的参数获得消息会话ID以及用户在快速回复对话框中输入的内容从而与服务器进行交互。
private async Task HandleReply(ToastNotificationActionTriggerDetail details, QueryString args)
{
// Get the conversation the toast is about
int conversationId = int.Parse(args["conversationId"]); // Get the message that the user typed in the toast
string message = (string)details.UserInput["tbReply"]; // In a real app, this would be making a web request, sending the new message
await Task.Delay(TimeSpan.FromSeconds(2.3)); // In a real app, you most likely should NOT notify your user that the request completed (only notify them if there's an error)
SendToast("Your message has been sent! Your message: " + message);
} private async Task HandleLike(ToastNotificationActionTriggerDetail details, QueryString args)
{
// Get the conversation the toast is about
int conversationId = int.Parse(args["conversationId"]); // In a real app, this would be making a web request, sending the like command
await Task.Delay(TimeSpan.FromSeconds(1.1)); // In a real app, you most likely should NOT notify your user that the request completed (only notify them if there's an error)
SendToast("Your like has been sent!");
}
当然也不要忘记在应用启动的时候注册这个BackgroundTask,这部分代码可以放在应用 Onlaunched 和 OnActivated事件中。
private void RegisterBackgroundTask()
{
const string taskName = "ToastBackgroundTask"; // If background task is already registered, do nothing
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
return; // Otherwise create the background task
var builder = new BackgroundTaskBuilder()
{
Name = taskName,
TaskEntryPoint = typeof(ToastNotificationBackgroundTask).FullName
}; // And set the toast action trigger
builder.SetTrigger(new ToastNotificationActionTrigger()); // And register the task
builder.Register();
}
另外刚才除了后台执行代码的两个按钮还有查看消息和查看图片按钮的执行逻辑,这里应该是放在Launched 和 Activated方法中的首先要判断应用的 IActivatedEventArgs类型是否ToastNotificationActivatedEventArgs如果是尝试模仿后台任务中查找按钮参数的方法找到会话ID和照片连接并导航到应用的正确页面去。
private async Task OnLaunchedOrActivated(IActivatedEventArgs e)
{
// Initialize things like registering background task before the app is loaded
await InitializeApp(); #if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif Frame rootFrame = Window.Current.Content as Frame; // Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
// TODO: Load state from previously suspended application
} // Place the frame in the current Window
Window.Current.Content = rootFrame;
} // Handle toast activation
if (e is ToastNotificationActivatedEventArgs)
{
var toastActivationArgs = e as ToastNotificationActivatedEventArgs; // If empty args, no specific action (just launch the app)
if (toastActivationArgs.Argument.Length == )
{
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage));
} // Otherwise an action is provided
else
{
// Parse the query string
QueryString args = QueryString.Parse(toastActivationArgs.Argument); // See what action is being requested
switch (args["action"])
{
// Open the image
case "viewImage": // The URL retrieved from the toast args
string imageUrl = args["imageUrl"]; // If we're already viewing that image, do nothing
if (rootFrame.Content is ImagePage && (rootFrame.Content as ImagePage).ImageUrl.Equals(imageUrl))
break; // Otherwise navigate to view it
rootFrame.Navigate(typeof(ImagePage), imageUrl);
break; // Open the conversation
case "viewConversation": // The conversation ID retrieved from the toast args
int conversationId = int.Parse(args["conversationId"]); // If we're already viewing that conversation, do nothing
if (rootFrame.Content is ConversationPage && (rootFrame.Content as ConversationPage).ConversationId == conversationId)
break; // Otherwise navigate to view it
rootFrame.Navigate(typeof(ConversationPage), conversationId);
break; default:
throw new NotImplementedException();
} // If we're loading the app for the first time, place the main page on the back stack
// so that user can go back after they've been navigated to the specific page
if (rootFrame.BackStack.Count == )
rootFrame.BackStack.Add(new PageStackEntry(typeof(MainPage), null, null));
}
} // Handle launch activation
else if (e is LaunchActivatedEventArgs)
{
var launchActivationArgs = e as LaunchActivatedEventArgs; // If launched with arguments (not a normal primary tile/applist launch)
if (launchActivationArgs.Arguments.Length > )
{
// TODO: Handle arguments for cases like launching from secondary Tile, so we navigate to the correct page
throw new NotImplementedException();
} // Otherwise if launched normally
else
{
// If we're currently not on a page, navigate to the main page
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage));
}
} else
{
// TODO: Handle other types of activation
throw new NotImplementedException();
} // Ensure the current window is active
Window.Current.Activate();
}
好了这样你的应用就可以处理 Windows10 可交互性的自定义消息通知了。
并且为了测试方便你可以在应用中使用 NotificationsExtensions.Win10 这个扩展类库进行测试,在nuget上可以直接下载到。
https://www.nuget.org/packages/NotificationsExtensions.Win10/
测试代码如下:
private void ButtonSendToast_Click(object sender, RoutedEventArgs e)
{
// In a real app, these would be initialized with actual data
string title = "Andrew sent you a picture";
string content = "Check this out, Happy Canyon in Utah!";
string image = "http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-81-permanent/2727.happycanyon1_5B00_1_5D00_.jpg";
string logo = "ms-appdata:///local/Andrew.jpg";
int conversationId = ; // Construct the visuals of the toast
ToastVisual visual = new ToastVisual()
{
TitleText = new ToastText()
{
Text = title
}, BodyTextLine1 = new ToastText()
{
Text = content
}, InlineImages =
{
new ToastImage()
{
Source = new ToastImageSource(image)
}
}, AppLogoOverride = new ToastAppLogo()
{
Source = new ToastImageSource(logo),
Crop = ToastImageCrop.Circle
}
}; // Construct the actions for the toast (inputs and buttons)
ToastActionsCustom actions = new ToastActionsCustom()
{
Inputs =
{
new ToastTextBox("tbReply")
{
PlaceholderContent = "Type a response"
}
}, Buttons =
{
new ToastButton("Reply", new QueryString()
{
{ "action", "reply" },
{ "conversationId", conversationId.ToString() } }.ToString())
{
ActivationType = ToastActivationType.Background,
ImageUri = "Assets/Reply.png", // Reference the text box's ID in order to
// place this button next to the text box
TextBoxId = "tbReply"
}, new ToastButton("Like", new QueryString()
{
{ "action", "like" },
{ "conversationId", conversationId.ToString() } }.ToString())
{
ActivationType = ToastActivationType.Background
}, new ToastButton("View", new QueryString()
{
{ "action", "viewImage" },
{ "imageUrl", image } }.ToString())
}
}; // Now we can construct the final toast content
ToastContent toastContent = new ToastContent()
{
Visual = visual,
Actions = actions, // Arguments when the user taps body of toast
Launch = new QueryString()
{
{ "action", "viewConversation" },
{ "conversationId", conversationId.ToString() } }.ToString()
}; // And create the toast notification
ToastNotification notification = new ToastNotification(toastContent.GetXml()); // And then send the toast
ToastNotificationManager.CreateToastNotifier().Show(notification);
}
有即时消息需求的应用是非常适合这个功能的,行动起来快试试把这个功能加到应用中吧。
参考资料:
Adaptive Tile Templates - Schema and Documentation
What's new with live tiles in Windows 10
What's new/different with toast notifications and action center in Windows 10
Quickstart: Sending a local toast notification and handling activations from it (Windows 10)
Adaptive and interactive toast notifications for Windows 10
希望上的总结可以帮助到大家, 同时欢迎大家在这里和我沟通交流或者在新浪微博上 @王博_Nick
使用 Windows10 自定义交互消息通知的更多相关文章
- 自定义WM_NOTIFY消息
自定义WM_NOTIFY消息 习惯了用自定义用户消息进行各种状态的通知,特别是子窗口与父窗口之间的交互.但ON_MESSAGE没有控件ID的限制,如果有多个子窗口发送同一个消息给父窗口时,父窗口就不知 ...
- 一个I/O线程可以并发处理N个客户端连接和读写操作 I/O复用模型 基于Buf操作NIO可以读取任意位置的数据 Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel 事件驱动消息通知观察者模式
Tomcat那些事儿 https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650860016&idx=2&sn=549 ...
- Android中的消息通知(NotificationManager和Notification)
下面来谈谈notification,这个notification一般用在电话,短 信,邮件,闹钟铃声,在手机的状态栏上就会出现一个小图标,提示用户处理这个通知,这时手从上方滑动状态栏就可以展开并处理这 ...
- Android消息通知(notification)和PendingIntent传值
通知栏的自定义布局:转:http://blog.csdn.net/vipzjyno1/article/details/25248021 拓展 实现自定义的通知栏效果: 这里要用到RemoteViews ...
- HTML 5的消息通知机制
译文来源:http://www.ido321.com/1130.html 原文:HTML 5 Notification 译文:HTML 5 的消息通知机制 译者:dwqs HTML 5 已经被应用到W ...
- 微信小程序客服消息开发实战:实时在手机上接收小程序客服消息通知,以及在手机上回复
在微信小程序开发中,可以非常方便的集成客服功能,只需要一行代码便可以将用户引导至客服会话界面.这行代码就是: <button open-type="contact" bind ...
- Redis自学笔记:4.4进阶-消息通知
4.4消息通知 4.4.1任务队列 传递任务的队列.与任务队列进行交互的实体有两类,一类是生产者,一类是消费者. 生产者将需要处理的任务放入任务队列中,二消费者不断从任务队列中读入任务 信息并执行. ...
- [UWP]实现一个轻量级的应用内消息通知控件
在UWP应用开发中,我们常常有向用户发送一些提示性消息的需求.这种时候我们一般会选择MessageDialog.ContentDialog或者ToastNotification来完成功能. 但是,我们 ...
- jenkins 构建后发送钉钉消息通知(插件)
钉钉,越来越多的公司采用,那么我们在持续集成中,也可以直接选择钉钉插件的,在之前的博客中 ,对发送的钉钉消息进行了定制,那样的话会开启一个新的任务, 其实今天呢,我们可以直接安装一个插件就可以发送了, ...
随机推荐
- 利用Junit4进行程序模块的测试,回归测试
①在你的工程里导入JUnit4的包 ②右击创建JUnit测试类,在测试类中编写测试代码即可. JUnit 目前需要掌握的有一下几点: Fixture系列:BeforeClass,AfterClass, ...
- C++ WIN32控制台异常关闭回调函数
/* This is an example of the SetConsoleCtrlHandler function that is used to install a control handle ...
- 使用Lucene.NET实现数据检索功能
引言 在软件系统中查询数据是再平常不过的事情了,那当数据量非常大,数据存储的媒介不是数据库,或者检索方式要求更为灵活的时候,我们该如何实现数据的检索呢?为数据建立索引吧,利用索引技术可以更灵活 ...
- winform下重画ListBox
Windows Forms是由Win32 API封装的开发组件,最初是为了替代mfc,但却没有体现与Model View Controller架构对应的特色,进而在.net framework 3.0 ...
- duilib进阶教程 -- 改进List控件 (16)
一.控件隐藏后,允许用代码操作所有行为. 在做播放器的时候,最常用的功能莫过于顺序播放.随机播放了,而当我们切换歌曲的时候,显然应该选中该歌曲,List的选中函数是SelectItem,但是调用此函数 ...
- 每天一个linux命令(4):mkdir命令
linux mkdir 命令用来创建指定的名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录. 1.命令格式: mkdir [选项] 目录... 2.命令 ...
- mysql闪退或者can not connect 127.0.0.1
MYSQL 无安装文件 exe执行时闪退 mysql闪退或者can not connect 127.0.0.1 APP 百款主流机型兼容性免费测 » Mysql 官网上下载的Mysql 但是没有 ...
- Leetcode 172 Factorial Trailing Zeroes
给定一个数n 求出n!的末尾0的个数. n!的末尾0产生的原因其实是n! = x * 10^m 如果能将n!是2和5相乘,那么只要统计n!约数5的个数. class Solution { public ...
- Win7下SQLite的简单使用
前言 SQLite 是一个软件库,实现了自给自足的.无服务器的.零配置的.事务性的 SQL 数据库引擎.SQLite 是在世界上最广泛部署的 SQL 数据库引擎.SQLite 源代码不受版权限制. 简 ...
- 从Windows XP系统迁移到Windows 7,Windows 8开始
Microsoft在2014年4月8日结束了Windows XP的支持.您的公司准备好了吗?如果您还没有迁移到Windows 7或8,那就要抓紧时间了.从现在起将不再向XP系统提供安全修补程序,而仍然 ...