Windows 10のインタラクティブなトースト通知はデスクトップアプリからも出せます。その前提として、
アクションセンター用のINotificationActivationCallbackを実装する 必要があります。なお、別にトースト通知がアクションセンターに入っている状態に限らないので、正確な表現ではありませんが。
1. インタラクティブなトーストのXMLを生成する
トーストはXMLで生成する必要がありますが、Windows 10ではWindows 8から形式が変わっています。といっても、Windows 8式でもそのまま出せるので、インタラクティブな機能が必要ならこちらを使うということです。説明は以下を参照。
このXMLは一から組み立ててもいいですが、MicrosoftがNotificationsExtensionsというライブラリを出していて(デスクトップアプリから使用可)、これでWindows 10式のXMLを生成できます。
これを使った例としては以下のようなもの。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// using System.IO;
// using NotificationsExtensions;
// using NotificationsExtensions.Toasts;
private const string MessageId = "Message";
private string ComposeInteractiveToast()
{
var toastVisual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText { Text = "DesktopToast WPF Sample" }, // Title
new AdaptiveText { Text = "This is an interactive toast test." }, // Body
},
AppLogoOverride = new ToastGenericAppLogo
{
Source = string.Format("file:///{0}", Path.GetFullPath("Resources/toast128.png")),
AlternateText = "Logo"
}
}
};
var toastAction = new ToastActionsCustom
{
Inputs =
{
new ToastTextBox(id: MessageId) { PlaceholderContent = "Input a message" }
},
Buttons =
{
new ToastButton(content: "Reply", arguments: "action=Replied") { ActivationType = ToastActivationType.Background },
new ToastButton(content: "Ignore", arguments: "action=Ignored")
}
};
var toastContent = new ToastContent
{
Visual = toastVisual,
Actions = toastAction,
Duration = ToastDuration.Long,
Audio = new NotificationsExtensions.Toasts.ToastAudio
{
Loop = true,
Src = new Uri("ms-winsoundevent:Notification.Looping.Alarm4")
}
};
return toastContent.GetContent();
}
この中でインタラクティブな機能はToastActionCustomの部分にあります。
InputsのToastTextBoxでテキストボックスを指定し、そのコンストラクタ―でidとして埋め込まれる文字列を決めています。このidがユーザーからの反応を受け取るときに意味を持ちます。
ButtonsのToastButtonでボタンを指定し、そのコンストラクタ―のcontentでボタンに表示される文字列を与え、同時にargumentsとして埋め込まれる文字列を決めています。
これで生成されたXMLが以下。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?>
<toast duration="long">
<visual>
<binding template="ToastGeneric">
<text>DesktopToast WPF Sample</text>
<text>This is an interactive toast test.</text>
<image src="file:///[executive folder path]\Resources\toast128.png" alt="Logo" placement="appLogoOverride" />
</binding>
</visual>
<audio src="ms-winsoundevent:Notification.Looping.Alarm4" loop="true" />
<actions>
<input id="Message" type="text" placeHolderContent="Input a message" />
<action content="Reply" arguments="action=Replied" activationType="background" />
<action content="Ignore" arguments="action=Ignored" />
</actions>
</toast>
先頭のヘッダーはなくても構いません。actionsの部分を見ると、inputが上のInputsに、actionが上のButtonsに対応しています。
これぐらいならたいした長さのXMLでもないので、アプリ内に文字列として持っておいて、変わる部分を実行時に嵌め込む方が早いかもしれません。
これを使ってトーストを出すとこうなります。
2. ユーザーの反応を受け取る
このトーストにユーザーが反応するとINotificationActivationCallbackの
Activate メソッドが実行されます。この引数から型変換を経て得られる情報は以下のとおり。
appUserModelId: AppUserModelID
invokedArgs: ユーザーが反応したaction(この場合はボタン)に埋め込まれたargumentsの文字列
data: 各input(この場合はテキストボックス)に埋め込まれたidをキーの文字列、inputに入力された内容を値の文字列とした構造体の配列
count: dataの配列の長さ
したがって、invokedArgsを見ることでどのボタンが押されたか、dataを見ることでどのテキストボックスに何が入力されていたか判別できるわけです。
なお、トーストの下半分のactions部分は地の部分を押しても反応しませんが、上半分のvisual部分は従来のトーストと同様に地の部分を押しただけでも反応し、その場合はinvokedArgsには何も返ってきません。また、この例でIgnoreとしているボタンはactivationTypeに何も指定していないので、押しても何も起こらず、トーストが消えるだけです。
具体例はレポジトリのサンプル(WPF)を見てください。
3. まとめ
以上のようにインタラクティブなトースト自体はさほど難しくはないですが、実際にどう利用するかというと、
トーストはユーザーが出さないよう設定できるので、ユーザーからの反応のルートをこれだけに頼れない。
トーストからできることと、アプリ本体からできることをよく整理しないと、ユーザーに余分な学習コストがかかる。複雑なことはアプリ本体を呼び出してから行った方が、たぶんコストがかからない。
という問題があるので、あまり凝らずにシンプルな方がいいのかなと思います。所詮さっと通り過ぎていくのが本来の通知ですし。