Shell - DrumMidiEditor/DrumMidiEditorApp GitHub Wiki

https://docs.microsoft.com/ja-jp/windows/apps/design/shell/

ツール

Notifications Visualizer
https://docs.microsoft.com/ja-jp/windows/apps/design/shell/tiles-and-notifications/notifications-visualizer

タイルとトースト通知の視覚的なプレビューが行える  

通知・配信方法

https://docs.microsoft.com/ja-jp/windows/apps/design/shell/tiles-and-notifications/choosing-a-notification-delivery-method

ローカル <タイル、バッジ、トースト>
 アプリが実行されている間、タイルやバッジを直接更新している間
 またはトースト通知を送信している間に通知を送信する API 呼び出しのセットです。

 * 音楽アプリでは、タイルを更新して "再生中" の音楽を表示
 * ゲーム アプリでは、ユーザーがゲームから離れるとタイルを更新してユーザーのハイ スコアを表示
 * グリフでアプリに新しい情報があることが示されたバッジは、アプリがアクティブ化されるとクリア

スケジュール <タイル、トースト>
 指定した時間に更新が行われるように事前に通知をスケジュールする API 呼び出しのセットです。

 * カレンダー アプリでは、予定されている会議用のトースト通知のアラームを設定

定期的 <タイル、バッジ>
 クラウド サービスをポーリングして新しいコンテンツの有無を調べて
 タイルとバッジを一定の間隔で定期的に更新する通知です。

 * 天気予報アプリでは、予報を表示するタイルを 30 分間隔で更新
 * "日替わりセール情報" サイトでは、本日のお買い得品を毎朝更新
 * イベントまでの日数を表示するタイルでは、表示される日数のカウントダウンを毎日深夜 0 時に更新

プッシュ <タイル、バッジ、トースト、直接>
 アプリが実行されていなくてもクラウド サーバーから送信される通知です。

 * ショッピング アプリでは、トースト通知を送信して、ユーザーが注目している商品のセール情報
 * ニュース アプリでは、ニュース速報が発生したときにタイルを更新
 * スポーツ アプリでは、試合の進行中にタイルを更新
 * 通信アプリでは、メッセージや電話の着信をアラートで知らせる

トースト通知

image

トースト通知は、ユーザーの画面の右下と通知センター (Windows 10のアクション センターと呼ばれます) に表示されるWindows通知

パッケージの追加が必要
image

アクティブ化対応

トースト通知クリック時に、アプリがアクティブ化されるが
その際に、下記設定をしていないとエラーになる。

  • Visual Studio の [ツール]-[GUID の作成]からGUIDを取得
     image

 <Guid("013F361C-A05E-4425-9E0F-F9D7EA076E56")>

  • Package.appxmanifest を編集
<Package
  ...
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
  xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
  IgnorableNamespaces="... com desktop">
  ...
  <Applications>
    <Application>
      ...
      <Extensions>
        <!--Specify which CLSID to activate when notification is clicked-->
        <desktop:Extension Category="windows.toastNotificationActivation">
            <desktop:ToastNotificationActivation ToastActivatorCLSID="013F361C-A05E-4425-9E0F-F9D7EA076E56" />
        </desktop:Extension>

        <!--Register COM CLSID-->
        <com:Extension Category="windows.comServer">
            <com:ComServer>
                <com:ExeServer Executable="DrumMidiEditorApp\DrumMidiEditorApp.exe" DisplayName="DrumMidiEditorApp" Arguments="----AppNotificationActivated:">
                    <com:Class Id="013F361C-A05E-4425-9E0F-F9D7EA076E56" />
                </com:ExeServer>
            </com:ComServer>
        </com:Extension>
      </Extensions>

    </Application>
  </Applications>
 </Package>

ローカルトースト通知

トースト通知のパターン
https://docs.microsoft.com/ja-jp/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=builder-syntax

トースト構成&通知

image image
// トーストコンテンツを構成後、通知
new ToastContentBuilder()
    // ---------------------------------------------------------
    // Launch:ユーザーがトーストをクリックしたときにアプリに渡される引数を定義
    // ---------------------------------------------------------
    .AddArgument("action", "viewConversation")
    .AddArgument("conversationId", 9813)

    // ---------------------------------------------------------
    // Visual:トーストの視覚的な部分
    // ---------------------------------------------------------

    // ヘッダー:通知ごとに同じ ID を指定すると通知内でグループ化される
    .AddHeader( "6289", "HeaderTitle", "action=openConversation&id=6289")

    // テキスト要素は3つまで追加可能。1行目がメインで、2,3行目がサブ情報
    .AddText( "トースト テスト通知", hintMaxLines: 2 )
    .AddText( "説明要素:歩きスマホは" )
    .AddText( "説明要素:止めましょう"  )

    // インライン画像
    .AddInlineImage( new Uri( "https://avatars.githubusercontent.com/u/97685486?v=4" ) )

    // アプリ ロゴの上書き:Windows 11では無効?(アプリロゴは Attribute 領域に表示)
    //.AddAppLogoOverride(new Uri("ms-appdata:///local/Andrew.jpg"), ToastGenericAppLogoCrop.Circle)

    // ヒーロー イメージ:Attribute領域の上にイメージを表示
    .AddHeroImage( new Uri( "https://user-images.githubusercontent.com/97685486/182838106-50765a8a-814a-42c6-9714-23a23284b593.png" ) )

    /**
     * URI指定パターン
     *   - http://          http/https リモート Web画像 ファイルサイズの制限あり
     *   - ms-appx:///      
     *   - ms-appdata:///   
     */

    // カスタム タイムスタンプ:通知時刻をアプリ側から指定可能
    .AddCustomTimeStamp( new DateTime( 2017, 04, 15, 19, 45, 00, DateTimeKind.Utc ) )

    // 進捗状況バー
    .AddVisualChild
        (
            new AdaptiveProgressBar()
            {
                Title               = "進捗バー",
                Value               = new BindableProgressBarValue( "progressValue" ),
                ValueStringOverride = new BindableString( "progressValueString" ),
                Status              = new BindableString( "progressStatus" )
            }
        )

    // ---------------------------------------------------------
    // Action:トーストの対話的な部分 (入力やアクションなど) 
    // ---------------------------------------------------------

    // テキスト入力
    .AddInputTextBox( "tbReply", placeHolderContent: "Type a response" )

    // 選択入力
    .AddToastInput
        (
            new ToastSelectionBox( "time" )
            {
                DefaultSelectionBoxItemId = "lunch",
                Items =
                {
                    new ToastSelectionBoxItem( "breakfast"  , "Breakfast"   ),
                    new ToastSelectionBoxItem( "lunch"      , "Lunch"       ),
                    new ToastSelectionBoxItem( "dinner"     , "Dinner"      ),
                }
            }
        )

    // 再通知時間入力
    .AddToastInput
        (
            new ToastSelectionBox( "snoozeTime" )
            {
                DefaultSelectionBoxItemId = "15",
                Items =
                {
                    new( "5"      , "5 minutes"     ),
                    new( "15"     , "15 minutes"    ),
                    new( "60"     , "1 hour"        ),
                    new( "240"    , "4 hours"       ),
                    new( "1440"   , "1 day"         )
                }
            })

    // ボタン
    .AddButton
        (
            new ToastButton()
                .SetContent("Reply")
                .SetTextBoxId( "tbReply" )
                .AddArgument("action", "reply")
                .SetBackgroundActivation()
        )
    .AddButton
        (
            new ToastButton()
                .SetContent("Like")
                .AddArgument("action", "like")
                .SetBackgroundActivation()
        )
    //.AddButton
    //    ( 
    //        new ToastButton()
    //            .SetContent("View")
    //            .AddArgument("action", "viewImage")
    //            .AddArgument("imageUrl", image.ToString() ) 
    //    )

    // [一時停止(再通知)]ボタン
    .AddButton
        (
            new ToastButtonSnooze() 
            { 
                SelectionBoxId = "snoozeTime",
            }
        )

    // [無視]ボタン
    .AddButton( new ToastButtonDismiss() )

    // シナリオ
    .SetToastScenario( ToastScenario.Reminder )         // リマインダー
    //.SetToastScenario( ToastScenario.Alarm )          // アラーム:トースト通知に少なくとも 1 つのボタンを指定する必要あり
    //.SetToastScenario( ToastScenario.IncomingCall )   // 着信呼び出し
    //.SetToastScenario( ToastScenario.******** )       // 重要な通知:未実装

    // ---------------------------------------------------------
    // Audio:トーストがユーザーに表示されるときに再生されるオーディオ
    //
    // [既定のサウンド一覧]
    // https://docs.microsoft.com/ja-jp/uwp/schemas/tiles/toastschema/element-audio#attributes-and-elements
    // ---------------------------------------------------------
    .AddAudio( new Uri( "ms-appx:///Sound.mp3" ) )

    // ---------------------------------------------------------
    // Show
    // ---------------------------------------------------------
    .Show
    (
        ( toast ) =>
        {
            toast.Tag               = "TagName";
            toast.Group             = "GroupName";
            toast.ExpirationTime    = DateTime.Now.AddMinutes( 3 );     // 有効期限

            toast.Data = new NotificationData();
            toast.Data.Values[ "progressValue" ]            = "0.6";
            toast.Data.Values[ "progressValueString" ]      = "15/26 songs";
            toast.Data.Values[ "progressStatus" ]           = "Downloading...";
            toast.Data.SequenceNumber                       = 0;      // 0(常に更新する)、1-(シーケンス位置指定)
        }
    ); 

トースト通知の更新

進捗バーの更新

var data = new NotificationData
{
    SequenceNumber = 0,      // 0(常に更新する)
};

data.Values[ "progressValue" ]          = "1.0";
data.Values[ "progressValueString" ]    = "26/26 songs";
data.Values[ "progressStatus" ]         = "Finished";

ToastNotificationManager.CreateToastNotifier().Update( data, "TagName", "GroupName" );

トースト通知の削除

// 全てのトースト履歴をクリア
ToastNotificationManagerCompat.History.Clear();

// 対象グループのトースト履歴を削除
ToastNotificationManagerCompat.History.RemoveGroup( "GroupName" );

// 対象タグのトースト履歴を削除
ToastNotificationManagerCompat.History.Remove( "TagName" );

// 対象タグ・グループに一致するトースト履歴を削除
ToastNotificationManagerCompat.History.Remove( "TagName", "GroupName" );

スケジュール

// スケジュール通知
new ToastContentBuilder()
    .AddText( "5秒後に通知" )
    .Schedule
        ( 
            DateTime.Now.AddSeconds( 5 ),
            toast =>
            {
                toast.Tag   = "18365";
                toast.Group = "ASTR 170B1";
            }
        );

// スケジュールの削除
var notifier = ToastNotificationManagerCompat.CreateToastNotifier();

var scheduledToasts = notifier.GetScheduledToastNotifications();

var toRemove = scheduledToasts
    .FirstOrDefault( toast => toast.Tag == "18365" && toast.Group == "ASTR 170B1" );

if ( toRemove != null )
{
    notifier.RemoveFromSchedule( toRemove );
}

ヘッダー

image

同じヘッダーIDを使用して、トースト通知

// ヘッダーテスト
new ToastContentBuilder()
    // ---------------------------------------------------------
    // Visual:トーストの視覚的な部分
    // ---------------------------------------------------------

    // ヘッダー
    .AddHeader( "6289", "HeaderTitle", "action=openConversation&id=6289")

    // テキスト要素は3つまで追加可能。1行目がメインで、2,3行目がサブ情報
    .AddText( "222" )

    // ---------------------------------------------------------
    // Show
    // ---------------------------------------------------------
    .Show
    (
        ( toast ) =>
        {
            toast.Tag               = "TagName2";
            toast.Group             = "GroupName";
            toast.ExpirationTime    = DateTime.Now.AddMinutes( 3 );     // 有効期限
        }
    ); 

  • アダプティブ コンテンツ
  • コンテキスト メニューのアクション
  • アクティブ化の設定が必要

バッジ通知

image

アプリ特有の概要や状態情報を伝達する

  • ネットワーク接続状態
  • ユーザーの状態
  • 未読メールの数
  • ソーシャル メディア アプリでの新しい投稿数
    など

通知バッジは、アプリが実行されているかどうかに関係なく
アプリのタスク バーのアイコンとスタート タイルの右下隅に表示される

数値バッジ

image
var badgeXml = BadgeUpdateManager.GetTemplateContent( BadgeTemplateType.BadgeNumber );

// 数値を設定
//  1 ~ 99 の数字。 値 0 はグリフ値 "none" と同じであり、バッジをクリア
//  99 を超える数字は、"99+" 表示
var badgeElement = badgeXml.SelectSingleNode( "/badge" ) as XmlElement;
badgeElement?.SetAttribute( "value", "99" );

// バッジ更新
BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update( new( badgeXml ) );

グリフバッジ(アイコンで状態を表す)

image
var badgeXml = BadgeUpdateManager.GetTemplateContent( BadgeTemplateType.BadgeGlyph );

var badgeElement = badgeXml.SelectSingleNode( "/badge" ) as XmlElement;
badgeElement?.SetAttribute( "value", "alert" );

// -----------------------------------------------
// [グリフ設定値]
// -----------------------------------------------
// none        : なし
// activity    : activity
// alarm       : alarm(アラーム)
// alert       : アラート
// attention   : attention(注意)
// available   : 利用可能
// away        : away(離席中)
// busy        : busy(取り込み中)
// error       : error
// newMessage  : newMessage(新しいメッセージ)
// paused      : paused(一時停止)
// playing     : playing(再生)
// unavailable : unavailable(利用不可)
// -----------------------------------------------

// バッジ更新
BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update( new( badgeXml ) );

バッジクリア

// バッジをクリア
BadgeUpdateManager.CreateBadgeUpdaterForApplication().Clear();

プッシュ通知

Windows プッシュ通知サービス (WNS) を利用することで
独自のクラウド サービスからトースト更新、タイル更新、バッジ更新、直接更新を送ることができる

サービスの登録が必要で、試すのはめんどう。
https://docs.microsoft.com/ja-JP/azure/notification-hubs/notification-hubs-windows-store-dotnet-get-started-wns-push-notification

タスク バー

アプリのタスク バーへのピン留めをアプリから促すことができる

// APIサポートチェック
if ( !ApiInformation.IsTypePresent( "Windows.UI.Shell.TaskbarManager" ) )
{
    return;
}

// タスク バーが存在し、ピン留めを使用できるかどうかを確認
if ( !TaskbarManager.GetDefault().IsPinningAllowed )
{
    return;
}

// アプリが現在タスク バーにピン留めされているかどうかを確認
if ( await TaskbarManager.GetDefault().IsCurrentAppPinnedAsync() )
{
    return;
}

// ピン止め確認:
if ( await TaskbarManager.GetDefault().RequestPinCurrentAppAsync() )
{

}

RequestPinCurrentAppAsync でエラーとなる。
(インターフェイスが登録されていません)

タイトル バー

タイトルバーを自分でカスタマイズできるよって話かね

ライブタイル

タイルとは、[スタート] メニュー上でアプリを表すもの
image
image

Package.appxmanifestで設定
image

ローカル タイル通知

URI をポーリングして、タイルの状態を更新したりもできるっぽい。

https://docs.microsoft.com/ja-jp/windows/apps/design/shell/tiles-and-notifications/sending-a-local-tile-notification

⚠️ **GitHub.com Fallback** ⚠️