連載
» 2013年02月14日 05時00分 UPDATE

WinRT/Metro TIPS:タイルに現在時刻を表示するには?[Win 8]

ライブ・タイルはリアルタイムに変更できないが、「スケジュールされたタイル通知」を使えば分単位で表示を更新できる。時刻表示の例でその実装方法を説明する。

[山本康彦,BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 ライブ・タイルはリアルタイムに変更できない。ライブ・タイルが見える状態、すなわちスタート画面を表示しているときには、ライブ・タイルを変更しようとするアプリはバックグラウンドでサスペンドされているので、ライブ・タイルを更新できないのだ。

 しかし、真のリアルタイムは無理だとしても、せめて時刻を分単位くらいの精度で表示できないだろうか? 「スケジュールされたタイル通知」を利用すればそれが可能になる。本稿では、それを利用してタイルに現在時刻を表示する方法を説明する。本稿のサンプルは「Windows Store app samples:MetroTips #24」からダウンロードできる。

事前準備

 Windows 8(以降、Win8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。

スケジュールされたタイル通知とは?

 前々回で利用したTileNotificationクラス(Windows.UI.Notifications名前空間)は静的なタイル通知であり、通知キューにセットすると直ちに表示可能になり、通知キューから削除されるまで表示され続けるものだった。

 それとは別に、表示される時刻を指定するタイル通知がある。「スケジュールされたタイル通知」と呼ばれるもので、ScheduledTileNotificationクラス(Windows.UI.Notifications名前空間)を使用する。これを通知キューに登録すると、指定した時刻にタイルを変更できるのだ。

 また、通知キューに登録できるタイル通知の数は前々回では5個までと説明したが、「スケジュールされたタイル通知」はそれとは別になっており、4096個まで登録できる*1。1分ごとにタイルを切り替えるならば、68時間以上にわたって異なるタイルを登録できるのだ*2

*1 「MSDN:TileUpdater.AddToSchedule」の注釈を参照。


*2 本稿では説明しないが、時間切れになる前にバックグラウンド・タスクを利用して続きのタイル通知を登録すれば、時刻を表示し続けるタイルが出来上がる。


スケジュールされたタイル通知を登録するには?

 開始時刻を指定してScheduledTileNotificationクラスのオブジェクトを作り、TileUpdaterクラス(Windows.UI.Notifications名前空間)のAddToScheduleメソッドを使って通知キューに登録すればよい。オプションとして、終了時刻と通知IDも設定できる。

 まず、前々回に説明したようにして通知キューを有効化しておく。そうしたら、次のコードのようにして「スケジュールされたタイル通知」を作って登録する。この例は、現在時刻の次に来る1分の間だけ、タイルに時刻を表示する。なお、ここでも前々回で紹介したNotificationsExtensionsライブラリを使用している。

// 現在時刻(分単位)
DateTimeOffset nowExact = DateTimeOffset.Now;
DateTimeOffset now
  = nowExact.AddSeconds(-nowExact.Second)
            .AddMilliseconds(-nowExact.Millisecond);
if (now.Second >= 59)  //処理中に次の分に入ると例外が出る可能性があるので、
  now.AddMinutes(1.0); //1分先送りする。

// 開始/終了時刻と表示文字列
DateTimeOffset deliveryTime = now.AddMinutes(1.0);  //開始時刻
DateTimeOffset expirationTime = deliveryTime.AddMinutes(1.0); //終了時刻
string displayLocal = deliveryTime.ToString("HH:mm");  //タイルに表示する文字列

// タイルのXMLを作成(NotificationsExtensionsを利用)
var squareTile
  = NotificationsExtensions.TileContent.TileContentFactory
    .CreateTileSquareText02();
squareTile.TextHeading.Text = displayLocal;
squareTile.TextBodyWrap.Text = "Local Time";
squareTile.Branding
  = NotificationsExtensions.TileContent.TileBranding.Name;

// タイル通知を作成
var tileNotification
  = new Windows.UI.Notifications
          .ScheduledTileNotification(squareTile.GetXml(), deliveryTime)
          {
            Id = displayLocal,
            ExpirationTime = expirationTime,
          };

// タイル通知を登録
var tileUpdater
  = Windows.UI.Notifications.TileUpdateManager
      .CreateTileUpdaterForApplication();
tileUpdater.AddToSchedule(tileNotification);

' 現在時刻(分単位)
Dim nowExact As DateTimeOffset = DateTimeOffset.Now
Dim now As DateTimeOffset _
  = nowExact.AddSeconds(-nowExact.Second) _
            .AddMilliseconds(-nowExact.Millisecond)
If (now.Second >= 59) Then  '処理中に次の分に入ると例外が出る可能性があるので、
  now.AddMinutes(1.0)       '1分先送りする。
End If

' 開始/終了時刻と表示文字列
Dim deliveryTime As DateTimeOffset = now.AddMinutes(1.0) '開始時刻
Dim expirationTime As DateTimeOffset = deliveryTime.AddMinutes(1.0) '終了時刻
Dim displayLocal = deliveryTime.ToString("HH:mm") 'タイルに表示する文字列

' タイルのXMLを作成(NotificationsExtensionsを利用)
Dim squareTile _
  = NotificationsExtensions.TileContent.TileContentFactory _
    .CreateTileSquareText02()
squareTile.TextHeading.Text = displayLocal
squareTile.TextBodyWrap.Text = "Local Time"
squareTile.Branding() _
  = NotificationsExtensions.TileContent.TileBranding.Name

' タイル通知を作成
Dim tileNotification _
  = New Windows.UI.Notifications _
    .ScheduledTileNotification(squareTile.GetXml(), deliveryTime)
With tileNotification
  .Id = displayLocal
  .ExpirationTime = expirationTime
End With

' タイル通知を登録
Dim tileUpdater _
  = Windows.UI.Notifications.TileUpdateManager _
      .CreateTileUpdaterForApplication()
tileUpdater.AddToSchedule(tileNotification)

「スケジュールされたタイル通知」を作って登録するコード(上:C#、下:VB)

 なお、時刻を表示させ続けるには、開始時刻/終了時刻を1分ずつずらして上のコードをループさせて、例えば120回(2時間)の通知を一度に登録する。その後、バックグラウンド・タスクで60分おきくらいに追加登録するとよい。本稿で示したコードは、そのままバックグラウンド・タスクでも動作する。ただし、バックグラウンド・タスクは予定していた時刻に正確に始まるとは限らないので、余裕を見ておくことが必要だ。また、追加登録の際には通知キューの中を調べて、重複して登録してしまわないようにする(後述)。

スケジュールされたタイル通知をセカンダリ・タイルに登録するには?

 セカンダリ・タイル用のTileUpdaterオブジェクトを使って通知キューに登録すればよい。それ以外は同じである。

 前回で説明したようにしてセカンダリ・タイルがすでに表示されているとする。そのタイルIDは“UtcTile”だとしよう。この場合、TileUpdaterオブジェクトは次のコードのようにして取得する。該当するセカンダリ・タイルがないときには例外が出るので、注意してほしい。

var tileUpdater2nd = Windows.UI.Notifications.TileUpdateManager
                     .CreateTileUpdaterForSecondaryTile("UtcTile");

Dim tileUpdater2nd = Windows.UI.Notifications.TileUpdateManager _
                     .CreateTileUpdaterForSecondaryTile("UtcTile")

セカンダリ・タイル用のTileUpdaterオブジェクトを取得するコード(上:C#、下:VB)

通知キューの中を調べるには?

 TileUpdaterクラスのGetScheduledTileNotificationsメソッドで、通知キューに入っている通知のリストが取得できる。特定の通知ID(=targetId)を持っている通知が入っているかどうかを判定するには、次のようにする。

var tileUpdater
  = Windows.UI.Notifications.TileUpdateManager
      .CreateTileUpdaterForApplication();

// 通知キューに入っている通知のリストを取得する
var notifyList = tileUpdater.GetScheduledTileNotifications();

// 特定の通知ID(=targetId)を持っている通知があるかどうか判定する
if (notifyList.Where(notify => notify.Id == targetId).Count() > 0)
{
  // 該当する通知がすでに登録されているので、
  // 重複して登録しないようにする
}

Dim tileUpdater _
  = Windows.UI.Notifications.TileUpdateManager _
      .CreateTileUpdaterForApplication()

' 通知キューに入っている通知のリストを取得する
Dim notifyList = tileUpdater.GetScheduledTileNotifications()

' 特定の通知ID(=targetId)を持っている通知があるかどうか判定する
If (notifyList.Where(Function(notify) notify.Id = targetId).Count() > 0) Then
  ' 該当する通知がすでに登録されているので、
  ' 重複して登録しないようにする
End If

通知キューに登録されている「スケジュールされたタイル通知」のリストに、特定の通知が入っているかどうかを判定するコード(上:C#、下:VB)

 なお、メインのタイルおよびセカンダリ・タイルのそれぞれは、個別に通知キューを持っているので、調べるキューを間違えないように注意してほしい。

実行結果

 以上のコードを応用して、メインのタイルにはローカル時刻を、セカンダリ・タイルにはUTC(協定世界時)を表示してみた。分の桁は、完全にぴったりとはいかないものの数秒と違えることなく変わっていく。

ap-winrttips_0024_01.gif 実行結果(スタート画面の一部)
左は、既定のタイル。ローカル時刻が表示されている。
右は、セカンダリ・タイル。UTCが表示されている。

まとめ

 「スケジュールされたタイル通知」を利用して、スタート画面のタイルに現在時刻を表示することができた。あとは、バックグラウンド・タスクで定期的に通知を追加してやれば、ずっと動き続ける時計が出来上がるだろう。

 なお、筆者が実験したところでは、1秒単位の通知ではタイルが更新されなかった(既定のタイルのままとなった)。その原因は仕様によるものか、ハードウェアの性能によるものかは分からない。また、千個を超すような大量の通知を登録するにはかなりの時間が掛かった。バックグラウンド・タスクで処理するならば百個程度にとどめておいた方がよいだろう。

 詳しくは、次のドキュメントを参考にしてほしい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。