消息通知是最常用的应用功能之一了,但是由于平台的差异,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结构来简单分析以下:

  1. <?xml version="1.0"?>
  2. <toast launch="action=viewConversation conversationId=384928">
  3. <visual>
  4. <binding template="ToastGeneric">
  5. <text>Andrew sent you a picture</text>
  6. <text>Check this out, Happy Canyon in Utah!</text>
  7. <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"/>
  8. <image src="ms-appdata:///local/Andrew.jpg" placement="appLogoOverride" hint-crop="circle"/>
  9. </binding>
  10. </visual>
  11. <actions>
  12. <input id="tbReply" type="text" placeHolderContent="Type a response"/>
  13. <action content="Reply" arguments="action=reply&amp;conversationId=384928" activationType="background" imageUri="Assets/Reply.png" hint-inputId="tbReply"/>
  14. <action content="Like" arguments="action=like&amp;conversationId=384928" activationType="background"/>
  15. <action content="View" arguments="action=viewImage&amp;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"/>
  16. </actions>
  17. </toast>

通过代码我们可以清晰的看到这条消息主要分为两部分

visual binding 部分中主要用来描述消息标题和消息文字内容,其中还包括了两张图片分别是用户头像和消息图片,通过两个 image 标签可以理解到实际上消息内容是一个网络图片,而发送者的头像则是一张应用的本地图片(通过ms-appdata:///local可以看出来)。

其次Action结点下的内容就是一个输入框和三个按钮了,很明显在 content属性中标明了他们的作用,并且请大家注意一下argument属性标明的内容会传入应用用来标记通知或者会话的ID,activationType是用来标记这个按钮触发事件以后,的代码执行位置,Background是一个后台任务(这样就意味着应用可以不启动就可以执行一些操作)。

接着我们来看一下这个应用的声明文件(package.appxmanifest)中声明了这个应用支持 background tasks 并且task类型是一个 System event

  1. <Extensions>
  2. <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTaskComponent.ToastNotificationBackgroundTask">
  3. <BackgroundTasks>
  4. <Task Type="systemEvent" />
  5. </BackgroundTasks>
  6. </Extension>
  7. </Extensions>

接着我们看看如何在后台任务中处理这个消息通知。首先通过 details.Argument来获取到之前在XML中设置的消息参数,通过swith case按钮的conten属性中的内容来进行逻辑判断从而执行不同的应用程序逻辑。

  1. public async void Run(IBackgroundTaskInstance taskInstance)
  2. {
  3. // Get a deferral since we're executing async code
  4. var deferral = taskInstance.GetDeferral();
  5.  
  6. try
  7. {
  8. // If it's a toast notification action
  9. if (taskInstance.TriggerDetails is ToastNotificationActionTriggerDetail)
  10. {
  11. // Get the toast activation details
  12. var details = taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;
  13.  
  14. // Deserialize the arguments received from the toast activation
  15. QueryString args = QueryString.Parse(details.Argument);
  16.  
  17. // Depending on what action was taken...
  18. switch (args["action"])
  19. {
  20. // User clicked the reply button (doing a quick reply)
  21. case "reply":
  22. await HandleReply(details, args);
  23. break;
  24.  
  25. // User clicked the like button
  26. case "like":
  27. await HandleLike(details, args);
  28. break;
  29.  
  30. default:
  31. throw new NotImplementedException();
  32. }
  33. }
  34.  
  35. // Otherwise handle other background activations
  36. else
  37. throw new NotImplementedException();
  38. }
  39.  
  40. finally
  41. {
  42. // And finally release the deferral since we're done
  43. deferral.Complete();
  44. }
  45. }

在实现代码中可以再次获取更详细的参数获得消息会话ID以及用户在快速回复对话框中输入的内容从而与服务器进行交互。

  1. private async Task HandleReply(ToastNotificationActionTriggerDetail details, QueryString args)
  2. {
  3. // Get the conversation the toast is about
  4. int conversationId = int.Parse(args["conversationId"]);
  5.  
  6. // Get the message that the user typed in the toast
  7. string message = (string)details.UserInput["tbReply"];
  8.  
  9. // In a real app, this would be making a web request, sending the new message
  10. await Task.Delay(TimeSpan.FromSeconds(2.3));
  11.  
  12. // In a real app, you most likely should NOT notify your user that the request completed (only notify them if there's an error)
  13. SendToast("Your message has been sent! Your message: " + message);
  14. }
  15.  
  16. private async Task HandleLike(ToastNotificationActionTriggerDetail details, QueryString args)
  17. {
  18. // Get the conversation the toast is about
  19. int conversationId = int.Parse(args["conversationId"]);
  20.  
  21. // In a real app, this would be making a web request, sending the like command
  22. await Task.Delay(TimeSpan.FromSeconds(1.1));
  23.  
  24. // In a real app, you most likely should NOT notify your user that the request completed (only notify them if there's an error)
  25. SendToast("Your like has been sent!");
  26. }

当然也不要忘记在应用启动的时候注册这个BackgroundTask,这部分代码可以放在应用 Onlaunched 和 OnActivated事件中。

  1. private void RegisterBackgroundTask()
  2. {
  3. const string taskName = "ToastBackgroundTask";
  4.  
  5. // If background task is already registered, do nothing
  6. if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
  7. return;
  8.  
  9. // Otherwise create the background task
  10. var builder = new BackgroundTaskBuilder()
  11. {
  12. Name = taskName,
  13. TaskEntryPoint = typeof(ToastNotificationBackgroundTask).FullName
  14. };
  15.  
  16. // And set the toast action trigger
  17. builder.SetTrigger(new ToastNotificationActionTrigger());
  18.  
  19. // And register the task
  20. builder.Register();
  21. }

另外刚才除了后台执行代码的两个按钮还有查看消息和查看图片按钮的执行逻辑,这里应该是放在Launched 和 Activated方法中的首先要判断应用的 IActivatedEventArgs类型是否ToastNotificationActivatedEventArgs如果是尝试模仿后台任务中查找按钮参数的方法找到会话ID和照片连接并导航到应用的正确页面去。

  1. private async Task OnLaunchedOrActivated(IActivatedEventArgs e)
  2. {
  3. // Initialize things like registering background task before the app is loaded
  4. await InitializeApp();
  5.  
  6. #if DEBUG
  7. if (System.Diagnostics.Debugger.IsAttached)
  8. {
  9. this.DebugSettings.EnableFrameRateCounter = true;
  10. }
  11. #endif
  12.  
  13. Frame rootFrame = Window.Current.Content as Frame;
  14.  
  15. // Do not repeat app initialization when the Window already has content,
  16. // just ensure that the window is active
  17. if (rootFrame == null)
  18. {
  19. // Create a Frame to act as the navigation context and navigate to the first page
  20. rootFrame = new Frame();
  21.  
  22. rootFrame.NavigationFailed += OnNavigationFailed;
  23.  
  24. if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
  25. {
  26. // TODO: Load state from previously suspended application
  27. }
  28.  
  29. // Place the frame in the current Window
  30. Window.Current.Content = rootFrame;
  31. }
  32.  
  33. // Handle toast activation
  34. if (e is ToastNotificationActivatedEventArgs)
  35. {
  36. var toastActivationArgs = e as ToastNotificationActivatedEventArgs;
  37.  
  38. // If empty args, no specific action (just launch the app)
  39. if (toastActivationArgs.Argument.Length == )
  40. {
  41. if (rootFrame.Content == null)
  42. rootFrame.Navigate(typeof(MainPage));
  43. }
  44.  
  45. // Otherwise an action is provided
  46. else
  47. {
  48. // Parse the query string
  49. QueryString args = QueryString.Parse(toastActivationArgs.Argument);
  50.  
  51. // See what action is being requested
  52. switch (args["action"])
  53. {
  54. // Open the image
  55. case "viewImage":
  56.  
  57. // The URL retrieved from the toast args
  58. string imageUrl = args["imageUrl"];
  59.  
  60. // If we're already viewing that image, do nothing
  61. if (rootFrame.Content is ImagePage && (rootFrame.Content as ImagePage).ImageUrl.Equals(imageUrl))
  62. break;
  63.  
  64. // Otherwise navigate to view it
  65. rootFrame.Navigate(typeof(ImagePage), imageUrl);
  66. break;
  67.  
  68. // Open the conversation
  69. case "viewConversation":
  70.  
  71. // The conversation ID retrieved from the toast args
  72. int conversationId = int.Parse(args["conversationId"]);
  73.  
  74. // If we're already viewing that conversation, do nothing
  75. if (rootFrame.Content is ConversationPage && (rootFrame.Content as ConversationPage).ConversationId == conversationId)
  76. break;
  77.  
  78. // Otherwise navigate to view it
  79. rootFrame.Navigate(typeof(ConversationPage), conversationId);
  80. break;
  81.  
  82. default:
  83. throw new NotImplementedException();
  84. }
  85.  
  86. // If we're loading the app for the first time, place the main page on the back stack
  87. // so that user can go back after they've been navigated to the specific page
  88. if (rootFrame.BackStack.Count == )
  89. rootFrame.BackStack.Add(new PageStackEntry(typeof(MainPage), null, null));
  90. }
  91. }
  92.  
  93. // Handle launch activation
  94. else if (e is LaunchActivatedEventArgs)
  95. {
  96. var launchActivationArgs = e as LaunchActivatedEventArgs;
  97.  
  98. // If launched with arguments (not a normal primary tile/applist launch)
  99. if (launchActivationArgs.Arguments.Length > )
  100. {
  101. // TODO: Handle arguments for cases like launching from secondary Tile, so we navigate to the correct page
  102. throw new NotImplementedException();
  103. }
  104.  
  105. // Otherwise if launched normally
  106. else
  107. {
  108. // If we're currently not on a page, navigate to the main page
  109. if (rootFrame.Content == null)
  110. rootFrame.Navigate(typeof(MainPage));
  111. }
  112. }
  113.  
  114. else
  115. {
  116. // TODO: Handle other types of activation
  117. throw new NotImplementedException();
  118. }
  119.  
  120. // Ensure the current window is active
  121. Window.Current.Activate();
  122. }

好了这样你的应用就可以处理 Windows10 可交互性的自定义消息通知了。

并且为了测试方便你可以在应用中使用 NotificationsExtensions.Win10 这个扩展类库进行测试,在nuget上可以直接下载到。

https://www.nuget.org/packages/NotificationsExtensions.Win10/

测试代码如下:

  1. private void ButtonSendToast_Click(object sender, RoutedEventArgs e)
  2. {
  3. // In a real app, these would be initialized with actual data
  4. string title = "Andrew sent you a picture";
  5. string content = "Check this out, Happy Canyon in Utah!";
  6. 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";
  7. string logo = "ms-appdata:///local/Andrew.jpg";
  8. int conversationId = ;
  9.  
  10. // Construct the visuals of the toast
  11. ToastVisual visual = new ToastVisual()
  12. {
  13. TitleText = new ToastText()
  14. {
  15. Text = title
  16. },
  17.  
  18. BodyTextLine1 = new ToastText()
  19. {
  20. Text = content
  21. },
  22.  
  23. InlineImages =
  24. {
  25. new ToastImage()
  26. {
  27. Source = new ToastImageSource(image)
  28. }
  29. },
  30.  
  31. AppLogoOverride = new ToastAppLogo()
  32. {
  33. Source = new ToastImageSource(logo),
  34. Crop = ToastImageCrop.Circle
  35. }
  36. };
  37.  
  38. // Construct the actions for the toast (inputs and buttons)
  39. ToastActionsCustom actions = new ToastActionsCustom()
  40. {
  41. Inputs =
  42. {
  43. new ToastTextBox("tbReply")
  44. {
  45. PlaceholderContent = "Type a response"
  46. }
  47. },
  48.  
  49. Buttons =
  50. {
  51. new ToastButton("Reply", new QueryString()
  52. {
  53. { "action", "reply" },
  54. { "conversationId", conversationId.ToString() }
  55.  
  56. }.ToString())
  57. {
  58. ActivationType = ToastActivationType.Background,
  59. ImageUri = "Assets/Reply.png",
  60.  
  61. // Reference the text box's ID in order to
  62. // place this button next to the text box
  63. TextBoxId = "tbReply"
  64. },
  65.  
  66. new ToastButton("Like", new QueryString()
  67. {
  68. { "action", "like" },
  69. { "conversationId", conversationId.ToString() }
  70.  
  71. }.ToString())
  72. {
  73. ActivationType = ToastActivationType.Background
  74. },
  75.  
  76. new ToastButton("View", new QueryString()
  77. {
  78. { "action", "viewImage" },
  79. { "imageUrl", image }
  80.  
  81. }.ToString())
  82. }
  83. };
  84.  
  85. // Now we can construct the final toast content
  86. ToastContent toastContent = new ToastContent()
  87. {
  88. Visual = visual,
  89. Actions = actions,
  90.  
  91. // Arguments when the user taps body of toast
  92. Launch = new QueryString()
  93. {
  94. { "action", "viewConversation" },
  95. { "conversationId", conversationId.ToString() }
  96.  
  97. }.ToString()
  98. };
  99.  
  100. // And create the toast notification
  101. ToastNotification notification = new ToastNotification(toastContent.GetXml());
  102.  
  103. // And then send the toast
  104. ToastNotificationManager.CreateToastNotifier().Show(notification);
  105. }

有即时消息需求的应用是非常适合这个功能的,行动起来快试试把这个功能加到应用中吧。

参考资料:

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 自定义交互消息通知的更多相关文章

  1. 自定义WM_NOTIFY消息

    自定义WM_NOTIFY消息 习惯了用自定义用户消息进行各种状态的通知,特别是子窗口与父窗口之间的交互.但ON_MESSAGE没有控件ID的限制,如果有多个子窗口发送同一个消息给父窗口时,父窗口就不知 ...

  2. 一个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 ...

  3. Android中的消息通知(NotificationManager和Notification)

    下面来谈谈notification,这个notification一般用在电话,短 信,邮件,闹钟铃声,在手机的状态栏上就会出现一个小图标,提示用户处理这个通知,这时手从上方滑动状态栏就可以展开并处理这 ...

  4. Android消息通知(notification)和PendingIntent传值

    通知栏的自定义布局:转:http://blog.csdn.net/vipzjyno1/article/details/25248021 拓展 实现自定义的通知栏效果: 这里要用到RemoteViews ...

  5. HTML 5的消息通知机制

    译文来源:http://www.ido321.com/1130.html 原文:HTML 5 Notification 译文:HTML 5 的消息通知机制 译者:dwqs HTML 5 已经被应用到W ...

  6. 微信小程序客服消息开发实战:实时在手机上接收小程序客服消息通知,以及在手机上回复

    在微信小程序开发中,可以非常方便的集成客服功能,只需要一行代码便可以将用户引导至客服会话界面.这行代码就是: <button open-type="contact" bind ...

  7. Redis自学笔记:4.4进阶-消息通知

    4.4消息通知 4.4.1任务队列 传递任务的队列.与任务队列进行交互的实体有两类,一类是生产者,一类是消费者. 生产者将需要处理的任务放入任务队列中,二消费者不断从任务队列中读入任务 信息并执行. ...

  8. [UWP]实现一个轻量级的应用内消息通知控件

    在UWP应用开发中,我们常常有向用户发送一些提示性消息的需求.这种时候我们一般会选择MessageDialog.ContentDialog或者ToastNotification来完成功能. 但是,我们 ...

  9. jenkins 构建后发送钉钉消息通知(插件)

    钉钉,越来越多的公司采用,那么我们在持续集成中,也可以直接选择钉钉插件的,在之前的博客中 ,对发送的钉钉消息进行了定制,那样的话会开启一个新的任务, 其实今天呢,我们可以直接安装一个插件就可以发送了, ...

随机推荐

  1. 【源码笔记】BlogEngine.Net 中的权限管理

    BlogEngine.Net 是个功能点很全面的开源博客系统,容易安装和实现定制,开放接口支持TrackBack,可以定义主题配置数据源等等.可谓五脏俱全,这里先记录一下它基于Membership的权 ...

  2. Windows下使用Redis(一)安装使用

    一.Redis 是什么 Redis 是一款依据BSD开源协议发行的高性能Key-Value存储系统(cache and store).它通常被称为数据结构服务器,因为值(value)可以是 字符串(S ...

  3. jQuery document window load ready 区别详解

    用过JavaScript的童鞋,应该知道window对象和document对象,也应该听说过load事件和ready事件,小菜当然也知道,而且自认为很了解,直到最近出了问题,才知道事情并不是那么简单. ...

  4. js常规日期格式处理、月历渲染、倒计时函数

    日期格式处理在前端的日常任务中非常常见,但是为此引入monent.js这样的类库又会显得有点臃肿,毕竟我们一个特定的项目中,并不需要monent.js那么全的涵盖范围.另外,如果现在公司让你自己手写一 ...

  5. 1121 if条件语句练习--输入年月日判断执行

    <script type="text/javascript"> var a=prompt("请输入一个年","请输入年份"); ...

  6. Java band [Cite]

    SampleModel  取样模型Databuffer 数据缓冲区 Raster 光栅Sample 样本band  带 SampleModel是java awt中的一个抽象类,它定义了一个接口,用于提 ...

  7. JQ属性和css部分测试

    1.attr(name|properties|key,value|fn)  设置或返回被选元素的属性值. <div class="attr">设置或返回被选元素的属性值 ...

  8. no sigar-amd64-winnt.dll in java.library.path 错误

    需要维护别人写的一个WEB项目,还原数据库,从SVN中检出源码,运行,提示如下错误: 5526 [localhost-startStop-1] DEBUG Sigar  - no sigar-amd6 ...

  9. mysql忘记密码重置(mac)

    setp1: 苹果->系统偏好设置->最下边点mysql 在弹出页面中 关闭mysql服务(点击stop mysql server) step2:进入终端输入:cd /usr/local/ ...

  10. Android图片处理-图片压缩处理

    这里先重复温习一下上一篇,调用相册获取图片: /*** * 这个是调用android内置的intent,来过滤图片文件 ,同时也可以过滤其他的 */ Intent intent = new Inten ...