SlideShare a Scribd company logo
1
かずきの UWP 入門
2
目次
1. 本書について........................................................................................................................ 10
2. Universal Windows Platform とは ....................................................................................... 10
2.1. UWP アプリまでの歴史............................................................................................... 11
2.1.1. .NET Framework 3.0 ............................................................................................ 11
2.1.2. Silverlight.............................................................................................................. 11
2.1.3. Windows Phone 7.x .............................................................................................. 11
2.1.4. Windows 8 ............................................................................................................ 12
2.1.5. Universal app........................................................................................................ 12
2.1.6. Universal Windows Platform ................................................................................ 12
2.2. 開発の前に ................................................................................................................... 12
2.3. デバイスファミリ ........................................................................................................ 13
2.4. アダプティブ UI........................................................................................................... 14
2.5. Hello world................................................................................................................... 14
2.5.1. 前準備................................................................................................................... 14
2.5.2. プロジェクトの作成 ............................................................................................. 15
2.5.3. 画面の作成............................................................................................................ 17
2.5.4. プログラムの実行................................................................................................. 21
2.5.5. まとめ................................................................................................................... 25
3. XAML................................................................................................................................... 25
3
4. UWP のコントロールの基本クラス..................................................................................... 30
4.1. DependencyObject....................................................................................................... 30
4.1.1. 依存関係プロパティ ............................................................................................. 31
4.1.2. 添付プロパティ..................................................................................................... 35
4.1.3. スレッド操作 ........................................................................................................ 36
4.2. FrameworkElement ...................................................................................................... 37
4.3. Control ......................................................................................................................... 39
4.4. ContentControl ............................................................................................................ 39
4.5. ItemsControl ................................................................................................................ 39
4.6. Panel ............................................................................................................................ 40
5. データバインディング ......................................................................................................... 41
5.1. 実行時データバインディング ...................................................................................... 41
5.2. コンパイル時データバインディング............................................................................ 48
6. アプリケーションライフサイクル ....................................................................................... 53
6.1. アプリケーションの起動シーケンス............................................................................ 53
6.2. サスペンド時の処理..................................................................................................... 55
6.3. サスペンドからの復帰 ................................................................................................. 56
6.4. アプリケーションライフサイクルに対応したサンプル............................................... 56
7. ローカルデータとローミングデータ ................................................................................... 61
7.1. ローカルアプリデータ ................................................................................................. 62
4
7.2. ローミングデータ ........................................................................................................ 65
7.3. 一時アプリデータ ........................................................................................................ 66
8. Advanced XAML .................................................................................................................. 66
8.1. Style.............................................................................................................................. 66
8.1.1. 定義済みのスタイル ............................................................................................. 69
8.2. アニメーション ............................................................................................................ 70
8.2.1. キーフレームを使ったアニメーション ................................................................ 75
8.2.2. ThemeAnimation.................................................................................................. 76
8.2.3. ThemaTransition.................................................................................................. 79
8.3. Visual State Manager.................................................................................................... 82
8.4. Behavior ....................................................................................................................... 90
8.4.1. インストール ........................................................................................................ 90
8.4.2. 組み込み Behavior ................................................................................................ 91
8.4.3. Behavior の使い方 ................................................................................................ 92
8.4.4. Behavior の作成.................................................................................................... 95
8.4.5. Behavior の細かい使い方 ................................................................................... 103
8.5. DataTemplate ............................................................................................................ 103
8.5.1. ContentControl 系での使用................................................................................ 103
8.5.2. ItemsControl 系での使用.................................................................................... 110
8.6. ControlTemplate........................................................................................................ 114
5
8.6.1. クリック可能なテキストの作成 ......................................................................... 114
8.6.2. コントロールの Visual State............................................................................... 116
9. 代表的なコントロール ....................................................................................................... 120
9.1. レイアウトパネル ...................................................................................................... 120
9.1.1. StackPanel .......................................................................................................... 120
9.1.2. Grid .................................................................................................................... 122
9.1.3. Canvas ................................................................................................................ 126
9.1.4. RelativePanel ...................................................................................................... 128
9.2. Border ........................................................................................................................ 131
9.3. TextBlock ................................................................................................................... 133
9.4. Button ........................................................................................................................ 135
9.5. TextBox...................................................................................................................... 138
9.6. CheckBox ................................................................................................................... 140
9.7. RadioButton ............................................................................................................... 141
9.8. RepeatButton ............................................................................................................. 145
9.9. ToggleSwitch.............................................................................................................. 147
9.10. ToggleButton ......................................................................................................... 149
9.11. ComboBox.............................................................................................................. 150
9.12. CalendarDatePicker ............................................................................................... 153
9.13. CalendarView ......................................................................................................... 156
6
9.14. DatePicker.............................................................................................................. 158
9.15. TimePicker............................................................................................................. 160
9.16. FlipView ................................................................................................................. 162
9.17. Frame ..................................................................................................................... 168
9.18. CommandBar ......................................................................................................... 170
9.19. ListView と GridView............................................................................................. 175
9.19.1. 選択項目の操作................................................................................................... 180
9.20. Image...................................................................................................................... 191
9.20.1. XAML 内での URL............................................................................................. 191
9.20.2. 画像の表示例 ...................................................................................................... 192
9.20.3. ユーザーの指定したファイルの表示.................................................................. 194
9.20.4. 画像の表示時の拡大方法方法............................................................................. 198
9.21. InkCanvas............................................................................................................... 200
9.21.1. ペン以外での入力の受け付け............................................................................. 201
9.21.2. ペンの色・太さを変える .................................................................................... 202
9.21.3. 文字認識 ............................................................................................................. 204
9.22. MapControl ............................................................................................................ 206
9.23. MediaElement ........................................................................................................ 215
9.24. Povot ...................................................................................................................... 217
9.25. PasswordBox .......................................................................................................... 221
7
9.26. ProgressBar ............................................................................................................ 223
9.27. ProgressRing .......................................................................................................... 225
9.28. ScrollViewer ........................................................................................................... 226
9.29. Slider ...................................................................................................................... 231
9.30. WebView ................................................................................................................ 232
9.31. AugoSuggestBox..................................................................................................... 237
9.32. SplitView ................................................................................................................ 248
9.32.1. ハンバーガーメニューの実装............................................................................. 249
10. 共有................................................................................................................................ 253
10.1.1. 共有の送信側の作成 ........................................................................................... 253
10.1.2. 共有の受信側の作成 ........................................................................................... 260
11. バックグラウンドタスク ............................................................................................... 265
11.1.1. バックグラウンドタスクの作成と登録 .............................................................. 265
11.1.2. バックグラウンドタスクとフォアグランドの連携 ............................................ 269
11.1.3. バックグラウンドタスクの追加情報.................................................................. 275
11.1.4. システムイベントへの応答 ................................................................................ 275
12. タイルとトースト .......................................................................................................... 276
12.1.1. タイルやトーストを作成するための補助ツール................................................ 276
12.1.2. ライブタイル ...................................................................................................... 278
12.1.3. セカンダリタイル............................................................................................... 281
8
12.1.4. トースト ............................................................................................................. 284
13. プッシュ通知.................................................................................................................. 293
14. コルタナ連携.................................................................................................................. 313
14.1.1. フォアグラウンドアプリの起動 ......................................................................... 313
14.1.2. バックグラウンドアプリの起動 ......................................................................... 323
15. アプリ間連携.................................................................................................................. 331
15.1. AppService.............................................................................................................. 331
15.2. 別アプリの起動 ...................................................................................................... 336
15.2.1. プロトコルによるアプリの起動 ......................................................................... 336
15.2.2. 結果を受け取るプロトコルによるアプリの起動................................................ 337
16. 複数デバイスに対応したアプリの作成.......................................................................... 339
16.1. アダプティブコード ............................................................................................... 339
16.2. アダプティブ UI..................................................................................................... 344
17. UserControl とテンプレートコントロール ................................................................... 349
17.1. UserControl............................................................................................................ 349
17.2. テンプレートコントロール .................................................................................... 353
18. UWP でのアプリの設計................................................................................................. 361
18.1. MVVM パターンとは............................................................................................. 361
18.2. Prism for Windows Runtime .................................................................................. 362
18.2.1. 足し算アプリの作成 ........................................................................................... 363
9
18.2.2. 基本的なクラスの使い方 .................................................................................... 378
19. まとめ ............................................................................................................................ 387
10
1. 本書について
本書は、Windows 10 で追加された Universal Windows Platform(以下 UWP)についての入門
書になります。以下の開発環境を前提としています。
1. Windows 10
2. Visual Studio 2015 Update 3
開発言語としては、XAML と C#を使用しています。本書では、C#の言語面での解説は行いま
せん。C#についての入門は別途書籍やサイトなどを参考にしてください。
また、UWP を実行する OS は以下のものを想定しています。
1. Windows 10
2. Windows 10 Mobile
2. Universal Windows Platform とは
UWP とは、Windows 10 で追加された、プラットフォームで以下の特徴を持ちます。
1. Windows 10 が動くすべてのデバイス上でワンバイナリでアプリが動く
(ア) Windows 10 の動くデスクトップ
(イ) Windows 10 の動くタブレット
(ウ) Windows 10 Mobile の動く電話
(エ) Surface Hub
(オ) HoloLens
(カ) Windows 10 IoT の動くラズベリーパイなどの IoT デバイス
2. Windows ストアで配布が可能
11
UWP 上で動くアプリのことを UWP アプリといいます。
2.1. UWP ア プ リ ま で の 歴史
ここで少し余談ですが、UWP アプリに至るまでの歴史を著者の主観が入った状態ですが少し
見ていきたいと思います。さかのぼれば何処までもさかのぼれますが、ここでは、.NET
Framework 3.0 で追加された WPF からの歴史を見ていきたいと思います。
2.1.1. .NET Framework 3.0
.NET Framework 3.0 で、Windows Presentation Foundation(以下 WPF)が追加されました。
これは、デスクトップアプリケーションを開発するためのプラットフォームで XAML(ザム
ル)と呼ばれる XML をベースとした言語で画面を構築して、C#でロジックを記述するという
開発スタイルが登場しました。この開発スタイルは、UWP でも受け継がれています。
2.1.2. Silverlight
次に、Rich Internet Application(以下 RIA)と呼ばれるキーワードが持てはやされた時代が来ま
す。この時は、現在のように HTML5 を中心とした開発ではなく Flash や Siliverlight と呼ばれ
るブラウザプラグイン上でアプリケーションを構築することが主流だったと私は感じていま
す。(HTML/CSS/JavaScript でも開発出来たが、今よりも凄く大変だった)
Silverlight は、勢いのあったころは WPF を駆逐するのではないかと錯覚させるほど、機能追加
が活発で、OutOfBrowser というブラウザプラグインなのに、サンドボックス内にありつつデ
スクトップアプリケーションのように動作するモードなども追加されました。UWP も大ざっ
ぱにみると、サンドボックスの中で動く XAML と C#によって開発されたアプリが動くという
ことで、とても似たような雰囲気があります。
2.1.3. Windows Phone 7.x
次に、Windows Phone です。これは、Silverlight がスマートフォンアプリとして動くという点
が特徴です。この時は、Silverlight がこのまま全プラットフォームにひろがっていくのではな
いかと感じていました。(現実はそうではありませんでしたが…)
12
2.1.4. Windows 8
Silverlight の波も去り、Microsoft がタッチファーストという考えで作り出し、世間的には
Vista 並みに失敗したと認識されている(私は好きですよ)Windows 8 が登場します。
Windows 8 では、ストアアプリと呼ばれる UWP の前身となるアプリケーションプラットフォ
ームが登場します。COM の上に構築された Windows Runtime 上で動くアプリケーションがこ
こで登場しました。Windows Runtime は、UWP でも使用されています。
2.1.5. Universal app
Windows 8.1 と Windows Phone 8.1 で 95%くらい API が共通化されて、ほぼ同一ソースで
Windows 8.1 上と Windows Phone 8.1 上で動くアプリケーションのバイナリを、それぞれ作成
することが出来るようになりました。ソースコードレベルとはいえ、同じコードが電話でもパ
ソコンでも動くという大きな衝撃を与えてくれたテクノロジです。
2.1.6. Universal Windows Platform
そして、UWP に繋がります。Universal app がソースコードの共有だったものを、UWP で
は、同一バイナリが Windows 10 の動く様々なデバイスで動くということを実現しました。開
発手法は、WPF が登場してから一貫して XAML と C#による開発です。
HoloLens への対応や Windows 10 への力の入れ具合などから、現在、そして今後も大きく
Microsoft が投資していくプラットフォームであることが伺えます。現に XAML などの開発言
語も WPF と比べて機能強化が積極的に行われています。本書では、この WPF から歴史を積み
上げて、現在の最新のプラットフォームである UWP についての開発について取り上げます。
2.2. 開 発 の前 に
UWP の開発の話しに入る前に、UWP のアプリの作法であるガイドラインを紹介しておきま
す。このガイドラインは、守らなければいけないというものではないですが、UWP のアプリ
を作る上で、よくあるナビゲーションパターンや画面パターンなど、多くの有益な情報が記載
されています。
13
是非、Windows ストアなどに提出するようなアプリを作る場合には一読しておくことをお勧め
しておきます。
Windows アプリ UX デザイン ガイドライン
https://msdn.microsoft.com/ja-jp/mt634411.aspx
上記ページの中にある、「Windows 10 アプリ UX デザイン ガイド」が、それになります。
2.3. デ バ イス フ ァ ミ リ
UWP を開発するうえで知っておいたほうがいい概念としてデバイスファミリというものがあ
ります。デバイスファミリとは、アプリが動作するプラットフォームのようなものです。
Universal app が Windows 8.1 と Windows Phone 8.1 などの OS を対象としていたのに対し
て、UWP ではデバイスファミリを対象とします。デスクトップパソコンでは、デスクトップ
デバイスファミリに基づいて実行し、モバイル端末ではモバイルデバイスファミリに基づいて
実行されます。ユニバーサルデバイスファミリと呼ばれる特別なデバイスファミリも存在し、
ユニバーサルデバイスファミリで提供される、API は、全てのデバイスファミリで動作するこ
とが保証されています。
デバイスファミリの関係をあらわした図を以下に示します。
各デバイスファミリには、プラットフォームに固有の API が提供されています。これらの API
は、実行時に動的に存在を確認して呼び出すアダプティブコードを記述することが出来ます。
デバイスファミリを選択するということは、呼び出せる API 群を決定するということです。先
ほども言った通り、アダプティブコードを記述することでデバイスファミリ固有の API を使用
しつつ、他のデバイスファミリ上でも動作させるといったことも出来るようになっています。
14
必要があれば、デスクトップデバイスファミリ上でしか動作させないといったことも可能で
す。
2.4. ア ダ プテ ィ ブ UI
UWP は、様々なデバイス上で動作するアプリケーションを、ワンバイナリで作ることが出来
ます。逆に言うと、ワンバイナリで様々な画面サイズで動作するアプリケーションを作らない
といけないということになります。このような要件に対応するため、アダプティブ UI を実現
しなければなりません。アダプティブ UI は、画面サイズに応じて最適な画面レイアウトを提
供する UI です。
以下のような小さなモバイル端末から、ホワイトボードサイズの Surface Hub まで様々なデバ
イスに対応する必要があります。
(https://msdn.microsoft.com/ja-jp/library/windows/apps/dn894631.aspx より引用)
アダプティブ UI のパターンについても、「開発の前に」で紹介したガイドラインに記載があ
りますので、併せて確認してみてください。
2.5. Hello world
最初の UWP アプリとして Hello world を作成してみましょう。ボタンを押すと、Hello world
というメッセージボックスが表示されます。
2.5.1. 前 準 備
UWP のアプリを開発するためには、Visual Studio 2015 のインストール時に UWP の開発環境
をインストールするように構成する必要があります。選択をせずに、インストールした場合は
15
コントロールパネルからプログラムの追加と削除を選んで、Visual Studio 2015 を選択して変更
を押してから、UWP の開発環境をインストールしてください。
また、最初にやる設定として、パソコンを開発者モードにする必要があります。設定(Win +
i)を表示して「更新とセキュリティ」を選んで、「開発者向け」を選択して「開発者モード」
にします。
2.5.2. プ ロ ジェ ク ト の 作 成
メニューの「ファイル」→「新規作成」→「プロジェクト」を選択して、「テンプレート」の
「Visual C#」の「Windows」の「ユニバーサル」にある「空白のアプリ(ユニバーサル
Windows)」を選択します。名前の項目に HelloWorld と入力して「OK」を選択してくださ
い。
16
動作対象とするターゲットプラットフォームと最小プラットフォームのバージョンの選択ダイ
アログが出てくるので、そのまま「OK」を選択してください。
作成が完了すると以下のようなプロジェクトが作成されます。
17
1. Assets フォルダ
インストール後のプログラムフォルダに表示されるアイコンや、タイルなどに表示される
アイコンが入っています。
2. App.xaml/App.xaml.cs
アプリケーションのエントリーポイントです。
3. ApplicationInsights.config
ApplicationInsights の構成ファイルです。プロジェクト新規作成時に ApplicationInsights
を使用しない構成にしていた場合は作成されません。無くても問題ありません。
4. HelloWorld_TemporaryKey.pfx
アプリケーションのインストールパッケージを作成するときに使用される鍵です。
5. MainPage.xaml/MainPage.xaml.cs
アプリケーション起動時に最初に表示される画面です。
6. Package.appxmanifest
アプリケーションのマニフェストファイルです。権限の設定などを行います。
7. project.json
NuGet のライブラリの依存関係が記載されています。
2.5.3. 画 面 の作 成
18
Hello world の画面を作成します。MainPage.xaml をダブルクリックして開くとデザイナが表示
されます。
デザイナの下部には、XAML と呼ばれる画面を記述する言語が表示されています。デザイナの
左側にツールボックスが表示されていて、そこにボタンなどの各種コントロールがあります。
19
ツールボックスから Button をデザイナにドラッグアンドドロップすることで、好きな場所に
ボタンを配置出来ます。
20
Button を選択した状態で、プロパティウィンドウを見ると Button の設定を変更できることが
わかります。Content プロパティに Say hello と入力すると Button のテキストが Say hello にな
ります。
21
Button をデザイナ上でダブルクリックすることで、Button がクリックされたときの処理が生
成されます。MainPage.xaml.cs が以下のようになります。
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
}
}
このように XAML に紐づく C#のファイルをコードビハインドと言います。コードビハインド
に処理を書きましょう。button_Click メソッドを以下のように書き換えます。
private async void button_Click(object sender, RoutedEventArgs e)
{
var dialog = new MessageDialog("Hello world");
await dialog.ShowAsync();
}
MessageDialog は、名前空間がデフォルトで using されていないので、「Ctrl + .」を押して
using を追加してください。UWP の API は、非同期のものがほとんどです。少しでも処理に
時間がかかるものは全て非同期メソッドとして提供されています。async/await は頻繁に使うの
で、知らない方はマスターしておきましょう。
2.5.4. プ ロ グラ ム の 実 行
最後にプログラムの実行を行います。Visual Studio のツールバーを以下のように「x86」が選
択されていることを確認して「▶ローカルコンピューター」を選択します。
22
実行すると Button が置かれた Window が表示されます。Button をクリックすると以下のよう
にダイアログが表示されます。
続けて Windows 10 Mobile で動作させてみましょう。「▶ローカルコンピューター」の横の▼
をクリックすると以下のように動作対象のターゲットが選べます。
23
その中から、「Mobile Emulator 10.0.10586 WVGA 4 inch 512MB」(細かいバージョン番号
は異なっている可能性があります)を選択します。選択したあと、再度クリックすると
Windows 10 Mobile のエミュレータが起動して Hello world が実行されます。
24
Button をクリックすると、以下のようにダイアログが表示されます。
25
詳しい手順は紹介しませんが、CPU を「ARM」にして「▶Device」にすることで開発者モー
ドにした USB で接続された Windows 10 Mobile の実機にアプリを転送して動かすことが出来
ます。Windows 10 Mobile をお持ちの方は試してみてください。
2.5.5. ま と め
プロジェクトの作成から、アプリケーションの実行までを紹介しました。また、デザイナを使
って画面をデザイン出来ることや、C#を使って処理を書けることを示しました。そして、
UWP の醍醐味でもある、デスクトップとモバイルで同じアプリケーションが動くということ
も示しました。ちょっとした Hello world ですが、色々な物が詰まっています。
3. XAML
ここでは、UWP の画面構築に使用する XAML という言語についてみていきます。XAML は、
Hello world の章でデザイナの下に表示されていたもので、XML をベースにして作られた言語
になります。XAML は、画面構築専用の言語ではなくて、汎用的に使えるオブジェクトを組み
立てるための言語になります。ツリー構造を持ったオブジェクトの構築に必要な機能が色々詰
まっています。画面は、Page をルートとしたツリー構造のオブジェクトのため、XAML で記
述するのに、とても適している言語です。
XAML は、XML 名前空間を C#の名前空間に、タグをクラス名に、属性をプロパティ名に対応
させています。また、XML 名前空間には、いくつかの組み込みのものが定義されていて、それ
26
らには x や d や mc などの名前を付けることが一般的です。一般的な Page(UWP での画面を
あらわすクラス)の XAML での定義は以下のようになります。
<Page x:Class="XamlBasics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
</Page>
4 つの XML 名前空間が定義されています。まずは、XAML で定義する名前空間と、様々な便
利機能が定義されている x 名前空間と、デザイナ向けの機能を提供する d 名前空間です。mc
名前空間は、mc:Ignorable で実行時には無視する名前空間を指定するために定義されていま
す。x:Class 属性は、コードビハインドクラスのクラス名をあらわしています。この、x:Class
属性で、XAML と裏のロジックのクラスの結び付けが行われています。この形が UWP で扱う
XAML の基本的な形になります。
この XAML に紐づくコードビハインドは、以下のようになっています。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlBasics
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
}
}
27
XAML のタグが Page タグなので、Page クラスを継承している点がポイントです。また、
XAML で定義された設定を有効化するために、コンストラクタで InitializeComponent メソッ
ドを呼び出している点もポイントになります。
以下に XAML の基本機能を使った XAML を示します。
<Page x:Class="XamlBasics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlBasics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Content>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Children>
<Button Content="Hello world"
Click="Button_Click" />
</Grid.Children>
</Grid>
</Page.Content>
</Page>
まず、Button タグに注目してみましょう。Button の Content 属性に Hello world を指定してい
ます。これは、Button クラスのインスタンスの Content プロパティに Hello world という文字
列を設定していることになります。このように、とても直感的に属性を使ってプロパティを設
定可能です。また、もう 1 つ Click 属性がありますが、こちらはイベントになります。イベン
トでは、コードビハインドで対応するメソッドの名前を定義することで、イベントに対してメ
ソッドを追加することが出来ます。
次に、Page と Grid の関係について注目してみましょう。先ほどの Button の例のように、単純
な文字列や数値の場合は属性を使ってプロパティの値を指定できますが、プロパティに設定し
たいものが Button や Grid といった他のクラスのインスタンスだった場合に、特別な命名規約
に沿ったタグを使うとプロパティを設定できます。命名規約は、「クラス名.プロパティ名」と
28
いうタグになります。このような命名規約のタグを使うことで、タグの子要素のオブジェクト
をプロパティに設定することができます。この構文のことを、「プロパティ要素の構文」とい
います。
次に、Grid の Background 属性で指定されている{}で囲まれた部分です。これは「マークアッ
プ拡張」といって単純な文字列では指定できないようなオブジェクトを設定するための構文に
なります。StaticResource は、後程説明しますが、UWP のリソースと呼ばれるところから、オ
ブジェクトを取得してきます。その他に{x:Null}などで null を渡すといったことも可能です。
次にコンテンツ構文です。コンテンツ構文は、オブジェクトに対して 1 つだけ指定可能なコン
テンツプロパティというものを、タグの直下に書くことでコンテンツプロパティに自動で設定
出来るものです。例えば、Page では Content プロパティがコンテンツプロパティに指定されて
います。また、Grid は Children プロパティがコンテンツプロパティに指定されています。そ
のため、先ほどの XAML は、以下のようにシンプルに記述できます。
<Page x:Class="XamlBasics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlBasics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Hello world"
Click="Button_Click" />
</Grid>
</Page>
その他の構文として、コレクション構文というものがあります。例えば、Grid の Children
は、コレクション型なのですが、ここに複数のタグを追加するとコレクションに複数格納され
ます。例えば、Grid に Button を 2 つ以上置くことが出来ます。例えば以下のように書きま
す。
<Page x:Class="XamlBasics.MainPage"
29
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlBasics"
xmlns:d=http://schemas.microsoft.com/expression/blend/2008tugini
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Hello world"
Click="Button_Click" />
<Button VerticalAlignment="Top"
Content="Button2" />
<Button VerticalAlignment="Bottom"
Content="Button3" />
</Grid>
</Page>
次に、任意の名前空間にあるオブジェクトを XAML に組み込む方法を紹介します。XAML
は、XML 名前空間を C#の名前空間と対応づけることができます。対応づけかたは、上記の
XAML 内の local という XML 名前空間にあるとおり、「using:名前空間」という形の書式にな
ります。例えば XamlBasics という名前空間に Person という名前のクラスがある場合、以下の
ように記述できます。
<local:Person />
次に、添付プロパティという機能を紹介します。添付プロパティは、名前の通りオブジェクト
自体には定義されていないプロパティを追加で設定できる機能になります。UWP 上では、主
にコントロールのレイアウトに必要な情報をコントロールに追加するために使用されていま
す。例えば、Grid コントロールは、行と列を指定してコントロールを何処に配置するのか指定
できるのですが、そこで、以下のように使用されています。
<Page x:Class="XamlBasics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlBasics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
30
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<!-- 行の定義 -->
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- Grid の 0 行目、1 行目に配置 -->
<Button Content="Layout sample"
Grid.Row="0" />
<Button Content="Hello world"
Grid.Row="1" />
</Grid>
</Page>
Button タグの Grid.Row という属性で指定している箇所が添付プロパティにあたります。
Button に Grid が持っている Row という添付プロパティの値を設定しています。このように、
他のクラスで定義されたプロパティの値を設定できる点が特徴になります。
最後に、マークアップ拡張について説明します。マークアップ拡張は、複雑なオブジェクトを
簡単に記述するための記法になります。{}で囲んで記述します。例えば{x:Null}とすることで、
null をあらわすことが出来ます。その他に、データバインディングの章で紹介しますが、
{Binding Path=Name}などのようなデータバインディングオブジェクトの生成や、リソースの
参照を行うための{StaticResource KeyName}のようなものがあります。マークアップ拡張のそ
れぞれの意味については、各章で後述します。
4. UWP のコントロールの基本クラス
ここでは、UWP のコントロールの基本クラスである、DependencyObject について説明を行い
ます。
4.1. DependencyObject
31
DependencyObject は、UWP のコントロールに必要な基本的な機能を提供します。提供する機
能は以下の通りです。
1. 依存関係プロパティ
2. 添付プロパティ
3. スレッド操作
順に説明していきます。
4.1.1. 依 存 関係 プ ロ パ テ ィ
依存関係プロパティは、Windows Runtime で使用される特殊なプロパティで、デフォルト値の
提供や、プロパティの値に変更があったときに別のプロパティへの値の伝搬や、アニメーショ
ンのサポートや、データバインディングのサポートなど様々な機能を持ったプロパティです。
具体的には、以下のように実装されます。
public class Range : Windows.UI.Xaml.DependencyObject
{
// プロパティのキー
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(Range), new PropertyMetadata(0,
ValueChanged));
// CLR のプロパティとしてラッピング
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void ValueChanged(Windows.UI.Xaml.DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// 変更があったときの処理
32
}
}
まず、クラスが DependencyObject クラスを継承していることが必要となります。そして、
DependencyProperty クラスの Register メソッドで依存関係プロパティを登録します。引数
は、プロパティ名、プロパティの型、プロパティを所有する型、メタデータになります。メタ
データは、PropertyMetadata 型でコンストラクタの引数は、プロパティのデフォルト値と、プ
ロパティに変更があったときのコールバック(コールバックの指定はオプション)になりま
す。コールバックは、static なメソッドで、第一引数に値が変更されたオブジェクトと、プロ
パティの変更後の値と変更前の値を持ったイベント引数というシグネチャになります。依存関
係プロパティ自体には必須ではないのですが、CLR のプロパティの形(要は普通の C#のプロ
パティ)にラップするのが一般的です。こうすることで、コード上から自然にアクセスが可能
になります。CLR のプロパティの形式を使用しない場合と使用する場合のコードを以下に示し
ます。
var r = new Range();
// CLR のプロパティを使用しない場合
r.SetValue(Range.ValueProperty, 10);
var value = (int)r.GetValue(Range.ValueProperty);
// CLR のプロパティのラッパを使用する場合
r.Value = 100;
var value2 = r.Value;
依存関係プロパティの PropertyMetadata のコールバックを使うことで、プロパティの値に変更
があったときに、他のプロパティの値に状態を伝搬するなどの処理を書くことが出来ます。例
として Range クラスで、Min と Max プロパティを追加して、Value が必ず Min と Max の間に
あるように調整する処理を追加したコードを以下に示します。
public class Range : Windows.UI.Xaml.DependencyObject
{
// プロパティのキー
public static readonly DependencyProperty ValueProperty =
33
DependencyProperty.Register("Value", typeof(int), typeof(Range), new PropertyMetadata(0,
ValueChanged
public static readonly DependencyProperty MinProperty =
DependencyProperty.Register("Min", typeof(int), typeof(Range), new
PropertyMetadata(int.MinValue, Min
public static readonly DependencyProperty MaxProperty =
DependencyProperty.Register("Max", typeof(int), typeof(Range), new
PropertyMetadata(int.MaxValue, Max
// CLR のプロパティとしてラッピング
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public int Min
{
get { return (int)GetValue(MinProperty); }
set { SetValue(MinProperty, value); }
}
public int Max
{
get { return (int)GetValue(MaxProperty); }
set { SetValue(MaxProperty, value); }
}
// コールバック
private static void ValueChanged(Windows.UI.Xaml.DependencyObject d,
DependencyPropertyChangedEventArgs e
{
((Range)d).CourceValue();
}
34
private static void MinChanged(Windows.UI.Xaml.DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((Range)d).CourceMin();
((Range)d).CourceValue();
}
private static void MaxChanged(Windows.UI.Xaml.DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((Range)d).CourceMax();
((Range)d).CourceValue();
}
// 値を調整するメソッド
private void CourceMax()
{
if (this.Max < this.Min)
{
this.Max = this.Min;
}
}
private void CourceMin()
{
if (this.Min > this.Max)
{
this.Min = this.Max;
}
}
private void CourceValue()
{
if (this.Max < this.Value)
35
{
this.Value = this.Max;
}
if (this.Min > this.Value)
{
this.Value = this.Min;
}
}
}
このクラスを使って以下のようなコードを記述して動作を確認してみます。
var range = new Range();
Debug.WriteLine($"Min: {range.Min}, Max: {range.Max}, Value: {range.Value}");
range.Min = 0;
range.Max = 10;
range.Value = 100;
Debug.WriteLine($"Min: {range.Min}, Max: {range.Max}, Value: {range.Value}");
range.Max = -1;
Debug.WriteLine($"Min: {range.Min}, Max: {range.Max}, Value: {range.Value}");
実行するとデバッグ時の出力ウィンドウに以下の結果が表示されます。
Min: -2147483648, Max: 2147483647, Value: 0
Min: 0, Max: 10, Value: 10
Min: 0, Max: 0, Value: 0
4.1.2. 添 付 プロ パ テ ィ
次に、XAML でも紹介した添付プロパティについて説明します。添付プロパティは XAML で
説明した通り、オブジェクトに対して別のクラスで定義されたプロパティを取得または設定で
きる機能になります。DependencyObject では、以下のようにして添付プロパティを定義しま
す。
36
public class AttachedPropertySample
{
public static readonly DependencyProperty SampleValueProperty =
DependencyProperty.RegisterAttached("SampleValue", typeof(int),
typeof(AttachedPropertySample), new PropertyMetadata(0));
public static int GetSampleValue(Windows.UI.Xaml.DependencyObject obj)
{
return (int)obj.GetValue(SampleValueProperty);
}
public static void SetSampleValue(Windows.UI.Xaml.DependencyObject obj, int value)
{
obj.SetValue(SampleValueProperty, value);
}
}
DependencyProperty クラスの RegisterAttached メソッドで DependencyProperty クラスのイ
ンスタンスを作成する点と、添付プロパティは、CLR の形式のプロパティではなく、static な
SetXXX と GetXXX という名前でラッパーを作るという点が異なります。また、添付プロパテ
ィを定義するクラスは、必ずしも DependencyObject を継承する必要はありません。
先ほどの Range クラスに、SampleValue 添付プロパティの値を設定して取得するコードは以下
のようになります。
var range = new Range();
// 添付プロパティの値の設定と取得
AttachedPropertySample.SetSampleValue(range, 100);
var sampleValue = AttachedPropertySample.GetSampleValue(range);
添付プロパティも PropertyMetadata を使ってデフォルト値とコールバックが指定できます。コ
ールバックを使うことで依存関係プロパティと同じように、別のプロパティに対して変更を伝
搬することが出来ます。
4.1.3. ス レ ッド 操 作
37
DependencyObject は、生成されたスレッドに紐づくという特徴があります。別スレッドから
依存関係プロパティなどの操作を行うと例外が発生します。そのため、UWP のコントロール
は UI 用のスレッド(UI スレッド)に紐づけられていて、その他のスレッドから操作すること
が出来なくなっています。例えば、バックグラウンドでデータを読み込んで UI に反映すると
いった処理で、この特徴が問題になってきます。そのようなケースに対応するため、
DependencyObject には、自分が生成されたスレッドに紐づく CoreDispatcher というクラスを
保持しています。このオブジェクトには DependencyObject の Dispatcher プロパティからアク
セスが出来ます。CoreDispatcher クラスには、CoreDispatcher が紐づくスレッドで処理を行う
ための RunAsync メソッドが定義されていて、以下のように記述することでスレッドを切り替
えて処理をすることが出来ます。
await range.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
// DependencyObject の紐づけられたスレッドで処理を行える
});
第一引数の CoreDiapatcherPriority は処理の実行の優先度で以下の値を指定できます。
 Idle
一番優先度が低い。アイドル状態のときに実行される。
 Low
低い優先順位。自分より高い優先順位の処理がないと実行される。
 Normal
普通の優先順位。順番に実行されます。
 High
最高の優先順位。ユーザーアプリケーションでの使用はしない。
4.2. FrameworkElement
UWP のコントロールの多くがこのクラスを継承しています。このクラスは、レイアウトや
XAML を使ったプログラミングで重要になるデータバインディングや、見た目を共通化するた
めの Style や、リソースと呼ばれるデータを定義しておく場所の機能などを提供しています。
38
レイアウトに関するプロパティとして Margin というプロパティがあります。このプロパティ
を指定することで、コントロールの内側に余白を設けることが出来ます。例えば Button に余
白を設ける場合は以下のように XAML を記述します。
<Button Margin=”10,5,10,5”
Content=”Hello” />
マージンは、左、上、右、下の順番でカンマで区切って指定します。以下のように余白が設定
されます。(黒枠がマージンの外側)
リソースは、Resources プロパティとして定義され、そこにブラシや Style などの様々なオブジ
ェクトの名前を付けて登録することが出来ます。リソースに定義されたオブジェクトは、
StaticResource マークアップ拡張によって XAML から取得できます。例えば、SolidColorBrush
と言う単色塗りつぶしのブラシを定義して、ボタンから参照するコードは以下のようになりま
す。
<Page x:Class="XamlBasics.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlBasics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<SolidColorBrush x:Key="Brush"
Color="Red" />
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Hello"
Background="{StaticResource Brush}" />
39
</Grid>
</Page>
赤色のブラシを Brush という名前(x:Key で指定)で定義して、それをボタンの背景色に
StaticResource マークアップ拡張を使って指定しています。StaticResource で参照できる値は、
StaticResource を使う場所よりも親のコントロールで定義されている必要があります。例え
ば、上記の Brush という名前のリソースを取得する場合は、Button よりも上の階層である
Page の Resources で定義されているため取得できています。また、StaticResource マークアッ
プ拡張は、親へ親へ Resources を探索していくので、同じ名前のリソースが定義されていた場
合は、より自分に近い場所のリソースを参照します。Page まで親へさかのぼって見つからない
場合は、App クラスの Resources で定義されたものを参照します。App クラスでも見つからな
い場合は、組み込みで定義されているリソースを参照します。
4.3. Control
FrameworkElement を拡張し、コントロールに必要な基本的な機能を提供します。自分でカス
タムコントロールを提供する場合に、このクラスを継承することがあります。主な提供機能は
コントロールの見た目を自由に変更することが出来る Template という機能になります。
4.4. ContentControl
単一の要素を表示するためのコントロールになります。Page や Button や UserControl など多
くのコントロールが、このコントロールを継承しています。ContentControl は、Content プロ
パティに設定されたデータを非常に柔軟に表示する能力を持っていて、文字列が設定されてい
れば文字列を表示し、コントロールが設定されていれば、コントロールを表示します。任意の
オブジェクトを表示することも可能で、その場合 ContentTemplate というテンプレートを使っ
て見た目を定義できます。また、ContentTemplateSelector などを使うことでオブジェクトを
どのように表示するかということをカスタマイズすることも可能です。
4.5. ItemsControl
ItemsControl は複数の要素を表示するためのコントロールになります。ItemsSource プロパテ
ィに設定された IEnumerable に対して要素を表示します。表示される 1 要素ずつは
ContentControl を継承したクラスにホストされ ContentControl で説明したように柔軟に要素
40
を表示することが出来ます。ListBox、ListView などの複数の要素を表示するコントロールは
基本的にこのコントロールを継承しています。
ItemsControl は、INotifyCollectionChanged を実装しているコレクション(厳密には IList も必
要)を ItemsSource に設定することで、コレクションの要素の追加や削除に表示を追随させる
ことが出来ます。この INotifyCollectionChanged のデフォルトの実装クラスとして
System.Collections.ObjectModel.ObservableCollection<T>クラスが提供されています。このク
ラスを ItemsSource に設定することで、コレクションの追加・削除に簡単に追随させることが
出来るようになります。
基本的には、要素は縦に並ぶように配置されますが、ItemsPanel を変更することで縦ならびを
横ならびに変更できます。具体的には以下のような XAML を記述します。
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- 横並びにする -->
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
このように、ItemsControl は ContentControl のような柔軟な表示をしつつ、ItemsPanel を使
って並びを柔軟に対応できるような作りになっています。
4.6. Panel
Panel は、複数のコントロールをホストすることが出来るコントロールです。UWP のレイアウ
トシステムによって、ホストしたコントロールを柔軟に配置することが出来ます。Canvas(絶
対座標での要素の配置)、Grid(テーブルレイアウト)、StackPanel(縦や横に積み上げる形
で配置)、RelativePanel(相対位置による配置)などのコントロールがあります。これらのコ
ントロールは Children というプロパティにコントロールを格納して使います。Children プロパ
ティは XAML で説明したコンテンツコントロールになっているため、以下のようにタグの下に
直接別のコントロールを記述することが出来ます。
41
<StackPanel>
<Button Content=”Button1” />
<Button Content=”Button2” />
<Button Content=”Button3” />
</StackPanel>
5. データバインディング
著者が UWP(XAML を使った)アプリケーションで非常に重要な要素と考えるデータバイン
ディングについて説明します。この機能を使いこなすことで、見た目とロジックを綺麗に切り
離したアプリケーションを作ることが出来ます。
UWP のデータバインディングには 2 種類のものが存在します。WPF の頃から存在する実行時
データバインディングと、UWP に追加されたコンパイル時データバインディングになりま
す。順番に解説していきます。
5.1. 実 行 時デ ー タ バ イ ンデ ィ ン グ
実行時データバインディングは、FrameworkElement で実装されている機能になります。
DataContext というプロパティに格納されたオブジェクトと、コントロールのプロパティの値
の同期をとる機能になります。(厳密にいうと Binding の Source に設定されたオブジェクトの
プロパティと、コントロールのプロパティの同期をとる機能になります。何も指定しないとデ
フォルトの Binding の Source が DataContext になります。)同期の取り方には、以下の 3 種
類が存在します。
1. OneWay(デフォルト)
DataContext に設定されたオブジェクトのプロパティ(ソースと言います)からコントロ
ールのプロパティ(ターゲットと言います)に対する 1 方向の値の同期になります。ソー
スの値の変更を感知したらターゲットの値が更新されます。ソースの値の変更の感知は、
DependencyProperty の場合は自動的に、そうでない場合は INotifyPropertyChanged の
PropertyChanged イベントにより検知を行います。
42
2. TwoWay
ソースからターゲットの間を双方向に同期を取ります。ソースからターゲットへの同期の
タイミングは OneWay と同じです。
3. OneTime
初回の 1 度だけソースからターゲットに対して値の同期を取ります。
実行時データバインディングには Binding マークアップ拡張を使います。例えば以下のような
Person という名前のクラスがあるとします。
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
private int age;
public int Age
{
get { return this.age; }
set
{
this.age = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age)));
43
}
}
}
Name と Age プロパティを持っていて、INotifyPropertyChanged インターフェースによる変更
通知を実装しています。これを Page の DataContext に以下のように設定します。
<Page x:Class="RuntimeDataBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RuntimeDataBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.DataContext>
<local:Person />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
</Grid>
</Page>
Grid を StackPanel(子要素を縦や横に並べて表示するコントロール)に置き換えて、以下のよ
うに TextBox と TextBlock を置いてデータをバインドさせます。
<Page x:Class="RuntimeDataBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RuntimeDataBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.DataContext>
<local:Person />
</Page.DataContext>
44
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Text="{Binding Path=Name, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding Path=Name}" />
<TextBox Text="{Binding Path=Age, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding Path=Age}" />
</StackPanel>
</Page>
最初の TextBox と TextBlock の組が Name プロパティへのデータバインディングで、2 つ目の
TextBox と TextBlock の組が Age プロパティへのデータバインディングになります。実行する
と TextBox と TextBlock の値が Person クラスを通して同期されていることが確認できます。
Binding マークアップ拡張には、以下のようなプロパティが設定出来ます。
 Path
ソースのプロパティを指定します。「プロパティ 1.プロパティ 2」のようにオブジェクト
45
を辿るように Path を記述することが出来ます。また、配列などの「プロパティ 1[0]」記
法にも対応しています。Path プロパティは、Binding マークアップ拡張の最初に書く場合
は、省略することが可能です。つまり先ほどのコードの例は「{Binding Name,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}」のように記述することが出来
ます。
 Mode
OneWay, TwoWay, OneTime を指定します。
 UpdateSourceTrigger
Default, PropertyChanged, Explicit を指定します。通常、Default は PropertyChanged と
同様の意味になります。TextBox の Text に Binding するときは、Default はフォーカスが
外れた時に値の同期をとります。テキストが変化する度に値の同期をとる場合は上記の例
のように明示的に PropertyChanged を指定します。Explicit を指定した場合は
BindingExpression の UpdateSource メソッドを呼び出したタイミングで同期が取られま
す。
 Converter
ソースからターゲットと、ターゲットからソースへ値が同期されるときに値の変換ロジッ
クをあらわす IValueConverter を実装したクラスを指定します。
 ConverterParameter
Converter に渡すパラメータを指定します。
 ElementName
データバインディングのソースに x:Name で名前を付けたコントロールを指定する場合に
使用します。
 FallbackValue
バインディングで値を返せない場合に返す値を指定します。
 TargetNullValue
ターゲットの値が null の時に返す値を指定します。
46
 RelativeSource
バインディングのソースを相対的に指定します。以下の 2 種類の指定方法があります。
{Binding …, RelativeSource={RelativeSource TemplatedParent}}
{Binding …, RelativeSource={RelativeSource Self}}
TemplatedParent は、ControlTemplate と呼ばれるコントロールの見た目を定義する場所
で使用します。コントロール自身がソースとなります。
Self は、ターゲットの要素をバインディングのソースに指定します。
 Source
バインディングのソースを指定します。何も指定しない場合はデフォルトで DataContext
プロパティの値が使われます。
ElementName などを使ったデータバインディングの例を以下に示します。まず、以下のよう
な IValueConverter インターフェースを実装したクラスを定義します。
public class SanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// ソースからターゲット
return $"{value}さん";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
// ターゲットからソース方向
// ここでは未サポート
throw new NotSupportedException();
}
}
Converter は、FrameworkElement で紹介したリソースに定義して使用します。では、TextBox
に入力した値を使って「○○さん」と表示する TextBlock を作成してみます。
<Page x:Class="RuntimeDataBinding.MainPage"
47
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RuntimeDataBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:SanConverter x:Key="SanConverter" />
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox x:Name="TextBox" />
<TextBlock Text="{Binding Text, ElementName=TextBox, Converter={StaticResource
SanConverter}}" />
</StackPanel>
</Page>
実行すると、以下のように TextBox の入力に同期して、TextBlock の値が書き換わります。
あまりやる機会はないですが、実行時データバインディングは C#のコードからも設定するこ
とが出来ます。例えば、Name というプロパティと TextBox の Text をバインディングする場
合は以下のようなコードになります。
var binding = new Binding
{
Path = new PropertyPath("Name"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
48
this.TextBox.SetBinding(TextBox.TextProperty, binding);
Binding クラスのインスタンスを生成して、Path や Mode などのプロパティを設定したあと
に、ターゲットとなるオブジェクトの SetBinding メソッドで、バインディングのターゲットと
なる依存関係プロパティ(添付プロパティも可)と、Binding オブジェクトを指定します。
5.2. コ ン パイ ル 時 デ ー タバ イ ン ディ ン グ
実行時データバインディングは、バインディングを実行時に解決しますが、コンパイル時デー
タバインディングは、コンパイル時にバインディングの解決を行います。コンパイル時データ
バインディングの特徴を以下に示します。
 実行時ではなくコンパイル時にバインディングの情報が確定するため実行時データバイン
ディングに比べて高速
 バインディングのソースが、DataContext ではなく Page になっている
 デフォルトの Mode が OneTime になっている
実行時データバインディングとの大きな違いは以上のようになります。コンパイル時データバ
インディングには、{x:Bind …}というマークアップ拡張を使います。TextBox と TextBlock を
バインドするコードの例を以下に示します。
<Page x:Class="CompileTimeDataBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CompileTimeDataBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox x:Name="TextBox" />
<TextBlock Text="{x:Bind TextBox.Text, Mode=TwoWay}" />
</StackPanel>
</Page>
49
x:Name 属性を使って、TextBox を Page のメンバーとして定義しています。そのため、
TextBlock で定義している x:Bind から TextBox という名前で参照できます。実行すると、以下
のようになります。
コンパイル時データバインディングで指定可能なプロパティは以下の通りです。
 Path
 Mode
 TargetNullValue
 FallbackValue
 Converter
 ConverterParameter
各パラメータについては実行時データバインディングと、ほぼ同じになります。また、x:Bind
の特徴として、イベントハンドラをバインドできるという点があります。イベントハンドラの
バインドは、以下のシグネチャのメソッドに対して行えます。
void Foo();
void Foo(object sender, object eventArgs);
void Foo(object sender, XXXXEventArgs eventArgs); // 各イベントのイベント引数の型
例として、以下のようなクラスをバインドしてみたいと思います。
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
50
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public void ResetName()
{
this.Name = null;
}
}
XAML を以下に示します。
<Page x:Class="CompileTimeDataBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CompileTimeDataBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:Person x:Name="Person" />
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Text="{x:Bind Person.Name, Mode=TwoWay, TargetNullValue='未入力'}" />
<TextBlock Text="{x:Bind Person.Name, Mode=OneWay}" />
<Button Content="Reset"
51
Click="{x:Bind Person.ResetName}" />
</StackPanel>
</Page>
Page の Resources で x:Name を使って Page のメンバーとして Person を定義しています。コー
ドビハインドで以下のように定義するのと同じようなイメージになります。
public sealed partial class MainPage : Page
{
public Person Person { get; } = new Person();
}
そして、TextBox や TextBlock と Name プロパティをバインドしています。イベントのバイン
ドは Button の Click イベントで行っています。Person クラスに定義した ResetName メソッド
をバインドしています。実行すると以下のようになります。
TextBox に入力すると、Person クラスの Name プロパティを介して TextBlock に値が同期され
ます。(コンパイル時データバインディングの TextBox の Text プロパティへのバインディング
は特殊で、フォーカスが外れたときに値の同期が行われます)
52
ボタンをクリックすると、TextBox が未入力の状態に戻ります。
53
この他に、コンパイル時データバインディングは実行時にコードから設定できないという点が
実行時データバインディングと異なります。
6. アプリケーションライフサイクル
UWP アプリは、デスクトップアプリケーションよりスマートフォンアプリケーションのよう
なライフサイクルで動作します。具体的に言うと、デスクトップではアプリケーションは最小
化すると休止状態に移行して、メモリの使用状況によっては OS によってアプリケーションが
終了されることがあります。この終了はアプリケーションから検知することが出来ないため、
休止状態に入るときに終了されてもいいようにアプリケーションで備える必要があります。モ
バイルでは、バックグラウンドに回ったときに、休止状態に入ります。この UWP のライフサ
イクルを図にすると以下のようになります。
アプリケーションの起動は、プロジェクトの App クラスの OnLaunched メソッドがエントリ
ーポイントとなります。このとき、引数の LaunchActivatedEventArgs に直前のアプリの状態
(終了状態だったのかなど)が入っているので、それを見て必要に応じて復帰処理などを行い
ます。
6.1. ア プ リケ ー シ ョ ン の起 動 シ ーケ ン ス
54
アプリケーションは、LaunchActivatedEventArgs の PreviousExecutionState の値を見て起動時
の処理を適切に行う必要があります。PreviousExecutionState には、以下の値が格納されてい
ます。
 NotRunning
アプリケーションは実行されていなかった。
 Running
アプリケーションは実行中。
 Suspended
アプリケーションは中断状態だった。
 Terminated
アプリケーションは中断後に終了された。
 ClosedByUser
アプリケーションはユーザーによって終了された。
アプリケーションの起動時に行わないといけないことは、新規作成したひな形の App クラスに
定義されています。OnLaunched メソッドから不要な処理やコメントを除いたものを以下に示
します。
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: 以前中断したアプリケーションから状態を読み込みます
}
55
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
Window.Current.Activate();
}
}
UWP アプリは、Window.Current を通じてアプリの Window にアクセスできます。この
Window の Content プロパティに Frame という画面遷移機能を持ったコントロールを配置し
て、その中で画面遷移を行います。Window.Current.Content に値が設定されていないという
ことは、終了状態からの起動ということなので、Frame を新規作成して設定します。さらに先
ほど説明した PreviousExecutionState を確認して Terminated の時に、直前の中断時に保存し
たデータを使って復元を行います。最後に PrelaunchActivated で直前のアクティブ化状態を確
認して、アクティブでない場合は、必要に応じて画面遷移(Frame の Navigate メソッド)を行
い、自分自身をアクティブ化します。
以上が、アプリケーションの基本的な起動シーケンスになります。
6.2. サ ス ペン ド 時 の 処 理
アプリケーションがサスペンドされるときには、App クラスで Suspending イベントが発生し
ます。デフォルトで作成されている App クラスではコンストラクタで Suspending イベントを
購読して、OnSuspending メソッドが呼ばれるようになっています。
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
56
}
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: アプリケーションの状態を保存してバックグラウンドの動作があれば停止します
deferral.Complete();
}
e.SuspendingOperation.GetDeferral メソッドの呼び出しから Complete メソッドの間で保存処
理を行います。この GetDeferral で取得したオブジェクトの Complete メソッドを呼び出すと
中断時の状態保存が終わったということを通知したことになります。
6.3. サ ス ペン ド か ら の 復帰
あまり利用することはありませんが、サスペンド状態からアプリケーションが終了されないま
まフォアグラウンドに復帰したときに呼び出される処理があります。App クラスで Resuming
イベントを購読することで処理が出来ます。
6.4. ア プ リケ ー シ ョ ン ライ フ サ イク ル に 対 応 した サ ン プル
ここでは、簡単な中断処理に対応したアプリケーションを作成したみたいと思います。
LifecycleApp という名前でアプリケーションを作ります。そして、以下のようなクラスを作成
します。
using System.ComponentModel;
namespace LifecycleApp
{
public class LifecycleAppModel : INotifyPropertyChanged
{
public static LifecycleAppModel Instance { get; } = new LifecycleAppModel();
public event PropertyChangedEventHandler PropertyChanged;
private string value;
57
public string Value
{
get { return this.value; }
set
{
this.value = value;
this.PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Value)));
}
}
}
}
このクラスを使って簡単なページを作成します。TextBox にデータを表示するだけの単純なペ
ージにします。MainPage.xaml.cs に以下のようなプロパティを定義します。
public sealed partial class MainPage : Page
{
public LifecycleAppModel Model { get; } = LifecycleAppModel.Instance;
public MainPage()
{
this.InitializeComponent();
}
}
MainPage.xaml を以下のように編集して LifecycleAppModel の Value と TextBox の Text をバ
インドします。
<Page x:Class="LifecycleApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:LifecycleApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
58
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Text="{x:Bind Model.Value, Mode=TwoWay}" />
</StackPanel>
</Page>
この状態でアプリケーションが中断されたときにどう動くか確認してみます。アプリケーショ
ンをデバッグ起動して、Visual Studio のツールバーで右クリックして「デバッグの場所」にチ
ェックを入れます。(チェックが入っている場合は、そのままで大丈夫です)
59
デバッグの場所にチェックを入れると「ライフサイクルイベント」というのが作成されます。
そこの「中断とシャットダウン」を選択することで、アプリケーションをサスペンドさせて終
了させることが出来ます。
60
MainPage の TextBox に任意の文字列を入力して「中断とシャットダウン」をします。そし
て、再度アプリケーションを起動すると、TextBox の文字が消えていることが確認できます。
ユーザーからしたら、最小化(モバイルの場合はバックグラウンドに回した)したアプリの入
力がクリアされてしまったという挙動になります。これを中断処理に対応して改善します。ま
ず、App クラスの OnSuspending メソッドで ApplicationData.Current.LocalSettings を使って
データの保存を行います。ApplicationData.Current.LocalSettings.Values(ローカルにデータを
保存するための Dictionary)に InputValue をキーにして LifecycleAppModel の Value プロパテ
ィの値を保存します。
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
ApplicationData.Current.LocalSettings.Values["InputValue"] = LifecycleAppModel.Instance.Value;
deferral.Complete();
}
次に、OnLaunched メソッドの Terminated の if 文に、この保存したデータを読み込む処理を
追加します。
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
if (ApplicationData.Current.LocalSettings.Values.ContainsKey("InputValue"))
{
LifecycleAppModel.Instance.Value = (string)
ApplicationData.Current.LocalSettings.Values["InputValue"];
61
}
}
キーの存在を確認して、キーが存在した場合にデータの読み出しを行っています。この処理を
追加して、先ほどのように「中断とシャットダウン」をします。すると以下のように入力した
データが残っていることが確認できると思います。
中断前が以下のようになります。
中断からの復帰後は、以下のようになります。
7. ローカルデータとローミングデータ
UWP では、以下のデータの保存場所が提供されています。
 ローカルアプリデータ
 ローミングデータ
62
 一時アプリデータ
アプリデータの保存方法には、設定とファイルという 2 種類があります。設定は、int や string
などの基本的な型や DateTime 型や GUID 型などを保存することが出来るディクショナリのよ
うな形で提供されます。ファイルは、任意のファイル形式でデータを保存することが出来ま
す。
7.1. ロ ー カル ア プ リ デ ータ
ローカルアプリデータは、ApplicationData.Current.LocalSettings で設定にアクセスできます。
ApplicationData.Current.LocalFolder でファイルを保存するためのフォルダにアクセスできま
す。まず、設定についてみていきます。
設定の一番簡単な使い方は、Values プロパティにディクショナリのようにアクセスすることで
す。コード例を以下に示します。
var settings = ApplicationData.Current.LocalSettings;
settings.Values["Key"] = "Value";
var value = (string)settings.Values["Key"];
if (settings.Values.ContainsKey("Key"))
{
// Key が存在する場合
}
else
{
// Key が存在しない場合
}
// 削除
settings.Values.Remove(“Key”);
ApplicationDataCompositeValue というクラスを使うことで、値をまとめて管理することが出
来ます。以下のようなコードになります。
var settings = ApplicationData.Current.LocalSettings;
var composite = new ApplicationDataCompositeValue();
63
composite["Key1"] = "Value1";
composite["Key2"] = "Value2";
settings.Values["compositeValue"] = composite;
この他に、ApplicationDataContainer を使って、データを階層化して管理することが出来ま
す。この階層は 32 階層まで作成することが出来ます。
var settings = ApplicationData.Current.LocalSettings;
// 作成
var container = settings.CreateContainer("Container1", ApplicationDataCreateDisposition.Always);
// 値の設定
container.Values["Key1"] = "Value1";
// 存在確認
if (settings.Containers.ContainsKey("Container1"))
{
// 存在するとき
}
else
{
// 存在しないとき
}
// 削除
settings.DeleteContainer("Container1");
設定は手軽に使えますが、ファイルを使って任意の形式でローカルアプリデータにデータを保
存することもできます。ファイルにアクセスするコードは以下のようになります。
// フォルダを取得
var folder = ApplicationData.Current.LocalFolder;
// ファイルを作成する(存在する場合は置き換える)
var file = await folder.CreateFileAsync("sample.txt", CreationCollisionOption.ReplaceExisting);
// ファイルに出力する
await FileIO.WriteTextAsync(file, "Hello world");
// ファイルから読み込む
var data = FileIO.ReadTextAsync(file);
64
ApplicationData.Current.LocalFolder に対して、CreateFileAsync を呼び出すことでファイルを
作成できます。第一引数がファイル名で、第二引数でファイルが存在したときの挙動などを指
定します。上記コードでは、ReplaceExisting を指定して、ファイルが既に存在する場合は、新
しいもので置き換えるという指定をしています。
ファイルの読み書きは FileIO クラスの WriteTextAsync メソッドと ReadTextAsync を使いま
す。JSON.NET などの.NET のクラスライブラリと連携するためには System.IO.Stream が必要
になるケースがあります。これは、ファイルの拡張メソッドを使うことで取得が可能になって
います。
// 書き込み
using (var stream = await file.OpenStreamForWriteAsync())
{
// JSON.NET などを使う
}
// 読み込み
using (var stream = await file.OpenStreamForReadAsync())
{
// JSON.NET などを使う
}
アプリケーションのバージョンアップなどにより、設定やファイルに保存しているデータに非
互換が発生してしまった場合は、ApplicationData.Current.SetVersionAsync メソッドを使用し
てバージョン管理を行うことが出来ます。SetVersionAsync メソッドは、第一引数にバージョ
ン番号を指定し、第二引数にコールバックを指定します。コールバックでは、以下のように現
在のバージョンと要求されたバージョンが確認できます。
await ApplicationData.Current.SetVersionAsync(0, r =>
{
var d = r.GetDeferral();
Debug.WriteLine($"CurrentVersion: {r.CurrentVersion}, DesiredVersion: {r.DesiredVersion}");
// バージョンが違ってたら必要に応じて変換ロジックを走らせる
d.Complete();
65
});
SetVersionAsync を呼び出した場合は、バージョン番号が変わっていなくてもコールバックが
呼ばれるので、その点は注意する必要があります。
7.2. ロ ー ミン グ デ ー タ
ローカルアプリデータは、デバイスのローカルストレージに保存されます。ここで紹介するロ
ーミングデータは、同じアプリのデータを複数のデバイスで同期をとります。この機能を使う
ことで、例えばユーザーがデスクトップで途中まで呼んでいた書籍を、外出先で続きから読む
といったシナリオが実現可能になります。ローミングデータにも、設定とファイルが存在し
て、以下のようにアクセスを行います。
// 設定
var roamingSettings = ApplicationData.Current.RoamingSettings;
// ファイル
var roamingFolder = ApplicationData.Current.RoamingFolder;
RoamingSettings も RoamingFolder も、ローカルアプリデータと同じ方法で使用できます。ロ
ーミングデータは、ApplicationData.RoamingStorageQuota で指定されるサイズ(KB 単位)以
上のデータがある場合は、ローミングが実行されません。この値は、Windows 10 Version 1511
で確認したところ 100 という値が反ってきました。
RoamingData がアップデートされたことを検知するには、
ApplicationData.Current.DataChanged イベントを使用します。以下のようなコードになりま
す。
public MainPage()
{
this.InitializeComponent();
ApplicationData.Current.DataChanged += Current_DataChanged;
}
private void Current_DataChanged(ApplicationData sender, object args)
{
66
// RoamingData がアップデートされた
}
7.3. 一 時 アプ リ デ ー タ
一時アプリデータは、設定は存在せずにファイルのみになります。以下のようにして一時アプ
リデータのフォルダを取得します。
// 一時アプリフォルダを取得する
var folder = ApplicationData.Current.TemporaryFolder;
フォルダ取得後は、ローカルアプリデータで示した通りファイルへのアクセスが可能です。
8. Advanced XAML
ここでは、UWP を作るうえで必須となる XAML の高度な機能を紹介したいと思います。
8.1. Style
UWP には、Style と呼ばれるコントロールのプロパティの設定を外だしにして定義する仕組み
があります。これを使うことで HTML の CSS のように見た目の定義を別にすることが出来ま
す。HTML の CSS と異なる点は、XAML という統一された言語でコントロールのツリー構造
と見た目の定義ができるという点です。HTML の CSS を知っている方向けに説明すると、
HTML の CSS がセレクタによって適用対象を指定して、見た目を定義するのに対して、
XAML の Style は名前を付けてリソースに定義して、コントロールから、それを参照して設定
します。(コントロールのスタイルにインラインで書くことも出来ますが、それならコントロ
ールに直接プロパティを設定したほうがいいと思われます)
Style の定義は、以下のように Page や App クラスの Resources に Style タグを使って定義しま
す。
<Page x:Class="ControlsApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlsApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
67
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- MyTextBlock という名前で TextBlock のスタイルを定義 -->
<Style x:Key="MyTextBlock"
TargetType="TextBlock">
<!-- FontStyle プロパティに Italic を指定 -->
<Setter Property="FontStyle"
Value="Italic" />
<!-- FontWeight に Bold を指定 -->
<Setter Property="FontWeight"
Value="Bold" />
</Style>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<!-- StaticResource を使って Style を設定 -->
<TextBlock Text="Hello world"
Style="{StaticResource MyTextBlock}"/>
</Grid>
</Page>
Style タグの TargetType で、何の型に対する Style なのか指定します。そして、Style タグの中
に Setter タグを使って何のプロパティに、どんな値を設定するのかを指定します。定義した
Style は、コントロールにある Style プロパティに StaticResource マークアップ拡張を使って指
定します。上記の XAML で、以下のように TextBlock が太字のイタリック体になります。
Style は、別のスタイルをベースに拡張して作ることも出来ます。Style の BaseOn にベースと
なる Style を StaticResource マークアップ拡張で指定します。先ほどの例の MyTextBlock スタ
68
イルを拡張して赤色の設定を追加した ExTextBlock という名前のスタイルを定義した XAML
を以下に示します。
<Page x:Class="ControlsApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlsApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- MyTextBlock という名前で TextBlock のスタイルを定義 -->
<Style x:Key="MyTextBlock"
TargetType="TextBlock">
<!-- FontStyle プロパティに Italic を指定 -->
<Setter Property="FontStyle"
Value="Italic" />
<!-- FontWeight に Bold を指定 -->
<Setter Property="FontWeight"
Value="Bold" />
</Style>
<!-- MyTextBlock を拡張して赤色にする -->
<Style x:Key="ExTextBlock"
TargetType="TextBlock"
BasedOn="{StaticResource MyTextBlock}">
<Setter Property="Foreground"
Value="Red" />
</Style>
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<!-- StaticResource を使って Style を設定 -->
<TextBlock Text="Hello world"
Style="{StaticResource MyTextBlock}" />
<TextBlock Text="Hello world2"
Style="{StaticResource ExTextBlock}" />
69
</StackPanel>
</Page>
この XAML を表示すると以下のようになります。ExTextBlock が、MyTextBlock に対して赤
色を追加したものになっていることが確認できます。
8.1.1. 定 義 済み の ス タ イ ル
UWP には、いくつかの Style が予め定義されています。この定義は「C:Program Files
(x86)Windows
Kits10DesignTimeCommonConfigurationNeutralUAP10.0.10586.0Generic」フォルダ
の generic.xaml でされています。よく使うものをいくつか紹介します。
 HeaderTextBlockStyle
ヘッダー用の大きな TextBlock 用 Style です。
 SubheaderTextBlockStyle
サブヘッダー用のヘッダーより一回り小さな TextBlock 用の Style です。
 TitleTextBlockStyle
タイトル用の TextBlock 用の Style です。
 SubtitleTextBlockStyle
サブタイトル用の TextBlock 用の Style です。
 BodyTextBlockStyle
本文用の TextBlock 用の Style です。
70
 CaptionTextBlockStyle
キャプション用の TextBlock 用の Style です。
 TextBlockButtonStyle
TextBlock の見た目を持ったボタンを定義するための Button 用の Style です。
 NavigationBackButtonNormalStyle
戻るボタン用の Button 用の Style です。
 NavigationBackButtonSmallStyle
小さな戻るボタン用の Button 用の Style です。
8.2. ア ニ メー シ ョ ン
UWP はアニメーションを組み込みでサポートしています。UWP のアニメーションは、指定し
た依存関係プロパティを指定した時間内で、指定した変化量で変化させ続ける仕組みになりま
す。単純な Canvas.Left(レイアウト用の Canvas コントロールの左端の位置を指定する添付プ
ロパティ)をアニメーションさせる XAML を以下に示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<Storyboard x:Key="MoveRectStoryboard">
<DoubleAnimation Storyboard.TargetName="Rect"
Storyboard.TargetProperty="(Canvas.Left)"
To="100"
Duration="0:0:5" />
</Storyboard>
</Page.Resources>
<Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
71
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red"
Canvas.Top="10"
Canvas.Left="10" />
</Canvas>
</Page>
アニメーションは、通常 Storyboard というものにまとめられます。Storyboard は、Resources
で定義を行うのが一般的です。上記の例では、Storyboard 内に、Page に定義されている
Rectangle コントロールの左の座標を 5 秒かけて 100 に変化させるというアニメーションが定
義されています。DoubleAnimation がその定義になります。DoubleAnimation に対して
Storyboard の添付プロパティである TargetName でアニメーションのターゲットを指定して、
TargetProperty で何のプロパティをアニメーションさせるか指定します。
アニメーションは、定義しただけでは再生されないので、再生するコードを追加したいと思い
ます。Page 読み込み時に呼び出される Loaded イベントハンドラを定義します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="Page_Loaded">
…
</Page>
コードビハインドで、Resources から Storyboard を取得して再生するコードを書きます。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
72
namespace XamlAdvApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Storyboard を取得して再生する
var storyboard = (Storyboard)this.Resources["MoveRectStoryboard"];
storyboard.Begin();
}
}
}
Storyboard に対して Begin メソッドを呼び出すとアニメーションが開始されます。実行すると
以下のように時間の経過とともに矩形が右へ移動していきます。
73
アニメーションの値の指定方法で一番単純なのは From と To を使って指定する方法になりま
す。この例では、From を省略(省略すると現在の値が開始の値となります)して To だけ指定
しています。そのため、結果として 10 から 100 まで 5 秒かけてアニメーションするようにな
っています。この他にも To のかわりに By を指定する方法があります。By は、From から By
の値だけ変化させるという意味になります。From が 10 で By が 20 の場合は 10 + 20 で 30 ま
で移動するという形になります。XAML の例を以下に示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="Page_Loaded">
<Page.Resources>
<Storyboard x:Key="MoveRectStoryboard">
<DoubleAnimation Storyboard.TargetName="Rect"
Storyboard.TargetProperty="(Canvas.Left)"
By="100"
Duration="0:0:5" />
</Storyboard>
</Page.Resources>
<Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Rectangle x:Name="Rect"
74
Width="100"
Height="100"
Fill="Red"
Canvas.Top="10"
Canvas.Left="10" />
</Canvas>
</Page>
このコードだとアニメーション開始時点で Canvas.Left が 10 で、5 秒かけて 110 まで移動する
という動きをします。
デフォルトでは、アニメーションは 1 度終了すると再度手動で実行するまで停止しています。
アニメーションに繰り返しを指定すると、指定した回数、指定した方法でアニメーションを繰
り返させることが出来ます。AutoReverse プロパティを指定すると、アニメーションが終了し
たあと、逆方向のアニメーションを再生するか指定できます。サンプルで示した矩形が右に移
動するアニメーションに指定すると、右に移動が完了したあと元の位置に向かって左方向にア
ニメーションするようになります。また、RepeatBehavior にアニメーションを繰り返す時間を
指定できます。ここで指定した時間だけ、アニメーションを再生し続けることが出来ます。
AutoReverse と組み合わせることで、行ったり来たりというアニメーションを実行させること
も出来ます。色に対して指定すると点滅させるという効果も持たせることが出来ます。
TimeSpan 型なので Duration プロパティと同様に「時:分:秒」の書式で指定しますが、Forever
を指定することで無限にアニメーションを再生し続けることが出来ます。 以下にアニメーショ
ンの繰り返しの指定例を示します。
<Storyboard x:Key="MoveRectStoryboard">
<DoubleAnimation Storyboard.TargetName="Rect"
Storyboard.TargetProperty="(Canvas.Left)"
By="100"
Duration="0:0:5"
RepeatBehavior="0:0:13"
AutoReverse="True"/>
</Storyboard>
この例では、アニメーションが 13 秒間繰り返しを続けます。
75
ここまでは、DoubleAnimation を例にアニメーションを説明してきました。UWP のアニメー
ションは、DoubleAnimation 以外にも以下の Animation が定義されています。
 PointAnimation
点のアニメーションを行います。
 ColorAnimation
色のアニメーションを行います。
8.2.1. キ ー フレ ー ム を 使 った ア ニ メー シ ョ ン
ここまでのアニメーションは、時間に応じて滑らかに値が変わっていくアニメーションでし
た。UWP では、このほかにキーフレームを使った、指定した時間にパッと切り替わるアニメ
ーションも提供しています。ObjectAnimationUsingKeyFrame を使って定義します。
ObjectAnimationUsingKeyFrame クラスの KeyFrames プロパティに対して指定した時間に、ど
の値を指定するのかを設定します。DiscreteObjectKeyFrame に KeyTime で時間、Value で値
を設定してアニメーションを定義します。以下に 2 秒で 50 に、5 秒で 150 に、10 秒で 250 に
パッパッと移動するアニメーションの定義を示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="Page_Loaded">
<Page.Resources>
<Storyboard x:Key="MoveRectStoryboard">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Rect"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:10">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:2">
<DiscreteObjectKeyFrame.Value>
76
50.0
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:5">
<DiscreteObjectKeyFrame.Value>
150.0
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:10">
<DiscreteObjectKeyFrame.Value>
250.0
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red"
Canvas.Top="10"
Canvas.Left="10" />
</Canvas>
</Page>
8.2.2. ThemeAnimation
テーマアニメーションを使うと、UWP のアプリっぽい標準のアニメーションを簡単に組み込
むことが出来ます。組み込みアニメーションには以下のようなものがあります。
 DragItemThemeAnimation
ドラッグされている項目要素に適用されているアニメーションをあらわします。
77
 DragOverThemeAnimation
ドラッグされている要素の下にある要素に適用されるアニメーションをあらわします。
 DropTargetItemThemeAnimation
ドロップ先となりうる要素に適用されるアニメーションです。
 FadeInThemeAnimation
フェードインするアニメーションをあらわします。
 FadeOutThemeAnimation
フェードアウトするアニメーションをあらわします。
 PointerDownThemeAnimation
タップまたはクリックするユーザー操作のアニメーションをあらわします。
 PointerUpThemeAnimation
要素をタップして指を離すときのアニメーションをあらわします。
 PopInThemeAnimation
ポップインをあらわすアニメーションをあらわします。
 PopOutThemeAnimation
ポップインが閉じたり削除されたりするときに適用されるアニメーションをあらわしま
す。
 RepositionThemeAnimation
位置が変更されたときのアニメーションをあらわします。
 SplitCloseThemeAnimation
スプリットのアニメーションで非表示にするアニメーションをあらわします。
 SplitOpenThemeAnimation
スプリットのアニメーションで表示するアニメーションをあらわします。
 SwipeBackThemeAnimation
スワイプ後に要素がもとに戻るアニメーションをあらわします。
78
 SwipeHintThemeAnimation
スワイプが可能であることを示すアニメーションをあらわします。
様々なアニメーションが定義されています。このアニメーションを使うことで他の UWP のア
プリのコントロールと同じような動きをさせることが出来ます。例えばフェードアウトさせる
アニメーションは以下のように定義します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="Page_Loaded">
<Page.Resources>
<Storyboard x:Key="FadeAnimation">
<FadeOutThemeAnimation TargetName="Rect" />
</Storyboard>
</Page.Resources>
<Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red"
Canvas.Top="10"
Canvas.Left="10" />
</Canvas>
</Page>
コードビハインドは以下のようになります。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
79
namespace XamlAdvApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Storyboard を取得して再生する
var storyboard = (Storyboard)this.Resources["FadeAnimation"];
storyboard.Begin();
}
}
}
これを実行すると矩形がフェードアウトして消える動きをします。
8.2.3. ThemaTransition
ThemeTransisiton は、コントロールが表示されたりドラッグされたりしたときに適用されるア
ニメーションになります。ThemeTransition を使うと、簡単に一般的な組み込みのアニメーシ
ョンを自動で再生させたりすることが出来ます。以下のような ThemeTransition があります。
 AddDeleteThemeTransition
ItemsControl の子要素が追加・削除されたときのアニメーションです。
 ContentThemeTransition
AddDeleteThemeTransition に追加して適用します。コンテンツに変化があるとき(よく
わからない)に適用されるアニメーションです。
 EdgeUIThemeTransition
小さい UI の変化のアニメーションです。(よくわからない)
80
 EntranceThemeTransition
コントロールが最初に表示されるときのアニメーションです。
 PaneThemeTransition
パネルの切り替えのアニメーションです。(よくわからない)
 PopupThemeTransition
ポップアップが表示されるときのアニメーションです。
 ReorderThemeTransition
ListView の要素の並び替えのアニメーションです。
 RepositionThemeTransition
コントロールの位置が変わったときのアニメーションです。
これらの ThemeTransition を適用するには、コントロールの Transitions プロパティか、
ItemsControl の ItemsContainerTransitions プロパティに設定します。前者はコントロール単品
に、校舎はコントロールの子要素に対して適用されます。例えば、表示されたタイミングでフ
ワッと表示されるようにするには以下のような XAML になります。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Hello world"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Button.Transitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
81
</Button.Transitions>
</Button>
</Grid>
</Page>
ItemsContainer の子要素が初期表示のタイミングでフワッと出てくるようにするには以下のよ
うになります。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ItemsControl>
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
<Button Content="Hello" />
<Button Content="Hello" />
<Button Content="Hello" />
<Button Content="Hello" />
<Button Content="Hello" />
</ItemsControl>
</Grid>
</Page>
ThemeTransition の特徴は、アニメーションの開始と終了まで自動で組み込まれている点で
す。特に理由がない場合は、ThemeTransition→ThemeAnimation→カスタムの Animation の順
番で採用を検討するとよいと思います。
82
8.3. Visual State Manager
ここでは、Visual State Manager について説明します。Visual State Manager は、名前のとおり
見た目の状態を管理するための機能です。もっと簡単に言ってしまうとアニメーションをグル
ーピングして管理するための機能になります。例えば、マウスがコントロールの上に乗った状
態では、こういうアニメーションをする、マウスがコントロールの上に無い場合は、こういう
アニメーションをするといったように、状態とアニメーションを紐づけます。
Visual State Manager は、VisualStateManager クラスの VisualStateGroups 添付プロパティで定
義します。VisualStateGroups 添付プロパティには VisualStateGroup を x:Name で名前を付け
て設定します。VisualStateGroup の中には VisualState を x:Name で名前を付けて設定します。
この VisualState の中にアニメーションで解説した Storyboard を設定して VisualState になった
ときのアニメーションを設定します。同じ VisualStateGroup 内の VisualState は、同時に設定
できないという特徴があります。例えば、A というグループに Hoge と Foo という名前の
VisualState があった場合、同時に Hoge と Foo のステートにすることは出来ません。同じタイ
ミングでステートを設定したい場合はグループをわける必要があります。A というグループに
Hoge、B というグループに Foo というステートがある場合は、Hoge と Foo ステートを同時に
設定することが出来ます。VisualState の切り替えは、VisualStateManager.GoToState メソッド
を使います。VisualState を切り替えるコントロールと、切り替え先の VisualState 名と、トラ
ンジション(後述します)の有無を指定します。
通常時は赤色で、マウスを上に持っていくと青色になる矩形を VisualStateManager で実現した
場合のコード例を以下に示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
83
<VisualStateGroup x:Name="CommonState">
<VisualState x:Name="Normal" />
<VisualState x:Name="Blue">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Rect"
Storyboard.TargetProperty="(Rect.Fill).(SolidColorBrush.Color)"
Duration="0:0:0"
To="Blue" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red"
PointerEntered="Rect_PointerEntered"
PointerExited="Rect_PointerExited"/>
</Grid>
</Page>
コードビハインドは以下のようになります。GoToState で Visual State Manager を切り替えて
いるだけです。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlAdvApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
84
}
private void Rect_PointerEntered(object sender,
Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Blue", true);
}
private void Rect_PointerExited(object sender,
Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Normal", true);
}
}
}
Visual State Manager は、Storyboard を使って VisualState の切り替え時に柔軟に表示を切り替
えることができます。しかし、単純な値の変更だけ(今回の赤から青の切り替えるといったよ
うなもの)のためにアニメーションを定義するのはメンドクサイです。アニメーションが不要
で、単純に値を設定するだけの場合には、Setter という機能を使うことで簡単に記述すること
が出来ます。Setter を指定するには、VisualState の Setters プロパティに Setter を指定しま
す。Setter には Target プロパティで指定するオブジェクトとプロパティを表すパスを設定しま
す。そして、Value に設定したい値を指定します。コード例を以下に示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonState">
<VisualState x:Name="Normal" />
85
<VisualState x:Name="Blue">
<VisualState.Setters>
<Setter Target="Rect.Fill"
Value="Blue" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red"
PointerEntered="Rect_PointerEntered"
PointerExited="Rect_PointerExited"/>
</Grid>
</Page>
実行すると、マウスを Rectangle の上に持って行ったタイミングで赤から青色に変わります。
これまで、Visual State Manager での VisualState の切り替えはコードから GoToState を実行し
て行ってきました。UWP の Visual State Manager では、StateTrigger を使うことで、XAML
で VisualState の切り替えを行うことが出来ます。StateTrigger は、VisualState の
StateTriggers プロパティに設定して使います。デフォルトでは AdaptiveTrigger という名前の
Window サイズによって VisualState を切り替える機能が提供されています。AdaptiveTrigger
の MinWindowWidth や MinWindowHeight を設定することで、その Window の幅や高さを超
えた時に、該当する VisualState に自動で切り替えてくれます。以下にコードを示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
86
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonState">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="Rect.Fill"
Value="Red" />
</VisualState.Setters>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="Blue">
<VisualState.Setters>
<Setter Target="Rect.Fill"
Value="Blue" />
</VisualState.Setters>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="750" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red" />
</Grid>
</Page>
上記コードだと、Window 幅が 750 以上の場合に青色の矩形になり、それより小さい場合は赤
色の矩形になります。AdaptiveTrigger のような StateTrigger は自作することが出来ます。こ
こでは作成方法は述べませんが、以下の GitHub 上のリポジトリに様々な便利な StateTrigger
が定義されています。カスタムの StateTrigger を作成するときの参考や、NuGet からインスト
ールも可能なので、使用してみると良いと思います。
87
WindowsStateTriggers
https://github.com/dotMorten/WindowsStateTriggers
最後にトランジションについて説明をします。トランジションは、VisualState が切り替わると
きに任意のアニメーションを挟むことが出来る機能になります。VisualStateGroup の
Transitions に設定できます。以下のような XAML を記述することで、Normal という
VisualState と Blue という VisualState の切り替え時に矩形が黒色になるというアニメーション
が実行されるようになります。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonState">
<VisualStateGroup.Transitions>
<VisualTransition>
<Storyboard>
<ColorAnimation Storyboard.TargetName="Rect"
Storyboard.TargetProperty="(Rect.Fill).(SolidColorBrush.Color)"
To="Black" />
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="Rect.Fill"
Value="Red" />
</VisualState.Setters>
88
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="Blue">
<VisualState.Setters>
<Setter Target="Rect.Fill"
Value="Blue" />
</VisualState.Setters>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="750" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Rect"
Width="100"
Height="100"
Fill="Red" />
</Grid>
</Page>
実行すると以下のようになります。幅が 750 より小さい時には以下のように赤色の矩形になり
ます。
89
そして、画面の幅を 750 以上にすると、以下のように 1 度矩形が黒くなるアニメーションが実
行されます。
その後青色の矩形になります。
90
8.4. Behavior
UWP の標準機能ではありませんが、Visual Studio と同時にインストールされる Blend for
Visual Studio でサポートされている Behavior というものがあります。Behavior は、UI に閉じ
たロジックを部品化するためのテクノロジになります。OSS で開発されている点も特徴です。
以下の GitHub で管理されています。
XAML Behaviors
https://github.com/Microsoft/XamlBehaviors
8.4.1. イ ン スト ー ル
Behavior は、NuGet で配布されていて、簡単に導入することが出来ます。
Microsoft.Xaml.Behaviors.Uwp.Managed パッケージを NuGet パッケージマネージャーからイ
ンストールします。
91
次に、XML 名前空間を定義します。以下のような Interactivity 名前空間と Core 名前空間を使
うのが一般的です。
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
8.4.2. 組 み 込み Behavior
Behavior には以下のものが定義されています。
 CallMethodAction
指定したメソッドを呼び出す Action
 ChangePropertyAction
指定したプロパティの値を変更する Action
 DataTriggerBehavior
データが指定した条件に合致したときに Action を実行する Trigger
 EventTriggerBehavior
指定したイベントが発行されたときに Action を実行する Trigger
 GoToStateAction
指定した VisualState に遷移する Action
 IncrementalUpdateBehavior
ListView などの ItemTemplate でレンダリングの順番を制御するための Behavior
 InvokeCommandAction
指定した Command を実行する Action
 NavigateToPageAction
指定した Page へ画面遷移する Action
 PlaySoundAction
指定した音楽を再生する Action
92
8.4.3. Behavior の 使 い 方
Behavior の使い方を説明します。Behavior は Behavior 単品で使用するものと、Trigger と
Action を組み合わせて使うものの 2 種類があります。組み込みの Behavior では
IncrementalUpdateBehavior が Behavior 単品で動作するもので、残りのものは
TriggerBehavior という名前で終わっているものが Trigger で、Action という名前で終わってい
るものが Action になります。TriggerBehavior は、条件を満たしたときに配下の Action を実行
するイメージです。
以下に Behavior の使用例を示します。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="PointerEntered">
<Core:ChangePropertyAction PropertyName="Background"
Value="Blue" />
</Core:EventTriggerBehavior>
<Core:EventTriggerBehavior EventName="PointerExited">
<Core:ChangePropertyAction PropertyName="Background"
Value="{ThemeResource
ApplicationPageBackgroundThemeBrush}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Grid>
</Page>
93
EventTriggerBehavior でイベントが発生したときに Action を実行するようにしています。そし
て、ChangePropertyAction で背景色を指定しています。上記のコードでは、ポインターが画面
内に入ったときに青色の背景色を指定して、ポインターが画面外に出たときに
ApplicationPageBackgroundThemeBrush で定義された色に変更しています。実行すると以下
のようになります。
実行直後は、通常の白色の画面が表示されます。
マウスを画面上に持ってくると青色に変化します。
94
このように、Behavior を使うことで簡単なプレゼンテーションロジックは XAML で定義する
ことが出来るようになります。例えば、TextBox に入力された値が p@ssw0rd のときに Button
が押せるようになる XAML は以下のようになります。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox x:Name="TextBox">
<Interactivity:Interaction.Behaviors>
95
<Core:DataTriggerBehavior Binding="{Binding Text, ElementName=TextBox,
UpdateSourceTrigger=PropertyChanged}"
ComparisonCondition="Equal"
Value="p@ssw0rd">
<Core:ChangePropertyAction TargetObject="{Binding ElementName=Button}"
PropertyName="IsEnabled"
Value="True" />
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding Text, ElementName=TextBox,
UpdateSourceTrigger=PropertyChanged}"
ComparisonCondition="NotEqual"
Value="p@ssw0rd">
<Core:ChangePropertyAction TargetObject="{Binding ElementName=Button}"
PropertyName="IsEnabled"
Value="False" />
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>
<Button x:Name="Button"
Content="Click" />
</StackPanel>
</Page>
GoToStateAction と組み合わせて Visual State Manager を切り替えることで、もっと複雑なこ
とも XAML だけで完結するようになります。
8.4.4. Behavior の 作 成
Behavior を使うことで、XAML で宣言的なロジックが定義できるようになります。Behavior
を作成することで、さらにロジックの幅をひろげることができます。
Behavior を作成する場合は、DependencyObject を継承して IBehavior を実装したクラスを定
義することになります。例えば、ボタンに設定可能で、クリック時にメッセージボックスを出
す Behavior の実装例を以下に示します。
using System;
96
using Microsoft.Xaml.Interactivity;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlAdvApp
{
public class AlertBehavior : DependencyObject, IBehavior
{
// Behavior のプロパティは添付プロパティとして定義すると
// Binding などが出来るので特に理由がない場合は添付プロパティがいい
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register(
"Message",
typeof(string),
typeof(AlertBehavior),
new PropertyMetadata("Hello world"));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
// IBehavior で定義されているプロパティ
public DependencyObject AssociatedObject { get; private set; }
// IBehavior で定義されているメソッド
// Behavior がコントロールに紐づけられたときに呼ばれる
public void Attach(DependencyObject associatedObject)
{
this.AssociatedObject = associatedObject;
var button = this.AssociatedObject as Button;
if (button != null)
{
97
button.Click += this.Button_Click;
}
}
// IBehavior で定義されているメソッド
// Behavior がコントロールから切り離されるときに呼ばれる
public void Detach()
{
var button = this.AssociatedObject as Button;
if (button != null)
{
button.Click -= this.Button_Click;
}
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var dialog = new MessageDialog(this.Message);
await dialog.ShowAsync();
}
}
}
コメントにも書いていますが、Behavior を作成するにあたり実装しなければならないのは以下
の 1 つのプロパティと 2 つのメソッドになります。
 AssociatedObject プロパティ
Behavior が割り当てられたオブジェクトを取得します。通常は後述する Attach プロパテ
ィの引数で渡されたオブジェクトを格納します。
 Attach メソッド
Behavior がオブジェクトに割り当てられたときに呼び出されます。初期化処理をここで行
います。
98
 Detach メソッド
Behavior のオブジェクトへの割り当てが解除されたときに呼び出されます。後始末処理を
ここで行います。
この Behavior は、通常の Behavior と同じように以下のように XAML で定義して使えます。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Click">
<Interactivity:Interaction.Behaviors>
<local:AlertBehavior Message="こんにちは世界" />
</Interactivity:Interaction.Behaviors>
</Button>
</StackPanel>
</Page>
実行してボタンを選択すると以下のようにメッセージボックスが表示されます。
99
TriggerBehavior の作成方法は、以下のような Actions プロパティを持つ Behavior を定義する
ことで作成可能です。
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace XamlAdvApp
{
// Actions をコンテンツプロパティとして設定する属性
[ContentProperty(Name = "Actions")]
public class ClickTriggerBehavior : DependencyObject, IBehavior
{
// Actions という名前の添付プロパティを作成する
public static readonly DependencyProperty ActionsProperty =
DependencyProperty.Register(
"Actions",
typeof(ActionCollection),
typeof(ClickTriggerBehavior),
new PropertyMetadata(null));
100
public ActionCollection Actions
{
get
{
// 値がなければ作成する
var result = (ActionCollection)GetValue(ActionsProperty);
if (result == null)
{
result = new ActionCollection();
SetValue(ActionsProperty, result);
}
return result;
}
}
public DependencyObject AssociatedObject { get; private set; }
public void Attach(DependencyObject associatedObject)
{
this.AssociatedObject = associatedObject;
var button = this.AssociatedObject as Button;
if (button != null)
{
button.Click += this.Button_Click;
}
}
public void Detach()
{
var button = this.AssociatedObject as Button;
if (button != null)
{
button.Click -= this.Button_Click;
}
}
101
private void Button_Click(object sender, RoutedEventArgs e)
{
// Actions を実行する
Interaction.ExecuteActions(this, this.Actions, e);
}
}
}
Action は、DependencyObject クラスを継承して IAction インターフェースを実装して作成し
ます。
using Microsoft.Xaml.Interactivity;
using Windows.UI.Popups;
using Windows.UI.Xaml;
namespace XamlAdvApp
{
public class AlertAction : DependencyObject, IAction
{
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register(
"Message",
typeof(string),
typeof(AlertAction),
new PropertyMetadata("Hello world"));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public object Execute(object sender, object parameter)
{
102
var dialog = new MessageDialog(this.Message);
var ignore = dialog.ShowAsync();
return null;
}
}
}
この Trigger と Action は以下のように定義して使うことができます。
<Page x:Class="XamlAdvApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Click">
<Interactivity:Interaction.Behaviors>
<local:ClickTriggerBehavior>
<local:AlertAction Message="こんにちは世界" />
</local:ClickTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
</StackPanel>
</Page>
実行してボタンを選択すると、以下のようにメッセージボックスが表示されます。
103
8.4.5. Behavior の 細 か い 使 い 方
Behavior の細かな使い方は、英語情報になりますが以下の GitHub のページから参照できま
す。一通り目を通しておくと XAML でできることの幅がひろがるのでお勧めです。
https://github.com/Microsoft/XamlBehaviors/wiki
上記サイトの右側のメニューの Behaviors Reference の箇所がリファレンスになります。
8.5. DataTemplate
UWP では、テンプレートという機能を使ってデータを柔軟に可視化することが出来ます。こ
こでは、そのための DataTemplate について説明します。DataTemplate は、名前の通りデータ
を表示するためのテンプレートになります。ContentControl と、それを継承した Button など
のコントロールや、ItemsControl と、それを継承した ListView や GridView や ListBox など
様々なコントロールで使用できます。
8.5.1. ContentControl 系 で の 使 用
最初に単一要素を表示するための ContentControl 系のコントロールでの使用方法について説明
します。ContentControl は、Content プロパティに任意のオブジェクトが設定可能です。コン
104
トロール以外を設定した場合は、ここで説明する DataTemplate を使って、表示方法が決定さ
れます。(DataTemplate が存在しない場合は ToString の結果が表示されます)
DataTemplate は、ContentControl 系のコントロールの場合は ContentTemplate プロパティに
設定します。例として、以下のような Person クラスがあるとします。
namespace XamlAdvApp
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
これを Content プロパティに設定した ContentControl を XAML で定義すると以下のようにな
ります。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ContentControl>
<local:Person Name="Tanaka"
Age="35" />
</ContentControl>
</Grid>
</Page>
このまま実行しても Person クラスの ToString の結果のクラス名が表示されます。ここに
DataTemplate を適用してみます。
105
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ContentControl>
<local:Person Name="Tanaka"
Age="35" />
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Page>
DataTemplate では、DataContext に ContentControl の Content に設定されたオブジェクトが
設定されているので、実行時データバインディングでプロパティの値をコントロールと紐づけ
ることが出来ます。上記のコードを実行すると以下のような結果になります。
106
コンパイル時データバインディングを使う場合は x:DataType 属性で、コンパイル時データバ
インディングで使用される型を指定する必要があります。コードを以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ContentControl>
<local:Person Name="Tanaka"
Age="35" />
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="local:Person">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Page>
実行結果は、実行時データバインディングと同じため省略します。
DataTemplate を再利用する場合は以下のように App クラスや Page クラスのリソースに
DataTemplate を定義して StaticResource マークアップ拡張で参照することができます。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
107
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="PersonTemplate"
x:DataType="local:Person">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ContentControl ContentTemplate="{StaticResource PersonTemplate}">
<local:Person Name="Tanaka"
Age="35" />
</ContentControl>
</Grid>
</Page>
DataTemplate には、状況に応じて選択する DataTemplate をプログラムで決定するための
DataTemplateSelector という機能があります。DataTemplateSelector クラスを継承して、
SelectTemplateCore メソッドをオーバーライドして DataTemplate の選択ロジックを記述しま
す。SelectTemplateCore メソッドは object item を受け取るものと object item,
DependencyObject container の 2 つの引数を受け取るものがあります。これらは、
ItemsControl.ItemsPanel が ItemsStackPanel または ItemsWrapGrid の場合、前者のメソッ
ドを使用します。ItemsPanel が VirtualizingStackPanel や WrapGrid などの別のパネルであ
る場合は、後者のメソッドを使用します。どちらのケースも対応できるように両方実装してお
くのが良いでしょう。例えば、Person クラスの Age プロパティの値によって選択する
DataTemplate を切り替える DataTemplateSelector の実装は以下のようになります。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlAdvApp
108
{
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate LessThan50Template { get; set; }
public DataTemplate Greater50Template { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if (item == null) { return null; }
var person = (Person)item;
if (person.Age <= 50)
{
return this.LessThan50Template;
}
else
{
return this.Greater50Template;
}
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject
container)
{
return this.SelectTemplateCore(item);
}
}
}
上記 DataTemplateSelector を使うには、ContentControl の ContentTemplateSelector プロパテ
ィに設定します。以下のような XAML になります。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
109
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Page.Resources>
<local:PersonDataTemplateSelector x:Key="PersonDataTemplateSelector">
<local:PersonDataTemplateSelector.LessThan50Template>
<DataTemplate x:DataType="local:Person">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="50 歳以下" />
</StackPanel>
</DataTemplate>
</local:PersonDataTemplateSelector.LessThan50Template>
<local:PersonDataTemplateSelector.Greater50Template>
<DataTemplate x:DataType="local:Person">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="50 歳より上" />
</StackPanel>
</DataTemplate>
</local:PersonDataTemplateSelector.Greater50Template>
</local:PersonDataTemplateSelector>
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ContentControl ContentTemplateSelector="{StaticResource
PersonDataTemplateSelector}">
<local:Person Name="Tanaka"
Age="35" />
</ContentControl>
<ContentControl ContentTemplateSelector="{StaticResource
PersonDataTemplateSelector}">
<local:Person Name="Kimura"
Age="55" />
</ContentControl>
110
</StackPanel>
</Page>
実行すると、Age のプロパティの値に応じて以下のように表示が切り替わります。
8.5.2. ItemsControl 系 で の 使 用
ItemsControl では、ItemTemplate が ContentControl の ContentTemplate に概要し、
ItemTemplateSelector が ContentControl の ContentTemplateSelector に該当します。
ItemsControl の ItemsSource プロパティに設定された要素に対して、ItemTemplate が適用さ
れて表示されます。
例えば、以下のような XAML があるとします。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ItemsControl x:Name="ItemsControlPeople">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<Border BorderThickness="1"
BorderBrush="Red">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
111
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Page>
この XAML のコードビハインドで以下のように ItemsSource に値を設定します。
using System.Linq;
using Windows.UI.Xaml.Controls;
namespace XamlAdvApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.ItemsControlPeople.ItemsSource = Enumerable.Range(1, 5)
.Select(x => new Person
{
Name = $"Tanaka {x}",
Age = 48 + x,
});
}
}
}
実行すると、以下のように ItemsControl に ItemTemplate が適用されて Border(枠をあらわす
コントロール)で囲まれた要素が 5 つ表示されます。
112
これを、DataTemplateSelector を使うようにするには ItemsControl の ItemTemplateSelector
を設定します。XAML の例を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ItemsControl x:Name="ItemsControlPeople">
<ItemsControl.ItemTemplateSelector>
<local:PersonDataTemplateSelector>
<local:PersonDataTemplateSelector.LessThan50Template>
<DataTemplate x:DataType="local:Person">
<Border BorderThickness="1"
113
BorderBrush="Gray">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</Border>
</DataTemplate>
</local:PersonDataTemplateSelector.LessThan50Template>
<local:PersonDataTemplateSelector.Greater50Template>
<DataTemplate x:DataType="local:Person">
<Border BorderThickness="1"
BorderBrush="Blue">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</Border>
</DataTemplate>
</local:PersonDataTemplateSelector.Greater50Template>
</local:PersonDataTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
</Grid>
</Page>
実行すると、以下のようにデータに応じて枠の色が変わります。
114
8.6. ControlTemplate
UWP のコントロールは、ControlTemplate という機能を使うことでコントロールの振る舞い
はそのままに、見た目を 100%カスタマイズすることが出来ます。コントロールの Template
プロパティにコントロールの見た目が定義されていて、その値を差し替えることでコントロー
ルの見た目を任意のものに変えることが出来ます。
8.6.1. ク リ ック 可 能 な テ キス ト の 作成
例として、クリックが可能なテキストを考えてみます。UWP でテキストを表すには TextBlock
というコントロールを使用しますが、TextBlock には Click イベントがありません。近い動作を
する PointerPressed などのイベントを使ってエミュレートする必要があります。Button の
Click と同じ動作をさせようとすると多少のコーディングが必要になります。Button のように
クリック可能な TextBlock を作りたい場合は、Button の見た目を TextBlock に差し替えるとい
う考えが UWP では簡単です。コード例を以下にしめします。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
115
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Hello world">
<Button.Template>
<!-- Button の見た目を差し替える -->
<ControlTemplate TargetType="Button">
<!-- 見た目は TextBlock で Text に Content の内容をバインドする -->
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource
Mode=TemplatedParent}}" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Page>
実行すると以下のような見た目になります。見た目上は TextBlock そのものですが、Click イベ
ントなど Button の動作がそのまま残っています。
116
8.6.2. コ ン トロ ー ル の Visual State
コントロールには、状態に応じて様々な Visual State が定義されています。例えば Button では
マウスオーバーの状態やマウスが乗っていない状態などです。この状態を確認するには、デフ
ォルトのスタイルをコピーする Visual Studio の機能を使うか、generic.xaml の定義を確認する
方法があります。
Visual Studio の機能を使うには、テンプレートを確認したいコントロールをデザイナ上で右ク
リックして「テンプレートの編集」→「コピーして編集」を選択します。
117
そうすると、展開された定義を Page や App.xaml の何処に展開するのかという選択肢が出てき
ます。そして展開先を選択するとコントロールのテンプレートが Style として展開されます。
例えば、Button にはマウスカーソルなどが上におかれたときの PointerOver という名前の
VisualState が定義されています。そして、通常時をあらわす Normal という名前の VisualState
が CommonStates という VisualStateGroup に定義されています。この VisualState を定義する
ことで、マウスなどのカーソルがコントロール上に来たときの見た目を変えることが出来ま
す。以下に PointerOver のときに文字の色が赤色になるように定義した XAML を示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
118
xmlns:local="using:XamlAdvApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="XamlAdvApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Hello world">
<Button.Template>
<!-- Button の見た目を差し替える -->
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="TextBlock.Foreground"
Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- 見た目は TextBlock で Text に Content の内容をバインドする -
->
<TextBlock x:Name="TextBlock"
Text="{Binding Content, RelativeSource={RelativeSource
Mode=TemplatedParent}}" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Page>
実行すると以下のようになります。
119
何もしてないときは黒色の文字になります。
マウスカーソルを上に持っていくと赤文字になります。
120
9. 代表的なコントロール
ここでは、UWP で使用できる代表的なコントロールについて紹介します。
9.1. レ イ アウ ト パ ネ ル
UWP では Panel を継承したコントロールを使って要素の並びを制御します。ここでは、代表
的な Panel を継承したコントロールについて紹介します。
9.1.1. StackPanel
StackPanel は、要素を縦や横に並べていくシンプルなコントロールです。デフォルト状態で
は、縦に並べていきます。以下の XAML を示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
121
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Button Content="Hello" />
<Button Content="Hello" />
<Button Content="Hello" />
<Button Content="Hello" />
<Button Content="Hello" />
</StackPanel>
</Grid>
</Page>
以下のように縦並びに表示されます。
122
StackPanel の Orientation プロパティに「Horizontal」を設定することで(デフォルトは
Vertical)横並びになります。
<StackPanel Orientation="Horizontal">
9.1.2. Grid
Grid は、画面を格子状に区切って、そこにコントロールを配置していきます。格子は、Grid
の RowDefinitions で行方向の区切りを指定して、ColumnDefinitions で列方向の区切りを指定
します。RowDefinitions の中では RowDefinition を使って行を定義していきます。
ColumnDefinitions の中では ColumnDefinition を使って列を定義していきます。定義された行
と列には Grid.Row 添付プロパティで行の位置を、Grid.Column で列の位置を、Grid.RowSpan
で何行占有するか、Grid.ColumnSpan で何列占有するかを指定します。格子内での配置につい
ては水平方向の配置をあらわす HorizontalAlignment で Center(中央寄せ)、Left(左寄
せ)、Right(右寄せ)、Stretch(全体占有)と、垂直方向の配置をあらわす
123
VerticalAlignment で Center(中央寄せ)、Bottom(下寄せ)、Top(上寄せ)、Stretch(全
体占有)を指定できます。
例として、2×2 の格子状に区切って、そこにボタンを配置してみます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Content="0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Button Content="0,1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Grid.Column="1" />
<Button Content="1,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Row="1" />
<Button Content="1,1"
Grid.Row="1"
124
Grid.Column="1"/>
</Grid>
</Page>
実行すると以下のように 2×2 の格子の中にボタンが配置されます。
デフォルトでは、RowDefinition や ColumnDefinition で定義した格子の幅は等間隔になりま
す。幅の指定方法には*と数字と Auto という 3 つがあります。
 *による指定(デフォルト)
余白を比率によって分割します。3*と 2*が指定された RowDefinition がそれぞれある場合
は、3:2 の比率で配置されます。
 数字による指定
ピクセル単位での幅の指定になります。
 Auto による指定
中に配置されたコントロールの幅によってサイズが決まります。
125
RowDefinition が Height プロパティで指定して ColumnDefinition が Width プロパティで、そ
れぞれ幅が指定できます。例として、画面左側にリストがあり、トップにアプリタイトルがあ
る画面の XAML を以下にしめします。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Sample application"
Style="{StaticResource TitleTextBlockStyle}"
Grid.ColumnSpan="2" />
<ListView Grid.Row="1"
Width="250">
<ListViewItem Content="Item1" />
</ListView>
<Border Grid.Row="1"
Grid.Column="1"
Background="Azure">
</Border>
</Grid>
126
</Page>
実行すると、以下のようになります。
Grid は、使いこなすと柔軟なレイアウトが組める非常に強力なコントロールです。是非使い方
をマスターしてください。
9.1.3. Canvas
Canvas は、Left、Top と ZIndex の絶対座標で位置を指定します。Left で左端からのピクセル
数、Top で上からのピクセル数、ZIndex は重なりあったときにどちらが上にくるかを指定しま
す。XAML の例を以下にしめします。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
127
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Canvas>
<Rectangle Fill="Red"
Width="100"
Height="100"
Canvas.Top="100"
Canvas.Left="100"
Canvas.ZIndex="1" />
<Button Content="Hello"
Canvas.Top="10"
Width="150"
Height="150"
Canvas.Left="10" />
</Canvas>
</Grid>
</Page>
実行すると以下のようになります。Rectangle に ZIndex=”1”を指定しているので、Button より
も Rectangle が上に来ている点が特徴です。
128
Canvas はシンプルなぶん高速に動作する点が特徴です。
9.1.4. RelativePanel
RelativePanel は、UWP で追加された新しいレイアウトパネルになります。XAML のネストを
深くしなくても柔軟にレイアウトを指定可能という点が特徴です。RelativePanel は、名前の通
り別のコントロールからの相対位置でコントロールを配置します。相対的な位置の指定は以下
のプロパティで行います。
 RelatviePanel.Above=”対象のコントロール名”
対象のコントロール名で指定したコントロールの上に配置されます。
 RelativePanel.Below=”対象のコントロール名”
対象のコントロール名で指定したコントロールの下に配置されます。
 RelativePanel.RightOf=”対象のコントロール名”
対象のコントロール名で指定したコントロールの右に配置されます。
129
 RelativePanel.LeftOf=”対象のコントロール名”
対象のコントロール名で指定したコントロールの左に配置されます。
 RelativePanel.AlignBottomWith=”対象のコントロール名”
対象のコントロール名で指定したコントロールと下の位置を合わせます。
 RelativePanel.AlignHorizontalCenterWith=”対象のコントロール名”
対象のコントロール名で指定したコントロールと水平方向の中央の位置を合わせます。
 RelativePanel.AlignLeftWith=”対象のコントロール名”
対象のコントロール名で指定したコントロールと左の位置を合わせます。
 RelativePanel.AlignRightWith=”対象のコントロール名”
対象のコントロール名で指定したコントロールと右の位置を合わせます。
 RelativePanel.AlignTopWith=”対象のコントロール名”
対象のコントロール名で指定したコントロールと上の位置を合わせます。
 RelativePanel.AlignVerticalCenterWith=”対象のコントロール名”
対象のコントロール名で指定したコントロールと垂直方向の中央の位置を合わせます。
このほかに、親になる Panel の位置に対して相対的に設定するプロパティがあります。これら
のプロパティは True を設定することで有効になります。
 RelativePanel.AlignBottomWithPanel
パネルの下端にコントロールの下を合わせるか指定します。
 RelativePanel.AlignHorizontalCenterWithPanel
パネルの水平方向の中心にコントロールの中心を合わせるか指定します。
 RelativePanel.AlignLeftWithPanel
パネルの左にコントロールの左を合わせるか指定します。
 RelativePanel.AlignRightWithPanel
パネルの右にコントロールの右を合わせるか指定します。
130
 RelativePanel.AlignTopWithPanel
パネルの上にコントロールの上を合わせるか指定します。
 RelativePanel.AlignVerticalCenterWithPanel
パネルの垂直方向の中央にコントロールの中心を合わせるか指定します。
RelativePanel のプロパティをいくつかつかってコントロールを配置した XAML を以下に示し
ます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="Origin"
Content="Origin"
RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True"/>
<Button Content="None" />
<Button Content="LeftOf"
RelativePanel.LeftOf="Origin" />
<Button Content="RightOf"
RelativePanel.RightOf="Origin" />
<Button Content="Above"
RelativePanel.Above="Origin" />
<Button Content="Below"
RelativePanel.Below="Origin" />
<Button Content="Bottom/RightWithPanel"
RelativePanel.AlignBottomWith="Origin"
RelativePanel.AlignRightWithPanel="True"/>
<Button Content="Center/BottomWithPanel"
131
RelativePanel.AlignHorizontalCenterWith="Origin"
RelativePanel.AlignBottomWithPanel="True"/>
</RelativePanel>
</Page>
実行すると以下のように表示されます。Origin を中心にして、相対的にコントロールが配置さ
れていることが確認できます。
9.2. Border
Border は、枠を提供するコントロールです。BorderThickness で枠の太さを指定して、
BorderBrush で枠線の太さを指定できます。また、Padding プロパティで内側の余白を指定し
たり、Background プロパティで背景色の塗りつぶしを指定できます。Border コントロールを
使うと簡単にコントロールをグルーピングできます。Border コントロールの使用例を以下に示
します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
132
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Border BorderThickness="2"
BorderBrush="Blue"
Padding="10"
Margin="10"
Background="Azure">
<Rectangle Fill="Red" />
</Border>
</Grid>
</Page>
以下のように赤色の矩形のまわりに枠線が表示されます。
133
Border の BorderThickness は Thickness 型で「左&右,上&下」や「左,上,右,下」の順番でそれ
ぞれの枠の太さを指定できます。以下のような XAML になります。
<Border BorderThickness="5,1,10,3"
BorderBrush="Blue"
Padding="10"
Margin="10"
Background="Azure">
<Rectangle Fill="Red" />
</Border>
実行すると以下のように枠線の太さが変わります。
9.3. TextBlock
シンプルなテキストを表示するためのコントロールです。Text プロパティに設定した文字列
か、Inlines プロパティに設定されたインラインテキスト要素を表示します。まず、簡単な Text
プロパティの設定例を示します。
134
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Hello world" />
</Grid>
</Page>
実行すると Hello world という文字列が表示されます。
Inlines プロパティを使うと Run オブジェクトや LineBreak オブジェクトを使って複数行の書
式付きテキストを表示できます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock>
<Run Text="Hello" />
<Underline>world</Underline>
<LineBreak />
135
<Bold>XAML</Bold>
<Italic>programming</Italic>
<Hyperlink NavigateUri="http://msdn.microsoft.com/">world</Hyperlink>
</TextBlock>
</Grid>
</Page>
実行すると以下のような表示になります。
9.4. Button
ボタンは、ユーザーがマウスやタッチで選択してコマンドを実行するための UI を提供しま
す。Button は Click イベントがあり、そのイベントを購読することで選択時の処理を記述でき
ます。Button は ContentControl を継承しているため、ContentControl で使用した
ControlTemplate などの機能を使うことが出来ます。単純な Button を置いて Click に応答する
コードは以下のようになります。
<Page x:Class="ControlApp.MainPage"
136
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Alert"
Click="Button_Click" />
</Grid>
</Page>
Click イベント発生時にアラートダイアログを出すには以下のようなコードビハインドを記述し
ます。
using System;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var dialog = new MessageDialog("Hello world");
await dialog.ShowAsync();
}
137
}
}
Click イベントのほかに、Command プロパティという ICommand インターフェースを実装し
たクラスを受け取るプロパティがあります。このプロパティに ICommand をセットすると、
ICommand の CanExecute が false を返す間は自動で押せない状態になり、CanExecute が true
を返すときには押せる状態になるということが出来ます。Button が押せる状態になったときに
選択されると ICommand の Execute メソッドが呼び出されます。以下のような ICommand を
実装したクラスを定義しておいて
public class AlertCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public async void Execute(object parameter)
{
var dialog = new MessageDialog("Hello world");
await dialog.ShowAsync();
}
}
Button の Command プロパティに設定することで Button を選択したときにダイアログが表示
されます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
138
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Alert">
<Button.Command>
<local:AlertCommand />
</Button.Command>
</Button>
</Grid>
</Page>
9.5. TextBox
TextBox は、文字列を入力するための UI を提供します。TextChanged イベントで入力テキス
トの変更を購読することができます。TextBox の Text プロパティを通して入力された文字列を
取得したり設定することができます。
Text プロパティとデータバインディングを行うときは Mode を TwoWay にすることで
TextBox からの入力をソースに反映できます。UpdateSourceTrigger を PropertyChanged に設
定することで、リアルタイムに変更をプロパティに反映できます。コンパイル時データバイン
ディングには UpdateSourceTrigger が無いため、フォーカスが外れたタイミングのみでの値の
同期が基本になります。
以下のような XAML で、コードビハインドのテキストを通じて TextBox の入力と TextBlock
の Text を同期させることができます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
139
<TextBox Text="{x:Bind InputText, Mode=TwoWay}"/>
<TextBlock Text="{x:Bind InputText, Mode=OneWay}"
Style="{StaticResource BodyTextBlockStyle}"/>
</StackPanel>
</Page>
コードビハインドは以下のようになります。
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private string inputText;
public string InputText
{
get { return this.inputText; }
set
{
this.inputText = value;
this.PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(InputText)));
}
}
}
}
140
実行すると以下のように TextBox のフォーカスが外れたタイミングでテキストが TextBlock に
同期されます。
9.6. CheckBox
CheckBox は、ユーザーに 2 択の選択肢を提供することが出来ます。IsChecked プロパティで
true か false でチェックがされているかいないかを取得できます。厳密にいうと IsChecked プ
ロパティは bool?型なので、true でも false でもない、不確定という null の選択肢もあります。
Checked イベントでチェックされたときのイベントを購読できます。Uncheked イベントでチ
ェックが外されたときのイベントを購読することが出来ます。
以下のように XAML で定義します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
141
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<CheckBox Content="Check me"
IsChecked="True" />
</Grid>
</Page>
以下のような見た目になります。
9.7. RadioButton
RadioButton は、複数の選択肢の中から 1 つを選ばせる UI を提供します。IsChecked プロパテ
ィでチェック状態を取得できる点は CheckBox と同じですが、同じ Panel 内に置かれたコント
ロールは、同時に 1 つしかチェックできないという特徴があります。以下に XAML を示しま
す。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Border BorderThickness="1"
BorderBrush="Black"
Padding="10">
<StackPanel>
<RadioButton Content="100 人以下" />
142
<RadioButton Content="500 人以下" />
<RadioButton Content="それ以上" />
</StackPanel>
</Border>
<Border BorderThickness="1"
BorderBrush="Black"
Padding="10">
<StackPanel>
<RadioButton Content="青" />
<RadioButton Content="黄" />
<RadioButton Content="赤" />
</StackPanel>
</Border>
</StackPanel>
</Page>
以下のような見た目になります。100 人以下、500 人以下、それ以上の中で 1 つ、青、黄、赤
の中で 1 つのみがチェック可能です。
143
同じ Panel 内にある CheckBox をグルーピングしたい場合は GroupName プロパティで明示的
に名前を付けることで同じ Panel 内や Panel 外の RadioButton をグルーピングできます。例え
ば、先ほどの XAML の RadioButton 全てに同じ GroupName を指定すると、6 個の
RadioButton の内、同時に選択できるのが 1 つに制限できます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Border BorderThickness="1"
BorderBrush="Black"
Padding="10">
144
<StackPanel>
<RadioButton Content="100 人以下"
GroupName="AGroup" />
<RadioButton Content="500 人以下"
GroupName="AGroup"/>
<RadioButton Content="それ以上"
GroupName="AGroup"/>
</StackPanel>
</Border>
<Border BorderThickness="1"
BorderBrush="Black"
Padding="10">
<StackPanel>
<RadioButton Content="青"
GroupName="AGroup" />
<RadioButton Content="黄"
GroupName="AGroup" />
<RadioButton Content="赤"
GroupName="AGroup" />
</StackPanel>
</Border>
</StackPanel>
</Page>
実行結果を以下に示します。
145
9.8. RepeatButton
RepeatButton は、押しっぱなしになったときに Click イベントを連続で発行する機能を持った
Button です。NumericUpDown のように数字を入力するようなコントロールで使われることが
多いコントロールになります。例えば、ボタンを押し続けるとカウントが上がっていくコード
は以下のようになります。
XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
146
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<RepeatButton Content="OK"
Click="{x:Bind Click}" />
<TextBlock Text="{x:Bind Count, Mode=OneWay}"
Style="{StaticResource BodyTextBlockStyle}"/>
</StackPanel>
</Page>
コードビハインドは以下のようになります。
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private int count;
public int Count
{
get { return this.count; }
set
{
this.count = value;
this.PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Count)));
}
}
147
public void Click()
{
this.Count++;
}
}
}
実行してボタンを押しっぱなしにするとカウントがインクリメントされていくことが確認でき
ます。
9.9. ToggleSwitch
ToggleSwitch は CheckBox と同じようにユーザーに ON/OFF の選択を提供するコントロール
です。Header プロパティで見出しを設定して、OnContent プロパティと OffContent プロパテ
ィで ON のときと OFF の時に表示されるコンテンツを指定できます。IsOn プロパティで
148
ON/OFF の状態を bool 型で取得できます。true のときが ON で false のときが OFF になりま
す。
XAML の例を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch Header="Option1"
OnContent="ON"
OffContent="OFF"
IsOn="True" />
<ToggleSwitch Header="Option1"
OnContent="ON"
OffContent="OFF"
IsOn="False" />
</StackPanel>
</Page>
実行すると以下のようになります。
149
9.10. ToggleButton
ToggleButton は、クリックをすると ON/OFF を切り替えることが出来るボタンです。Button
と同様に Content プロパティで表示コンテンツを指定して、IsChecked プロパティで選択状態
を取得または設定できあmす。IsChecked プロパティは、bool?型なので true(選択)、false(未
選択)、null(どちらでもない)状態を表すことが出来ます。XAML での使用例を以下に示し
ます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleButton Content="Button1" />
150
<ToggleButton Content="Button2"
IsChecked="True" />
</StackPanel>
</Page>
ToggleButton を 2 つ表示して、上が OFF の状態で下が ON の状態です。実行すると以下のよ
うに表示されます。
9.11. ComboBox
ComboBox は、ユーザーに対して複数の選択肢から 1 つの要素を選択する機能を提供するコン
トロールです。継承関係をたどっていくと ItemsControl を継承しているので、ItemsSource を
使ってコレクションを設定して、ItemTemplate で見た目を定義できます。選択された要素は、
SelectedItem で ItemsSource に設定された要素の中で選択されたオブジェクトのインスタンス
が取得できます。SelectedIndex で選択された要素のコレクション内でのインデックスが取得で
きます。使用例を以下に示します。
151
XAML は以下のようになります。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComboBox x:Name="ComboBox"
Header="Header"
ItemsSource="{x:Bind People}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock>
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Alert"
Click="{x:Bind Click}" />
</StackPanel>
</Page>
コードビハインドは以下のようになります。
using System;
using System.Linq;
using Windows.UI.Popups;
152
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
// ComboBox にバインドするデータ
public Person[] People { get; }
public MainPage()
{
this.InitializeComponent();
// 適当なデータを作っておく
this.People = Enumerable.Range(1, 10)
.Select(x => new Person
{
Name = $"tanaka {x}",
Age = 30 + x,
})
.ToArray();
}
public async void Click()
{
// 選択されてないときは何もしない
if (this.ComboBox.SelectedIndex == -1) { return; }
// 選択されてるときは、その情報を出す
var selectedPerson = (Person)this.ComboBox.SelectedItem;
var dialog = new MessageDialog(
$"{this.ComboBox.SelectedIndex}: {selectedPerson.Name}, {selectedPerson.Age}歳
");
await dialog.ShowAsync();
}
}
153
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
実行すると以下のようになります。要素を選択して、ボタンを選択するとダイアログが表示さ
れます。
9.12. CalendarDatePicker
CalendarDatePicker は、ドロップダウンでカレンダーを使ってユーザーに日付を選択する機能
を提供します。Date プロパティ(DateTimeOffset?型)で選択日を取得または設定でき、
DateFormat プロパティで日付のフォーマットを指定できます。PlaceholderText プロパティ
で、日付を選択してないときに表示するテキストを設定できます。使用例を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
154
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<CalendarDatePicker x:Name="CalendarDatePicker"
PlaceholderText="日付を選んでね"
DateFormat="{}{year.full}-{month.integer}-{day.integer}" />
<Button Content="Alert"
Click="{x:Bind Click}" />
</StackPanel>
</Page>
コードビハインドは以下のようになります。
using System;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
var dialog = new MessageDialog(
this.CalendarDatePicker.Date?.ToString("yyyy-MM-dd") ?? "未選択");
await dialog.ShowAsync();
}
}
155
}
実行すると以下のようになります。未選択状態でボタンを選択すると以下のようになります。
日付を選択しようとすると以下のようにカレンダーが表示されます。
156
日付を選択してボタンを選択すると、選択した日付が以下のようにアラートで表示されます。
9.13. CalendarView
CalendarView は、カレンダーを使って日付を選択する機能をユーザーに提供します。
SelectedDates プロパティで選択された日付を取得できます。SelectedDates プロパティは
IList<DateTimeOffset>型で複数選択に対応しています。SelectionMode プロパティに Single
を設定した場合は単一の日付の選択ですが、Multiple を設定した場合は複数の日付を選択可能
になります。
以下に使用例を示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
157
<CalendarView x:Name="CalendarView"
SelectionMode="Multiple"/>
<Button Content="Alert"
Click="{x:Bind Click}" />
</StackPanel>
</Page>
コードビハインドは以下のようになります。
using System;
using System.Linq;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
var lines = string.Join(Environment.NewLine, this.CalendarView
.SelectedDates
.Select(x => x.ToString("yyyy-MM-dd"))
.ToArray());
var dialog = new MessageDialog(lines);
await dialog.ShowAsync();
}
}
158
}
実行すると以下のようになります。
複数選択した日付が表示されていることが確認できます。
9.14. DatePicker
DatePicker は、ユーザーに対して日付をドロップダウンのような形式で選択する機能を提供し
ます。Date プロパティで DateTimeOffset 型として選択している値を取得または設定できま
す。また、MaxYear プロパティと MinYear プロパティを DateTimeOffset 型で指定すること
で、指定可能な年数の範囲を設定できます。
159
使用例の XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<DatePicker x:Name="DatePicker" />
<TextBlock Text="{x:Bind DatePicker.Date, Mode=OneWay}" />
</StackPanel>
</Page>
DatePicker で選択した日付を TextBlock にバインドしています。実行すると以下のようになり
ます。
160
日付の選択は、DatePicker を選択すると以下のようにドロップダウンが表示されて年と月と日
を、それぞれ選択可能になります。
9.15. TimePicker
TimePicker は、ドロップダウンの形式で時間を選択するための機能をユーザーに提供します。
TimeSpan 型の Time プロパティで選択された日付を取得したり設定できます。ClockIdentifier
プロパティに 12HourClock を指定すると午前・午後を指定する形のインターフェースになりま
す。デフォルトは 24 時間で時間を選択する 24HourClock が設定されています。
使用例の XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
161
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TimePicker x:Name="_24HourTimePicker" />
<TextBlock Text="{x:Bind _24HourTimePicker.Time, Mode=OneWay}" />
<TimePicker x:Name="_12HourTimePicker"
ClockIdentifier="12HourClock" />
<TextBlock Text="{x:Bind _12HourTimePicker.Time, Mode=OneWay}" />
</StackPanel>
</Page>
実行すると以下のようになります。
TimePicker を選択すると、以下のようにドロップダウンが展開されて ClockIdentifier に応じ
た選択肢が表示されます。以下の図は 12HourClock を設定した TimePicker の表示になりま
す。
162
9.16. FlipView
FlipView は左右にコンテンツを切り替えることが出来る ItemsControl になります。少数のデ
ータをユーザーに対して提供するのに向いています。ItemsControl のように ItemsSource にコ
レクションを設定して ItemTemplate を使って表示をカスタマイズできます。静的なコンテン
ツでよければ、以下のように FlipView の直下に FlipViewItem を設定することでも作成できま
す。コード例を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
163
<FlipView>
<FlipViewItem>
<TextBlock Text="Item1" />
</FlipViewItem>
<FlipViewItem>
<TextBlock Text="Item2" />
</FlipViewItem>
<FlipViewItem>
<TextBlock Text="Item3" />
</FlipViewItem>
</FlipView>
</Grid>
</Page>
実行すると以下のような表示になります。2 番目の Item2 を表示している状態になります。
164
左右の「<」や「>」のボタンを選択することで表示を切り替えることができます。あまりあり
ませんが、縦に表示を切り替えたいときは ItemsPanel に Orientation を Vertical に設定した
VirtualizingStackPanel にすることで実現できます。XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<FlipView>
<FlipView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</FlipView.ItemsPanel>
<FlipViewItem>
<TextBlock Text="Item1" />
</FlipViewItem>
<FlipViewItem>
<TextBlock Text="Item2" />
</FlipViewItem>
<FlipViewItem>
<TextBlock Text="Item3" />
</FlipViewItem>
</FlipView>
</Grid>
</Page>
以下のような表示になります。
165
切り替えボタンが上下になっているところで FlipView が横切り替えではなく縦切り替えになっ
ていることが確認できます。
ItemsSource にデータを設定する使用方法を以下に示します。まず、コードビハインドに表示
するためのデータを準備します。
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
private IEnumerable<Person> People { get; }
public MainPage()
166
{
this.InitializeComponent();
this.People = Enumerable.Range(1, 5)
.Select(x => new Person
{
Name = $"tanaka {x}",
Age = 30 + x,
});
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
この People プロパティを FlipView の ItemsSource にバインドして ItemTemplate を設定しま
す。XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<FlipView ItemsSource="{x:Bind People}">
<FlipView.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<Grid>
<TextBlock>
167
<Run Text="{x:Bind Name}" />
<Run Text="さん" />
<LineBreak />
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
</Page>
実行すると、以下のように表示されます。
ItemsSource に設定したデータに ItemTemplate が適用されて表示されていることが確認できま
す。
168
9.17. Frame
Frame はページ遷移をするための領域を提供します。実際のアプリケーション開発で Frame を
自分で作ることは、あまり多くありませんが、必ず意識しなければならないコントロールにな
ります。Frame は、App.xaml.cs を見ればわかるように Window.Current.Content というコント
ロールのルート要素として作成されています。
// App.xaml.cs から抜粋
Frame rootFrame = Window.Current.Content as Frame;
// ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返
さずに、
// ウィンドウがアクティブであることだけを確認してください
if (rootFrame == null)
{
// ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動
します
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: 以前中断したアプリケーションから状態を読み込みます
}
// フレームを現在のウィンドウに配置します
Window.Current.Content = rootFrame;
}
この後に MainPage へ遷移が行われています。
rootFrame.Navigate(typeof(MainPage), e.Arguments);
Frame の Navigate メソッドを使って、ページの型とページに渡す引数を指定して画面遷移がで
きます。この第 2 引数で渡した画面遷移の引数は、Page の OnNavigatedTo メソッドの引数の
169
NavigationEventArgs の Parameter プロパティで参照することが出来ます。コメントにある通
り OnNavigatedTo メソッドは他の画面から遷移してきたときに呼ばれる Page のメソッドで
す。それと対となる他ページへ移動したときに呼ばれる OnNavigatedFrom というメソッドも
あります。OnNavigatedTo メソッドで渡された引数を取得するコードを以下に示します。
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// 他ページから画面遷移してきたときに呼ばれる
base.OnNavigatedTo(e);
var param = e.Parameter;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// 他ページへ画面遷移したときに呼ばれる
base.OnNavigatedFrom(e);
}
}
}
この画面遷移時に渡せるパラメータは、int や string 型などの基本型や Guid 型などに限られて
います。自作クラスなどを渡すことはできないので注意してください。
170
Frame 内の Page では、Frame プロパティを通じて Page をホストしている Frame を取得でき
ます。これを使ってページ内のメソッドで Navigate メソッドを呼び出して別ページへ画面遷移
できます。以下のコードは OtherPage へ画面遷移する場合の例になります。
public void Click()
{
this.Frame.Navigate(typeof(OtherPage));
}
9.18. CommandBar
CommandBar は、ボタンや任意のコントロールを配置できるバーになります。特に Page クラ
スの TopAppBar プロパティと BottomAppBar プロパティに設定することで画面上部と下部に
固定されたコマンド領域を指定できます。CommandBar には PrimaryCommands プロパティ
(コンテンツプロパティとして設定されています)と SecondaryCommands プロパティがあ
り、その中に AppBarButton と AppBarSeparator と AppBarToggleButton を設定して使いま
す。AppBarButton がボタンで AppBarSeparator が区切りで AppBarToggleButton がチェック
ボックスのような役割になります。PrimaryCommands に設定した AppBarButton などはデフ
ォルトで表示され、SecondaryCommands に設定された AppBarButton などは、CommandBar
の右端に表示される「…」ボタンをクリックすることで、メニューが展開され表示されます。
AppBarButton と AppBarToggleButton は、Label プロパティと Icon プロパティを指定して使
います。Label プロパティは文字列情報で Icon プロパティはボタンに表示されるアイコンにな
ります。Icon プロパティは、Visual Studio のプロパティウィンドウで簡単に様々なアイコンが
指定可能です。SymbolIcon を使って定義済みの様々なアイコンを使用できます。FontIcon を
使うことで Segoe MDL2 Assets などのアイコンが定義されたフォントを使って様々なアイコン
を設定可能です。Icon プロパティのプロパティウィンドウでの編集の様子を以下に示します。
171
172
BottomAppBar に CommandBar を設定した場合の XAML の例を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Label="WiFi">
<AppBarButton.Icon>
<FontIcon Glyph="&#xE701;" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarSeparator />
<AppBarButton Icon="Accept"
Label="Accept"/>
<AppBarToggleButton Icon="AlignCenter"
Label="Center"/>
<CommandBar.SecondaryCommands>
<AppBarButton Label="SubMenu1" />
<AppBarButton Label="SubMenu2" />
</CommandBar.SecondaryCommands>
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
</Grid>
</Page>
初期状態で以下のように表示されます。
173
AppBarToggleButton は ToggleButton と同じように ON/OFF を切り開けることが出来ます。
ON にした状態を以下に示します。
174
画面右端の「…」ボタンを選択すると以下のように CommandBar が展開されて
SecondaryCommands に定義したメニューや AppBarButton の Label などが表示されます。
175
AppBarButton が押されたときの操作は Button と同じように Click イベントや Command プロ
パティに ICommand を設定することで可能です。
9.19. ListView と GridView
ListView/GridView は UWP アプリで、よくつかうコントロールです。ListView がデフォルト
で要素を縦に並べるのに対して GridView は要素を横に並べる点が特徴です。
以下に ListView と GridView の表示を比較するための簡単な XAML の例を示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
176
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListView>
<ListViewItem Content="Item1" />
<ListViewItem Content="Item2" />
<ListViewItem Content="Item3" />
</ListView>
<GridView Grid.Row="1">
<GridViewItem Content="Item1" />
<GridViewItem Content="Item2" />
<GridViewItem Content="Item3" />
</GridView>
</Grid>
</Page>
実行すると以下のようになります。ListView が縦で GridView が横に要素が並んでいることが
確認できます。
177
ListView は、要素が画面下部に到達するとスクロールバーでスクロールします。GridView
は、要素が画面の右端に到達すると折り返します。折り返して画面下部に要素が到達すると縦
スクロールが表示されます。これがデフォルトの挙動になります。
実際に、コレクションをバインドして動作を確認します。以下のように 1000 項目の Person ク
ラスを作成して ListView と GridView の ItemsSource にバインドします。
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public IEnumerable<Person> People { get; }
178
public MainPage()
{
this.InitializeComponent();
this.People = Enumerable.Range(1, 1000)
.Select(x => new Person
{
Name = $"tanaka {x}",
Age = 30 + x % 20,
});
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="PersonTemplate"
x:DataType="local:Person">
<Grid>
<TextBlock>
<Run Text="{x:Bind Name}" />
<Run Text="さん" />
179
<LineBreak />
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListView ItemsSource="{x:Bind People}"
ItemTemplate="{StaticResource PersonTemplate}">
</ListView>
<GridView Grid.Row="1"
ItemsSource="{x:Bind People}"
ItemTemplate="{StaticResource PersonTemplate}">
</GridView>
</Grid>
</Page>
実行すると以下のような表示になります。
180
ListView も GridView もデータの表示の並びが違うだけで、基本的に同じように使えます。こ
こからの説明では ListView を主体に説明していきますが、同じ方法が基本的にも GridView に
適用可能です。
9.19.1.選 択 項目 の 操 作
選択項目の操作には SelectedValue プロパティか SelectedIndex プロパティを使用します。
SelectedValue が選択項目のインスタンスそのものを取得または設定するのに対して、
SelectedIndex は、ListView に設定されているコレクションの中の何番目の項目が選択されて
いるかを取得または設定します。先ほどのプログラムの XAML を変更して以下のようにして
SelectedIndex プロパティと SelectedItem プロパティの動作を確認してみます。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
181
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="PersonTemplate"
x:DataType="local:Person">
<Grid>
<TextBlock>
<Run Text="{x:Bind Name}" />
<Run Text="さん" />
<LineBreak />
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView x:Name="ListView"
ItemsSource="{x:Bind People}"
ItemTemplate="{StaticResource PersonTemplate}">
</ListView>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind ListView.SelectedIndex, Mode=OneWay}"
Style="{StaticResource HeaderTextBlockStyle}"/>
<ContentControl Grid.Row="1"
Content="{x:Bind ListView.SelectedItem, Mode=OneWay}"
ContentTemplate="{StaticResource PersonTemplate}" />
182
</Grid>
</Grid>
</Page>
ListView を画面左側に配置して、画面右側に ListView の SelectedIndex プロパティと
SelectedItem プロパティをバインドしています。実行すると以下のようになります。初期状態
では、未選択状態のため SelectedIndex プロパティは-1 で、SelectedItem プロパティは null
(プロパティが取得できていない)になっています。
項目を選択すると、SelectedIndex プロパティと SelectedItem プロパティに値が連動して入っ
ていることが確認できます。
183
ListView は、SelectionMode プロパティで None、Single(規定値)、Multiple、Extended を指
定して選択時の動作をカスタマイズできます。Single のときは、今まで示した通り 1 つのみ項
目を選択できて、そのときの値は SelectedIndex プロパティと SelectedItem プロパティを使っ
てアクセスできます。Multiple と Extended を指定した場合複数項目の選択ができるようにな
ります。この場合は SelectedItems プロパティで選択された項目のコレクションが取得できま
す。このコレクションにプログラムから値を追加することで選択項目を制御することもできま
す。SelectionMode プロパティに Multiple を設定した場合は以下のように CheckBox が表示さ
れ複数項目が選択可能になります。
184
Extended を設定した場合は、Ctrl キーや Shift キーを押した状態でクリックすることで複数選
択が可能になります(電話では不向きな設定な気がします)
185
選択項目を表示するコード例を以下に示します。SelectionMode プロパティに Multiple を設定
して CommandBar の AppBarButton を Click したときに SelectedItems プロパティを使って選
択項目を取得しています。
XAML は以下のようになります。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Label="Alert"
Icon="Accept"
186
Click="{x:Bind Click}" />
</CommandBar>
</Page.BottomAppBar>
<Page.Resources>
<DataTemplate x:Key="PersonTemplate"
x:DataType="local:Person">
<Grid>
<TextBlock>
<Run Text="{x:Bind Name}" />
<Run Text="さん" />
<LineBreak />
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="ListView"
ItemsSource="{x:Bind People}"
ItemTemplate="{StaticResource PersonTemplate}"
SelectionMode="Multiple">
</ListView>
</Grid>
</Page>
コードビハインドは以下のようになります。
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace ControlApp
187
{
public sealed partial class MainPage : Page
{
public IEnumerable<Person> People { get; }
public MainPage()
{
this.InitializeComponent();
this.People = Enumerable.Range(1, 1000)
.Select(x => new Person
{
Name = $"tanaka {x}",
Age = 30 + x % 20,
});
}
public async void Click()
{
var message = string.Join(Environment.NewLine,
this.ListView.SelectedItems
.OfType<Person>()
.Select(x => x.Name)
.ToArray());
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
188
実行して、複数項目を選択して AppBarButton を押すと以下のように表示されます。
また、アイテムがタップされたときに、そのアイテムに対して操作をすることが可能です。
IsItemClickEnabled を True にすることで有効になります。SelectionMode を None にすると直
観的な動作になるので合わせて設定することをお勧めします。XAML の例を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
189
<ListView ItemsSource="{x:Bind People}"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="{x:Bind ItemClick}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<Grid>
<TextBlock>
<Run Text="{x:Bind Name}" />
<Run Text="さん" />
<LineBreak />
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
コードビハインドでは、ItemClick イベントハンドラでイベント引数の ClickedItem プロパティ
を使ってクリックされた項目の要素を取得できます。クリックされた項目の情報をメッセージ
ダイアログで表示するコードを以下に示します。
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
190
public IEnumerable<Person> People { get; }
public MainPage()
{
this.InitializeComponent();
this.People = Enumerable.Range(1, 10)
.Select(x => new Person
{
Name = $"tanaka {x}",
Age = 30 + x,
});
}
public async void ItemClick(object sender, ItemClickEventArgs e)
{
var target = (Person)e.ClickedItem;
var dialog = new MessageDialog($"{target.Name}さん {target.Age}歳");
await dialog.ShowAsync();
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
実行して、項目をタップすると以下のようになります。
191
9.20. Image
Image コントロールは画像を表示する機能を提供します。Image コントロールの Source プロパ
ティに ImageSource 型を指定することで画像を表示できます。XAML の場合は URL を指定す
ることで、画像の表示ができます。
9.20.1.XAML 内 での URL
XAML で指定できる URL は http や https で始まる Web 上のリソースを指し示すものの他に以
下のようなものがあります。
 ms-appx:///Images/Sample.png
アプリケーション内のリソースを表示します。上記 URL はアプリケーションのルートか
ら Images フォルダ内にある Sample.png を指し示します。
 ms-appdata:///local/Images/Sample.png
アプリケーションのローカルフォルダを指し示します。上記 URL はアプリケーションの
ローカルフォルダの Images フォルダ内にある Sample.png を指し示します。
192
 ms-appdata:///roaming/Images/Sample.png
アプリケーションのローミングフォルダを指し示します。上記 URL はアプリケーション
のローミングフォルダの Images フォルダ内にある Sample.png を指し示します。
 ms-appdata:///temp/Images/Sample.png
アプリケーションの一時保存フォルダを指し示します。上記 URL はアプリケーションの
一時保存フォルダの Images フォルダ内にある Sample.png を指し示します。
9.20.2.画 像 の表 示 例
例として、デフォルトのプロジェクトに必ず入っている Assets フォルダの下にある
SplashScreen.png を表示する XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Image Source="ms-appx:///Assets/SplashScreen.png" />
</Grid>
</Page>
実行すると、以下のようになります。
193
C#から設定する場合は、BitmapImage クラスを使用して以下のように書きます。まず、XAML
を修正して x:Name 属性をつけて C#からアクセスできるようにします。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Image x:Name="Image" />
</Grid>
</Page>
そして、コードビハインドで Uri クラスを BitmapImage のコンストラクタに渡して作成しま
す。
194
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Image.Source = new BitmapImage(new Uri("ms-appx:///Assets/
SplashScreen.png"));
}
}
}
実行結果は XAML で URL を指定したときと同じため省略します。
9.20.3.ユ ー ザー の 指 定 し たフ ァ イ ルの 表 示
ユーザーの指定したファイルを表示することも出来ます。ユーザーの指定したファイルは、
FileOpenPicker を使って取得できます。FileOpenPicker の使い方は以下のような流れになりま
す。
 FileOpenPicker のインスタンスを作成する
 FileTypeFilter に開きたいファイルの拡張子を登録する
 PickSingleFileAsync でファイルを開くダイアログを出す
 戻り値の StorageFile が null だったらキャンセルなので何もしない
 StorageFile の OpenReadAsync で IRandomAccessStream を取得する
 BitmapImage の SetSourceAsync で BitmapImage に読み込む
195
コード例を以下に示します。まず、XAML でコードビハインドから触るための x:Name を指定
した Image コントロールと、ファイルを開く操作のきっかけになる AppBarButton を置いた画
面になります。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="Accept"
Label="Open"
Click="{x:Bind Click}" />
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Image x:Name="Image" />
</Grid>
</Page>
コードビハインドでは、先ほどの FileOpenPicker の使い方の手順で示した通りのコードを書い
ています。
using System;
using Windows.Storage.Pickers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
196
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpg");
var file = await picker.PickSingleFileAsync();
if (file == null) { return; }
using (var reader = await file.OpenReadAsync())
{
var bitmap = new BitmapImage();
await bitmap.SetSourceAsync(reader);
this.Image.Source = bitmap;
}
}
}
}
実行すると以下のようになります。実行して、AppBarButton を選択するとファイルを開くダ
イアログが表示されます。
197
画像を選択すると画面に選択した画像が表示されます。
198
9.20.4.画 像 の表 示 時 の 拡 大方 法 方 法
Image コントロールは、明示的に幅と高さが指定されなかったときに画像をどのように拡大・
縮小して表示するかということが設定可能です。Stretch プロパティが設定項目で、None、
Fill、Uniform、UniformToFill の 4 項目が設定可能です。
 None
オリジナルのサイズを保持する。
 Fill
Image コントロールのサイズいっぱいに表示する。画像の縦横比は維持されない。
 Uniform(デフォルト値)
縦横比を維持しながら、Image コントロールに収まるように拡大・縮小する。
 UniformToFill
縦横比を維持しながら、Image コントロールいっぱいに表示されるように変更する。
199
動作を確認するために、Assets フォルダの下にある小さな正方形の StoreLogo.png を表示して
Stretch プロパティに上記値を適用して表示を比べてみます。
XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Row="0"
Grid.Column="0"
Source="ms-appx:///Assets/StoreLogo.png"
Stretch="None" />
<Image Grid.Row="1"
Grid.Column="0"
Source="ms-appx:///Assets/StoreLogo.png"
Stretch="Fill" />
<Image Grid.Row="0"
Grid.Column="1"
Source="ms-appx:///Assets/StoreLogo.png"
Stretch="Uniform" />
<Image Grid.Row="1"
Grid.Column="1"
200
Source="ms-appx:///Assets/StoreLogo.png"
Stretch="UniformToFill" />
</Grid>
</Page>
左上が None で、左下が Fill で、右上が Uniform で、右下が UniformToFill になります。実行
すると以下のようになります。
9.21. InkCanvas
InkCanvas コントロールは、ペンなどで線を引くことが出来る機能を提供します。InkCanvas
コントロールを画面に配置すると、それだけでペンによる入力を受け付けて線を引くことがで
きます。XAML を以下に示します。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
201
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<InkCanvas />
</Grid>
</Page>
実行すると以下のようにペンによる入力で線が引けます。
9.21.1.ペ ン 以外 で の 入 力 の受 け 付 け
IncCanvas はデフォルトではペンでの入力のみ受け付けます。InkCanvas の InkPresenter プロ
パティの InputDeviceTypes に CoreInputDeviceTypes 列挙体の Pen、Mouse、Touch を設定す
ることでペン以外のデバイスからの入力を受け付けることが出来ます。XAML でコードビハイ
ンドから触れるように名前を付けます。
<Page x:Class="ControlApp.MainPage"
202
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<InkCanvas x:Name="InkCanvas" />
</Grid>
</Page>
そして、コンストラクタで設定をします。
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.InkCanvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse |
CoreInputDeviceTypes.Touch;
}
}
}
これで実行するとペン以外のマウスやタッチでも入力を受け付けるようになります。
9.21.2.ペ ン の色 ・ 太 さ を 変え る
203
ペンの色と太さを変えるには InkDrawingAttributes を使用します。InkDrawingAttributes に各
種設定をして、InkPresenter の UpdateDefaultDrawingAttributes で更新を行います。例えば以
下のようなコードを、コードビハインドに記述します。
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.InkCanvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse |
CoreInputDeviceTypes.Touch;
// ペンの色や太さを変更する
var attr = new InkDrawingAttributes
{
Color = Colors.Red,
Size = new Size(10, 10),
};
this.InkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attr);
}
}
}
実行すると以下のようになります。
204
9.21.3.文 字 認識
文字認識には InkRecognizerContainer を使います。RecognizeAsync で InkCanvas の
InkPresenter の StrokeContainer と、認識対象を示す引数を渡してやれば結果が返ってきま
す。結果は、InkRecognitionResult の List なのでループで回して GetTextCandidates から認識
候補を取り出して処理をします。例として、第一候補のみをつなげた場合のコード例を示しま
す。
XAML は以下のようになります。
<Page x:Class="ControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
205
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="Accept"
Label="Recognize"
Click="{x:Bind Click}" />
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<InkCanvas x:Name="InkCanvas" />
</Grid>
</Page>
コードビハインドは以下のようになります。
using System;
using System.Linq;
using Windows.UI.Input.Inking;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
var irc = new InkRecognizerContainer();
var results = await irc.RecognizeAsync(this.InkCanvas.InkPresenter.StrokeContainer,
InkRecognitionTarget.All);
206
var dlg = new MessageDialog(results.Select(x =>
x.GetTextCandidates().First()).Aggregate((x, y) => x + y));
await dlg.ShowAsync();
}
}
}
実行して文字を書いて AppBarButton を選択すると以下のように文字認識結果が表示されま
す。
9.22. MapControl
MapControl は地図を表示する機能を提供します。MapControl を使うには Bing Map デベロッ
パーセンターというところでトークンを作る必要があります。Bing Map デベロッパーセンタ
ーは以下の URL からアクセスできます。
https://www.bingmapsportal.com/
207
My account の Create or view keys の Click here to create a new key の here の所のリンクをクリ
ックして必要情報を入力するとキーが作れます。
取得したキーを MapServiceToken に設定することで使えます。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
208
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Maps:MapControl x:Name="MapControl"
MapServiceToken="…Key…"/>
</Grid>
</Page>
実行すると、以下のように地図が表示されます。
地図はピンチイン・ピンチアウトやホイールスクロールなどで拡大縮小が出来ます。プログラ
ムから拡大縮小を行うには、ZoomLevel プロパティを指定します。20 で最大まで拡大で、1 で
一番縮小した状態になります。最大まで拡大した例は以下のようになります。
this.MapControl.ZoomLevel = 20;
マップを任意の場所に移動させるには、Center プロパティに、Geopoint クラスを指定しま
す。Geopoint クラスのコンストラクタには、BasicGeoposition クラスを指定して、Latitude プ
ロパティと Longitude プロパティで緯度経度を指定します。広島県庁を表示するコードは以下
のようになります。
209
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.MapControl.ZoomLevel = 20;
this.MapControl.Center = new Windows.Devices.Geolocation.Geopoint(
new Windows.Devices.Geolocation.BasicGeoposition
{
Latitude = 34.396560,
Longitude = 132.459622
});
}
}
}
実行すると以下のように広島県庁が最大拡大率で表示されます。
210
マップにアイコンを置くには MapIcon クラスを MapElements に追加することで実現できま
す。MapIcon クラスは、Location プロパティに場所を指定して、Title プロパティにアイコン
のタイトル、Image プロパティに表示するアイコンのイメージの
IRandomAccessStreamReference を指定します。広島県庁にアイコンを置くコードを以下に示
します。
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Maps;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
211
public MainPage()
{
this.InitializeComponent();
var ignore = SetupMapControlAsync();
}
private async Task SetupMapControlAsync()
{
this.MapControl.ZoomLevel = 20;
this.MapControl.Center = new Windows.Devices.Geolocation.Geopoint(
new Windows.Devices.Geolocation.BasicGeoposition
{
Latitude = 34.396560,
Longitude = 132.459622
});
// MapIcon
var imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/StoreLogo.png"));
var reference = RandomAccessStreamReference.CreateFromFile(imageFile);
var mapIcon = new MapIcon
{
Location = new Windows.Devices.Geolocation.Geopoint(
new Windows.Devices.Geolocation.BasicGeoposition
{
Latitude = 34.396560,
Longitude = 132.459622
}),
Title = "ひろしま!",
Image = reference
};
this.MapControl.MapElements.Add(mapIcon);
}
}
}
212
非同期処理を内部で使うため、コンストラクタに直接書くのではなく、コンストラクタから非
同期のメソッドを 1 段挟んで初期化処理をしています。上記コードを実行すると以下のような
表示になります。
マップには、任意の XAML コントロールを置くことも出来ます。コントロールを作成して
Location 添付プロパティを設定して Map コントロールの Children に追加することで表示でき
ます。コードを以下に示します。
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Maps;
using Windows.UI.Xaml.Media;
213
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
var ignore = SetupMapControlAsync();
}
private async Task SetupMapControlAsync()
{
this.MapControl.ZoomLevel = 20;
this.MapControl.Center = new Windows.Devices.Geolocation.Geopoint(
new Windows.Devices.Geolocation.BasicGeoposition
{
Latitude = 34.396560,
Longitude = 132.459622
});
// MapIcon
var imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/StoreLogo.png"));
var reference = RandomAccessStreamReference.CreateFromFile(imageFile);
var mapIcon = new MapIcon
{
Location = new Windows.Devices.Geolocation.Geopoint(
new Windows.Devices.Geolocation.BasicGeoposition
{
Latitude = 34.396560,
Longitude = 132.459622
}),
Title = "ひろしま!",
Image = reference
};
214
this.MapControl.MapElements.Add(mapIcon);
// XAML
var border = new Border
{
BorderBrush = new SolidColorBrush(Colors.Red),
BorderThickness = new Thickness(5),
Width = 100,
Height = 100
};
MapControl.SetLocation(border, new Windows.Devices.Geolocation.Geopoint(
new Windows.Devices.Geolocation.BasicGeoposition
{
Latitude = 34.396560,
Longitude = 132.459622
}));
this.MapControl.Children.Add(border);
}
}
}
実行すると、以下のようにアイコンに重なるように矩形が表示されます。
215
9.23. MediaElement
MediaElement コントロールは音楽や動画の再生機能を提供します。MediaElement でサポート
されるファイルの形式は以下のサイトを参照してください。
サポートされるオーディオとビデオの形式 (Windows ランタイム アプリ)
https://msdn.microsoft.com/ja-jp/library/hh986969.aspx
Assets フォルダの下に movie.wmv という動画ファイルを置いた場合、Source プロパティに以
下のように URL を指定することでファイルを再生することが出来ます。デフォルトでは自動
で再生が始まります。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
216
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<MediaElement Source="ms-appx:///Assets/movie.wmv" />
</Grid>
</Page>
実行すると以下のように表示されます。
自動再生をオフにするには、AutoPlay を Flase に設定することで可能です。また、
AreTransportControlsEnabled を True にすることで組み込みの UI を表示することが出来ま
す。XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
217
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<MediaElement Source="ms-appx:///Assets/movie.wmv"
AutoPlay="False"
AreTransportControlsEnabled="True"/>
</Grid>
</Page>
実行すると以下のように標準的な動画再生の UI が表示されるようになります。
また、IsMute プロパティでミュートの制御、IsFullScreen プロパティで全画面の制御、Volume
プロパティで 0~1 の範囲で音量の調整ができます。
9.24. Povot
Pivot コントロールは、FlipView のように複数のコンテンツの中から 1 つだけを表示する機能
を提供します。FlipView との違いは、Pivot コントロールの表示は以下のようにタブ表示に似
218
た感じで表示されるという点と FlipView が最後のコンテンツまで移動したあと最初のコンテン
ツに戻れない点に対して、Pivot は端のコンテンツから反対側の端のコンテンツへ移動できる
という点があります。
上記の表示を行う XAML は以下のようになります。基本的に、ほかの ItemsControl 系コント
ロールと同じになります。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot>
<PivotItem Header="Item1">
219
<TextBlock Text="Hello pivot." />
</PivotItem>
<PivotItem Header="Item2">
<TextBlock Text="Hello pivot." />
</PivotItem>
<PivotItem Header="Item3">
<TextBlock Text="Hello pivot." />
</PivotItem>
</Pivot>
</Grid>
</Page>
ItemsSource プロパティと ItemTemplate プロパティを使用した表示も対応しています。Pivot
には、ヘッダー項目もあるため HeaderTemplate も指定する必要があります。以下に使用例を
示します。まず、コードビハインドで Pivot に表示するコレクションを作成します。
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
public IEnumerable<Person> People { get; }
public MainPage()
{
this.InitializeComponent();
this.People = Enumerable.Range(1, 10)
.Select(x => new Person
{
Name = $"tanaka {x}",
Age = 30 + x,
});
220
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
そして、XAML で HeaderTemplate と ItemTemplate を設定した Pivot を作成します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot ItemsSource="{x:Bind People}">
<Pivot.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<Grid>
<TextBlock Text="{x:Bind Age}"
Style="{StaticResource HeaderTextBlockStyle}" />
</Grid>
</DataTemplate>
</Pivot.ItemTemplate>
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="local:Person">
<Grid>
<TextBlock Text="{x:Bind Name}"
Style="{StaticResource TitleTextBlockStyle}" />
</Grid>
221
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
</Grid>
</Page>
実行すると以下のようになります。
9.25. PasswordBox
PasswordBox コントロールは、認証画面のパスワード入力欄のようにユーザーの入力をマスク
して見えないようにする入力欄を提供します。XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
222
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<PasswordBox x:Name="PasswordBox" />
</StackPanel>
</Page>
実行して文字を打ち込むと以下のような表示になります。
PasswordBox は、Password プロパティで入力を取得できます。また、PasswordChar プロパテ
ィで入力をマスクしたときに表示される文字を指定できます。XAML での使用例を以下に示し
ます。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
223
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<PasswordBox x:Name="PasswordBox"
PasswordChar="*"/>
<TextBlock Text="{x:Bind PasswordBox.Password, Mode=OneWay}" />
</StackPanel>
</Page>
TextBlock に PasswordBox の Password プロパティをバインドして結果を表示しています。ま
た PasswordChar プロパティでパスワードをマスクしたときに表示される文字を*に変更して
います。実行して p@ssw0rd と入力したときの表示結果を以下に示します。
9.26. ProgressBar
ProgressBar は、ユーザーに処理の進捗状況を伝える機能を持ったコントロールです。
Minimum プロパティで、値の最小値を指定して、Maximum プロパティで最大値を指定しま
224
す。Value プロパティで、Minimum と Maximum の間の値を指定することで進捗度合を示すこ
とができます。また、進捗を明確に示すことができないが処理が進んでいることを示すための
アニメーションを表示するための IsIndeterminate プロパティがあります。このプロパティを
True にすることで、アニメーションが有効化されます。XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ProgressBar Minimum="0"
Maximum="100"
Value="50"
Margin="5"/>
<ProgressBar Minimum="0"
Maximum="1000"
Value="400"
Margin="5" />
<ProgressBar IsIndeterminate="True" />
</StackPanel>
</Page>
実行すると以下のようになります。一番上が 50%の進捗で、真ん中が 40%の進捗で、一番下
が、処理が進んでいることを示すアニメーションになります。
225
9.27. ProgressRing
ProgressRing も ProgressBar と同じで処理が実行中であることを示す UI を提供します。
ProgressRing は、進捗度合は示すことはできずに処理が進んでいることを示すアニメーション
を表示することができます。IsActive プロパティを False に設定することで非表示にすること
ができます。Visibility プロパティとの違いは、IsActive が False の場合は、非表示になります
がレイアウト上は残っているため、スペースを占有するという点です。XAML を以下に示しま
す。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
226
<ProgressRing IsActive="False"
Width="100"
Height="100" />
<ProgressRing IsActive="True"
Width="100"
Height="100"/>
</StackPanel>
</Page>
実行すると以下のようになります。上の ProgressRing は非表示ですが、領域は占有しているこ
とが確認できます。
9.28. ScrollViewer
ScrollViewer は、スクロール可能な領域を提供するコントロールです。水平方向と、垂直方向
のスクロールを提供するほかに、コンテンツの拡大・縮小の機能も提供します。ScrollViewer
は、HorizontalScrollMode プロパティ、VerticalScrollMode プロパティに、Auto、Enabled、
227
Disabled を設定することで、水平方向・垂直方向のスクロールの自動判定、有効、無効を設定
できます。さらに、HorizontalScrollBarVisibility プロパティ、VerticalScrollBarVisibility プロ
パティに Auto、Visible、Hidden、Disabled を設定することでスクロールバーの表示を自動判
定、表示、隠す、無効化を設定できます。
ScrollBarVisibility でスクロールバーを表示していても、ScrollMode を Disabled にすることで
スクロールバーが表示されていてもスクロールが出来ないという状態を作ることができます。
(通常はしませんが、こういう設定になっていてはまることがあります)また、
ScrollBarVisibility を Hidden にすることでスクロールバーを非表示にしたままスクロール可能
な領域を提供することができます。
また、ZoomMode プロパティを Enabled、Disabled を設定することで拡大・縮小を有効化、無
効化することができます。
各種プロパティの初期値は以下のようになっています。
 HorizontalScrollBarVisibility:Disabled
 VerticalScrollBarVisibility:Visible
 HorizontalScrollMode:Auto
 VerticalScrollMode:Auto
 ZoomMode:Disabled
例として、画像を縦横スクロールと拡大縮小が可能にする場合の XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
228
<ScrollViewer HorizontalScrollBarVisibility="Auto"
ZoomMode="Enabled">
<Image Source="ms-appx:///Assets/StoreLogo.png"
Width="700"/>
</ScrollViewer>
</Grid>
</Page>
実行すると以下のようになります。マウスの場合は以下のようにスクロールバーが表示されま
す。タッチの場合は、もう少し細いスクロールバーが表示されます。
2 本の指でピンチイン・ピンチアウトするか、マウスで Ctrl キーを押しながらホイールスクロ
ールすることで拡大縮小もできます。縮小した場合の実行例を以下に示します。
229
ScrollViewer には、HorizontalScrollBarVisibility のプロパティなどと同名の添付プロパティが
あります。GridView や ListView などのような内部に ScrollViewer を持ったコントロールに設
定することで、内部の ScrollViewer の挙動をカスタマイズできます。例えばデフォルトで縦方
向にスクロールする ListView ですが、以下のように ItemsPanel と ScrollViewer の添付プロパ
ティを使って横スクロールにすることができます。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<!-- 水平スクロールを有効にする -->
<ListView ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
230
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!-- 水平方向にコンテンツを並べる -->
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<Border Width="300"
Height="300"
Background="Red" />
<Border Width="300"
Height="300"
Background="Blue" />
<Border Width="300"
Height="300"
Background="Cyan" />
<Border Width="300"
Height="300"
Background="Gray" />
</ListView>
</Grid>
</Page>
実行結果を以下に示します。
231
9.29. Slider
Slider は、一定の範囲の値を入力するための UI を提供します。ProgressBar と同じように
Minimum プロパティと Maximum プロパティで値の範囲を設定し、Value プロパティで現在の
値を取得または設定できます。XAML の例を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Slider x:Name="Slider"
Minimum="50"
Maximum="150" />
232
<TextBlock Text="{x:Bind Slider.Value, Mode=OneWay}"
Style="{StaticResource HeaderTextBlockStyle}"/>
</StackPanel>
</Page>
実行すると以下のようになります。
9.30. WebView
WebView は、Web ブラウザの機能を提供します。Source プロパティに Uri を設定すること
で、そのページを表示することができます。例えば Bing を表示するような XAML は以下のよ
うになります。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
233
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<WebView Source="http://bing.co.jp" />
</Grid>
</Page>
実行すると以下のようになります。
NavigationStarting メソッドや NavigationCompleted イベントを購読することで画面遷移の状
態をハンドリングできます。NavigationStarting が画面遷移開始時に呼ばれるイベントで
NavigationCompleted イベントが、画面遷移が完了したときに呼ばれます。その他に
NavigationFailed イベントという画面遷移に失敗したときに呼ばれるイベントもあります。こ
のイベントを使うことで、画面遷移中に ProgressRing を表示するといったことが出来るように
なります。XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
234
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="Accept"
Label="Google"
Click="AppBarButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<WebView x:Name="WebView"
NavigationStarting="WebView_NavigationStarting"
NavigationCompleted="WebView_NavigationCompleted" />
<ProgressRing x:Name="ProgressRing"
Width="100"
Height="100"
Visibility="Collapsed"
IsActive="True" />
</Grid>
</Page>
コードビハインドを以下に示します。WebView は Navigate メソッドに Uri を渡すことで画面
遷移することができるので、それを使って Google を表示しています。
using System;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
235
public MainPage()
{
this.InitializeComponent();
}
private void WebView_NavigationStarting(WebView sender,
WebViewNavigationStartingEventArgs args)
{
this.ProgressRing.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private void WebView_NavigationCompleted(WebView sender,
WebViewNavigationCompletedEventArgs args)
{
this.ProgressRing.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void AppBarButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.WebView.Navigate(new Uri("http://google.co.jp"));
}
}
}
実行すると以下のようになります。読み込み中にプログレスリングが表示されていることが確
認できます。
236
WebView は Uri で示すリソースとしてアプリ内のリソースを指定することが出来ます。ms-
appx-web で始まる Uri がそれにあたります。例えばアプリのルートに html というフォルダを
用意して、その中の index.html を表示する場合は以下のようになります。
this.WebView.Navigate(new Uri(“ms-appx-web:///html/index.html”));
また、HTML 形式の文字列を指定することもできます。NavigateToString というメソッドを使
い引数に HTML を渡すことで、その HTML を表示することが出来ます。以下にコード例を示
します。
this.WebView.NavigateToString(@"
<html>
<head><title>Hello</title></head>
<body><b>Hello world</b></body>
</html>");
実行すると以下のようになります。
237
9.31. AugoSuggestBox
AutoSuggestBox は、検索用テキストボックスの UI を提供します。検索ワードに応じて検索候
補を表示したりといった一般的な検索用テキストボックスの機能を提供します。
AutoSuggestBox のデフォルトの見た目は TextBox と同じになります。以下のような XAML を
記述したとします。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<AutoSuggestBox />
</Grid>
238
</Page>
そうすると、TextBox と同じ見た目が表示されます。
検索ボックスっぽく右端にアイコンを表示することが出来ます。表示できるアイコンは、
AppBarButton と同じになります。プロパティウィンドウからアイコンで設定できます。実体
は、QueryIcon プロパティへの設定になります。
239
AutoSuggestBox で一番よく使うと思うのは虫眼鏡のアイコンになると思うので、QueryIcon プ
ロパティに Find を指定することが多いと思います。
<AutoSuggestBox QueryIcon="Find" />
実行すると以下のような見た目になります。
240
検索候補の表示は、TextChanged イベントで検索広報を ItemsSource に設定することで作成で
きます。以下のようなコードになります。まず XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<AutoSuggestBox QueryIcon="Find"
TextChanged="AutoSuggestBox_TextChanged"/>
</Grid>
</Page>
241
コードビハインドを以下に示します。入力を見て ItemsSource に適当な値を設定しています。
本番では、入力に応じて適切な値を設定すると良いと思います。イベント引数の Reason プロ
パティは、どういう経緯で入力が変化したのかを検出できます。今回はユーザーの入力により
変化したということを条件にしています。
using System;
using System.Linq;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
private Random Random { get; } = new Random();
public MainPage()
{
this.InitializeComponent();
}
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender,
AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = string.IsNullOrWhiteSpace(sender.Text) ?
null :
Enumerable.Range(1, 5 + this.Random.Next(5))
.Select(x => $"{sender.Text}の検索候補 {x}");
}
}
}
}
実行してテキストを入力すると、入力に応じて以下のような結果が表示されます。
242
検索の確定は QuerySubmitted イベントで拾うことが出来ます。QuerySubmitted イベントのイ
ベント引数の QueryText で入力文字列を取得できます。XAML を以下に示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<AutoSuggestBox QueryIcon="Find"
TextChanged="AutoSuggestBox_TextChanged"
QuerySubmitted="AutoSuggestBox_QuerySubmitted"/>
</Grid>
</Page>
コードビハインドを以下に示します。検索文字列をダイアログで表示しています。
243
using System;
using System.Linq;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace ControlApp
{
public sealed partial class MainPage : Page
{
private Random Random { get; } = new Random();
public MainPage()
{
this.InitializeComponent();
}
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender,
AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = string.IsNullOrWhiteSpace(sender.Text) ?
null :
Enumerable.Range(1, 5 + this.Random.Next(5))
.Select(x => $"{sender.Text}の検索候補 {x}");
}
}
private async void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender,
AutoSuggestBoxQuerySubmittedEventArgs args)
{
await new MessageDialog(args.QueryText).ShowAsync();
}
}
244
}
実行して選択候補を選ぶか、アイコンを選択することで以下のように表示されます。
AutoSuggestBox は、ItemTemplate を設定出来るため、ItemsSource に文字列ではないクラス
を要素とした IEnumerable<T>を設定して ItemTemplate を適用することで、検索候補を文字
列よりも情報量を豊富に表示することが出来ます。例えば、以下のようなコードになります。
XAML を以下に示します。Name と Age を持った Person クラスの存在を前提にしています。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<AutoSuggestBox QueryIcon="Find"
TextChanged="AutoSuggestBox_TextChanged"
QuerySubmitted="AutoSuggestBox_QuerySubmitted"
SuggestionChosen="AutoSuggestBox_SuggestionChosen">
<AutoSuggestBox.ItemTemplate>
245
<DataTemplate x:DataType="local:Person">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Source="ms-appx:///Assets/StoreLogo.png"
Grid.RowSpan="2" />
<TextBlock Text="{x:Bind Name}"
Style="{StaticResource BodyTextBlockStyle}"
Grid.Column="1"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}"
Grid.Column="1"
Grid.Row="1">
<Run Text="{x:Bind Age}" />
<Run Text="歳" />
</TextBlock>
</Grid>
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
</Grid>
</Page>
コードビハインドを以下に示します。
using System;
using System.Linq;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
246
namespace ControlApp
{
public sealed partial class MainPage : Page
{
private Random Random { get; } = new Random();
public MainPage()
{
this.InitializeComponent();
}
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender,
AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = string.IsNullOrWhiteSpace(sender.Text) ?
null :
Enumerable.Range(1, 5 + this.Random.Next(5))
.Select(x => new Person
{
Name = $"{sender.Text} {x}",
Age = 30 + x,
});
}
}
private async void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender,
AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion == null)
{
await new MessageDialog(args.QueryText).ShowAsync();
}
else
247
{
await new MessageDialog(((Person)args.ChosenSuggestion).Name).ShowAsync();
}
}
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender,
AutoSuggestBoxSuggestionChosenEventArgs args)
{
sender.Text = ((Person)args.SelectedItem).Name;
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
SuggestionChosen イベントで選択されたオブジェクトを実際のテキストにマッピングを行いま
す。QuerySubmitted イベントで、ChosenSuggestion に値が入っているか入っていないか(入
っている場合は選択肢が選ばれたという意味)を確認して処理を分岐しています。実行すると
以下のようになります。
実行して適当にテキストを入力した状態。
248
選択肢を選択すると、以下のように選んだ名前が表示されます。
9.32. SplitView
249
SplitView は左側にメニュー、右側にコンテンツを表示するコントロールです。SplitView の
Pane プロパティにメニュー部の UI を定義して、Content プロパティにコンテンツの UI を定
義します。Pane プロパティに定義したメニューは IsPaneOpen プロパティで True、False で表
示、非表示を切り替えることが出来ます。
Pane プロパティに定義した部分の表示方法は、DisplayMode プロパティで変更することが出
来ます。CompactOverlay、CompactInline、Overlay、Inline の 4 種類が設定できます。
Compact と名前がついているものは、IsPaneOpen プロパティが False の場合でも
CompactPaneLength プロパティで指定したサイズだけ表示されます。デフォルトは 48 なので
48 ピクセル表示されます。Overlay と名前がついているものは、Content プロパティを覆い隠
すように表示されます。Inline と名前がついているものは、Content プロパティの左側にイン
ラインで表示されます。つまり、Pane が表示されると、そのぶん Content プロパティで定義
した UI は右にずれます。
9.32.1.ハ ン バー ガ ー メ ニ ュー の 実 装
SplitView は、ハンバーガーメニューを実装するのに、よく使われます。ToggleButton の
IsChecked プロパティと SplitView の IsPaneOpen プロパティを Binding することで、それら
しく表示することが出来ます。以下に Pane プロパティ部分に ListView を使用した XAML の
例を示します。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ControlApp.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<SplitView IsPaneOpen="{Binding IsChecked, ElementName=ToggleButtonSplitView,
Mode=TwoWay}"
DisplayMode="CompactOverlay">
<SplitView.Pane>
<Grid>
250
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="ToggleButtonSplitView"
FontFamily="{ThemeResource
SymbolThemeFontFamily}"
Content="&#xE700;"
Width="48"
Height="40" />
<TextBlock Text="Select menu"
Style="{StaticResource TitleTextBlockStyle}"
Margin="5,0" />
</StackPanel>
<ListView x:Name="ListView"
SelectionMode="Single"
SelectedIndex="0"
Grid.Row="1">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding"
Value="0" />
<Setter Property="Margin"
Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListViewItem>
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Accept"
Width="48" />
<TextBlock Text="Menu 1"
Style="{StaticResource BodyTextBlockStyle}" />
</StackPanel>
</ListViewItem>
251
<ListViewItem>
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Find"
Width="48" />
<TextBlock Text="Menu 2"
Style="{StaticResource BodyTextBlockStyle}" />
</StackPanel>
</ListViewItem>
<ListViewItem>
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Edit"
Width="48" />
<TextBlock Text="Menu 3"
Style="{StaticResource BodyTextBlockStyle}" />
</StackPanel>
</ListViewItem>
</ListView>
</Grid>
</SplitView.Pane>
<ContentControl Content="{x:Bind ListView.SelectedIndex, Mode=OneWay}"
FontSize="48" />
</SplitView>
</Grid>
</Page>
ハンバーガーメニューのアイコンは SymbolThemeFontFamily で定義されたフォントの
&#xE700;で表示することが出来ます。実行すると以下のようになります。
252
ToggleButton をクリックすることで、Pane プロパティに定義した UI が表示されます。
253
10. 共有
UWP では、アプリ間でデータを共有するための仕組みが提供されています。この仕組みを使
うことで、簡単にアプリ間でテキストや画像などといったファイルをやりとりして動作するこ
とが出来ます。
10.1.1.共 有 の送 信 側 の 作 成
共有の送信側を作成するには、DataTransferManager の DataRequested イベントを実装しま
す。DataTransferManager クラスは、GetForCurrentView 静的メソッドで取得することが出来
ます。アプリケーション全体で同じものを共有するなら App クラスの OnLaunch メソッド
で、ページ単位で共有を有効化、無効化したい場合は Page の OnNavigatedTo でイベントを購
読して、OnNavigatedFrom でイベントの購読解除を行うと良いでしょう。ページで購読する場
合は、以下のようなコードイメージになります。
protected override void OnNavigatedTo(NavigationEventArgs e)
254
{
base.OnNavigatedTo(e);
var dtm = DataTransferManager.GetForCurrentView();
dtm.DataRequested += this.DataTransferManager_DataRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var dtm = DataTransferManager.GetForCurrentView();
dtm.DataRequested -= this.DataTransferManager_DataRequested;
}
private void DataTransferManager_DataRequested(DataTransferManager sender,
DataRequestedEventArgs args)
{
// TODO : ここに共有コンテンツを提供する処理を書く
}
DataRequested イベント引数の Request プロパティで DataRequest クラスのインスタンスが取
得できます。このクラスの Data プロパティで DataPackage クラスのインスタンスが取得でき
ます。この DataPackage クラスにデータを設定したりすることで共有するコンテンツを指定で
きます。DataPackage クラスには様々なデータを渡すためのメソッドがあります。以下のよう
なデータがサポートされています。
 テキスト(SetText メソッド)
 URI(SetUri メソッド)
 HTML(SetHtmlFormat メソッド)
 RTF(SetRtf メソッド)
 ビットマップ(SetBitmap メソッド)
 ファイル(SetStorageItems メソッド)
 カスタムデータ(SetData メソッド)
255
それぞれ、専用の設定用メソッドが提供されています。データを設定する他に、DataPackage
クラスの Properties クラスの Title を設定する必要があります。これは共有を説明するための
タイトルのテキストになります。より詳細な説明を設定するための Description プロパティも
提供されています。特に理由の無い限りは Description プロパティも設定するのが良いでしょ
う。
共有のための UI を表示するメソッドがあります。DataTransferManager クラスの
ShowShareUI 静的メソッドがそれになります。このメソッドを呼び出すことで、共有のための
UI が表示され、結果として DataTransferManager クラスの DataRequested メソッドが呼び出
されます。
簡単な入力テキストを共有するコード例を以下に示します。まず、XAML に共有用のテキスト
を設定する TextBox を置いて、共有を行うためのアプリバーボタンを置いているだけのシンプ
ルな画面を構築します。
<Page
x:Class="ShareApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ShareApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="ReShare"
Label="Share"
Click="{x:Bind ShareClick}"/>
</CommandBar>
</Page.BottomAppBar>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Text="{x:Bind InputText, Mode=TwoWay}"/>
</StackPanel>
</Page>
256
コードビハインドで、データの共有のためのイベントのセットアップや共有処理を行います。
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace ShareApp
{
public sealed partial class MainPage : Page
{
public string InputText { get; set; }
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var dtm = DataTransferManager.GetForCurrentView();
dtm.DataRequested += this.DataTransferManager_DataRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var dtm = DataTransferManager.GetForCurrentView();
dtm.DataRequested -= this.DataTransferManager_DataRequested;
}
private void DataTransferManager_DataRequested(DataTransferManager sender,
DataRequestedEventArgs args)
{
if (string.IsNullOrWhiteSpace(this.InputText))
{
257
return;
}
args.Request.Data.Properties.Title = "入力テキストの共有";
args.Request.Data.Properties.Description = "入力したテキストを共有します。";
args.Request.Data.SetText(this.InputText);
}
public void ShareClick()
{
DataTransferManager.ShowShareUI();
}
}
}
実行してテキストを入力して共有ボタンを選択すると以下のように共有の UI が表示されま
す。
258
共有の UI の上部に設定した Title と Description が表示されていることが確認できます。ここ
から例えば Twitter アプリを選択することで以下のようにツイート画面に共有したテキストが
表示されます。
共有するためのデータを用意するときに IO が発生するなど、非同期のメソッドを呼び出す場
合のために、DataRequest メソッドでは GetDeferral メソッドが提供されています。この戻り
値の Complete メソッドを呼び出すことで共有に必要な処理が完了したことを外部に通知でき
ます。例として、DataRequested イベントでアプリ内のリソースを共有する場合の例を以下に
示します。
private async void DataTransferManager_DataRequested(DataTransferManager sender,
DataRequestedEventArgs args)
{
var d = args.Request.GetDeferral();
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/StoreLogo.png"));
args.Request.Data.Properties.Title = $"{file.Name}の共有";
args.Request.Data.SetStorageItems(new[] { file });
d.Complete();
}
このコードを実行すると、以下のようになります。
259
共有は、共有の UI でアプリが選択されないと共有のために用意したデータは無駄になってし
まいます。そのため、毎回共有のたびに巨大なデータを作るようなケースは操作が重たくなっ
てしまい、ユーザーにとって悪い体験を提供してしまうことになります。このような時のため
に、データが必要になったタイミングで呼び出されるデリゲートを渡すことができます。使用
するメソッドは SetDataProvider メソッドになります。コード例を以下に示します。
private void DataTransferManager_DataRequested(DataTransferManager sender,
DataRequestedEventArgs args)
{
args.Request.Data.Properties.Title = "アプリ内リソースの共有";
args.Request.Data.SetDataProvider(StandardDataFormats.Bitmap, async (e) =>
{
var d = e.GetDeferral();
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/StoreLogo.png"));
var reference = RandomAccessStreamReference.CreateFromFile(file);
e.SetData(reference);
d.Complete();
});
}
260
10.1.2.共 有 の受 信 側 の 作 成
共有の受信側になるには、Package.appxmanifest ファイルを開いて「宣言」のタブの使用可能
な宣言で「共有ターゲット」を追加します。共有で受信可能なファイルの形式かデータ形式を
設定します。データ形式は「新規追加」ボタンを選択して Text などのデータ形式を入力する
ことで設定します。URI、Bitmap、StorageItems などを指定したり、独自のデータ形式を指定
可能です。独自のデータ形式を指定する場合は、送信側もそれに対応していなければなりませ
ん。以下は、Text を指定した場合の画面になります。
サポートされるファイルの種類も同様に「新規作成」ボタンを押して.txt などの拡張を入力し
ます。以下は、.txt を指定した場合の画面になります。
ここで設定した内容に応じて、共有の UI の選択肢にアプリが表示されます。アプリが選択さ
れると、App クラスの OnShareTargetActivated メソッドがコールバックされます。ここで共
有時の処理を行います。例えば Frame を作成して、共有用の UI ページへ遷移します。このと
き、イベント引数の ShareOperation プロパティは、共有の完了を通知したりするのに必要なの
261
で、画面遷移の引数に渡してページに引き渡します。通常画面遷移では、画面遷移の引数にこ
ういったオブジェクト型は渡しませんが、画面遷移履歴を保持しない共有の UI の場合に限っ
て言えば選択肢としてありだと考えています。(というか後述する公式サンプルプログラムが
そういう実装になっている)
例として ShareTargetPage というページを作ります。XAML を以下に示します。
<Page x:Class="ShareApp.ShareTargetPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ShareApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Header="共有されたテキスト"
Text="{x:Bind SharedText, Mode=TwoWay}" />
<Button Content="Execute"
Click="Button_Click" />
</StackPanel>
</Page>
コードビハインドを以下に示します。
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.DataTransfer.ShareTarget;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
262
namespace ShareApp
{
public sealed partial class ShareTargetPage : Page, INotifyPropertyChanged
{
public ShareTargetPage()
{
this.InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private ShareOperation ShareOperation { get; set; }
private string sharedText;
public string SharedText
{
get { return this.sharedText; }
set
{
this.sharedText = value;
this.PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(SharedText)));
}
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
this.ShareOperation = (ShareOperation)e.Parameter;
if (this.ShareOperation.Data.Contains(StandardDataFormats.Text))
{
this.SharedText = await this.ShareOperation.Data.GetTextAsync();
}
else if (this.ShareOperation.Data.Contains(StandardDataFormats.StorageItems))
{
263
var items = await this.ShareOperation.Data.GetStorageItemsAsync();
var file = items.OfType<IStorageFile>().FirstOrDefault();
if (file != null)
{
this.SharedText = await FileIO.ReadTextAsync(file);
}
}
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Delay(3000); // 受信したデータを使って何かやる
this.ShareOperation.ReportCompleted();
}
}
}
OnNavigatedTo メソッドで受信したデータが Text か StorageItems の場合に内容を
SharedText に格納しています。Button の選択時に 3 秒待って共有を完了しています。通常、
ここで受信したデータを使って何かやるのが一般的です。(Twitter クライアントならツイー
トするなど)最後に、App クラスの OnShareTargetActivated で画面遷移処理を行います。コ
ードを以下に示します。
protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
var f = new Frame();
Window.Current.Content = f;
f.Navigate(typeof(ShareTargetPage), args.ShareOperation);
Window.Current.Activate();
}
実行して、テキストファイルや文字列を共有すると以下のように共有の一覧にアプリが表示さ
れます。
264
アプリを選択すると、以下のように ShareTargetPage が表示されます。
265
Execute ボタンを選択すると、3 秒まって共有アプリが終了することが確認できます。
11. バックグラウンドタスク
バックグラウンドタスクは、アプリケーションが起動していないときや中断時にも OS によっ
てバックグラウンドで実行されるプログラムです。バックグラウンドタスクは Windows ラン
タイムプロジェクトで作成された IBackgroundTask インターフェースを実装したクラスで
Package.appxmanifest と BackgroundTaskBuilder クラスによって設定された条件で起動しま
す。
バックグラウンドタスクは、一般的に軽量なタスクをこなすために使用されます。常時実行し
て高負荷な処理をするためのものではないので注意してください。バックグランドタスクは、
ウォールクロック時間で 30 秒間起動することが許されています。(ウォールクロック時間は
実際の時間だと思います)
11.1.1.バ ッ クグ ラ ウ ン ド タス ク の 作成 と 登 録
一番簡単なバックグラウンドタスクであるタイマーのバックグラウンドの作成を通してバック
グラウンドタスクの作成から登録、実行までの流れを見ていきます。まず、バックグラウンド
タスクを作成するために、以下のプロジェクトを作成します。
 BackgroundTaskSampleApp(空白のアプリ(ユニバーサル Windows))
 BackgroundTaskSampleApp.Tasks(Windows ランタイムコンポーネント(ユニバーサル
Windows))
プロジェクトを作成したら、BackgroundTaskSampleApp から
BackgroundTaskSampleApp.Tasks プロジェクトへの参照を追加します。
BackgroundTaskSampleApp.Tasks プロジェクトの Class1 クラスを TimerBackgroundTask にリ
ネームして以下のコードを記述します。
using System.Diagnostics;
using Windows.ApplicationModel.Background;
266
namespace BackgroundTaskSampleApp.Tasks
{
public sealed class TimerBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
Debug.WriteLine(“Hello world background task.”);
}
}
}
次に、BackgroundTaskSampleApp プロジェクトの Package.appxmanifest を開いて「宣言」タ
ブを開きます。使用可能な宣言から「バックグラウンド タスク」を選択して追加します。プロ
パティが表示されるので「サポートされるタスクの種類」でタスクの種類を設定します。今回
はタイマーなので「タイマー」にチェックを入れます。次に「エントリポイント」でタスクの
クラス名を指定します。今回は「BackgroundTaskSampleApp.Tasks.TimerBackgroundTask」
を指定します。
267
最後に、コードでバックグラウンドタスクの登録を行います。App クラスの OnLaunched メソ
ッドの先頭で以下のコードを記述します。
// ステータスチェック
var status = await BackgroundExecutionManager.RequestAccessAsync();
if (status != BackgroundAccessStatus.Denied)
{
// 存在チェック
if (!BackgroundTaskRegistration.AllTasks.Values.Any(x => x.Name == "Sample timer task"))
{
// 登録
268
var b = new BackgroundTaskBuilder
{
Name = "Sample timer task",
TaskEntryPoint = "BackgroundTaskSampleApp.Tasks.TimerBackgroundTask",
};
// 15 分のタイマーでワンショットではない
b.SetTrigger(new TimeTrigger(15, false));
// 条件としてユーザーがいるときのみ実行する(オプション)
b.AddCondition(new SystemCondition(SystemConditionType.UserPresent));
b.Register();
}
}
まず、バックグラウンドタスクを登録する前に、RequestAccessAsync メソッドを呼び出す必要
があります。ユーザーが明示的にデバイス設定でバックグラウンドタスクに対するアプリのア
クセス許可を拒否した場合は、BackgroundAccessStatus.Denied を返します。次に、これから
登録するバックグラウンドタスクが既に登録されているかどうかをチェックします。このチェ
ックで、まだバックグラウンドタスクが登録されていない場合は BackgroundTaskBuilder でバ
ックグラウンドタスクを登録します。BackgroundTaskBuilder では、Name(任意の名前。こ
の名前でタスクの存在チェック等を行います)と TaskEntryPoint(タスクのクラス名)と
Trigger(バックグラウンドタスクを起動するトリガーの種類)と、バックグラウンドタスクが
起動する条件(オプション)を設定したうえで Register メソッドを呼び出します。今回の場合
は、Sample timer task が名前で、BackgroundTaskSampleApp.Tasks.TimerBackgroundTask が
エントリーポイントで、TimeTrigger がトリガーで、new
SystemCondigion(SystemConditionType.UserPresent))が条件になります。TimeTrigger は、
第一引数で実行間隔を指定して、第二引数でワンショットかどうかを指定します。今回の例で
は 15 分間隔でワンショットではないという指定をしています。TimeTrigger は 15 分が指定で
きる最小の値で、それ以下の値を指定した場合は例外がスローされます。
条件は任意に設定でき、SystemConditionType で指定できます。今回は、UserPresent という
ユーザーが存在する間だけバックグラウンドタスクが実行できるという条件を指定しました。
その他に InternetAvailable などネットワーク接続が有効な場合などが指定できます。
269
SystemConditionType については以下のドキュメントを参照してください。
https://msdn.microsoft.com/library/windows/apps/br224835
プログラムを実行すると画面が起動します。15 分待つと最低でも 1 回出力ウィンドウにメッセ
ージが出力されます。現実問題としてデバッグしづらいので、バックグラウンドタスクを任意
に起動させる手段も提供されています。「ライフサイクルイベント」の右側の▼を選択する
と、バックグラウンドタスクの名前が表示されます。これを選択することで、任意のタイミン
グでバックグラウンドタスクを実行することが出来ます。
これを選択すると出力ウィンドウに以下のメッセージが表示されます。
Hello world background task.
11.1.2.バ ッ クグ ラ ウ ン ド タス ク と フォ ア グ ラ ン ドの 連 携
バックグラウンドタスクとフォアグランドの処理の連携方法について説明します。バックグラ
ウンドタスクとフォアグランドの処理は、static な変数などを利用して連携を行うことはでき
ません。BackgroundTaskRegistration(Register メソッドの戻り値か AllTasks から取得する)
の Progress イベントで途中経過を得ることと、Completed イベントでバックグラウンドタスク
の完了を知ることが出来ます。データのやり取りは Progress イベントではイベント引数の
Progress プロパティで進捗を確認するための数値が確認できます。それ以外のデータについて
は ApplicationData.Current.LocalSettings を使用します。
270
例として、先ほど作成した TimerBackgroundTask を以下のように変更します。
using System;
using Windows.ApplicationModel.Background;
using Windows.Storage;
using Windows.System.Threading;
namespace BackgroundTaskSampleApp.Tasks
{
public sealed class TimerBackgroundTask : IBackgroundTask
{
private IBackgroundTaskInstance TaskInstance { get; set; }
private BackgroundTaskDeferral Deferral { get; set; }
public void Run(IBackgroundTaskInstance taskInstance)
{
this.TaskInstance = taskInstance;
this.Deferral = this.TaskInstance.GetDeferral();
ThreadPoolTimer.CreatePeriodicTimer(this.Tick, TimeSpan.FromSeconds(1));
}
private void Tick(ThreadPoolTimer timer)
{
if (this.TaskInstance.Progress >= 100)
{
// タイマーを停止して完了時刻のタイムスタンプを LocalSettings に設定し
て終了する
timer.Cancel();
ApplicationData.Current.LocalSettings.Values["TimerBackgroundTask"] =
DateTimeOffset.Now.ToString("yyyy/MM/dd HH:mm:sszzz");
this.Deferral.Complete();
}
else
{
271
// Progress を更新する
this.TaskInstance.Progress += 10;
}
}
}
}
1 秒間隔のタイマーを作成して、その中で Progress を 10 ずつインクリメントしています。今
回のように、Run メソッドの終了よりも遅延してバックグラウンドタスクの終了を通知したい
場合は GetDeferral メソッドで BackgroundTaskDeferral を取得します。
BackgroundTaskDeferral は、Complete を呼ぶことでタスクの終了を通知できます。タイマー
の中で 10 回処理が実行されたら、現在の時間を LocalSettings に書き込んで終了しています。
MainPage.xaml を以下のように編集します。状態を表示するための TextBlock を置いていま
す。
<Page x:Class="BackgroundTaskSampleApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BackgroundTaskSampleApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="TextBlock"
Style="{StaticResource HeaderTextBlockStyle}" />
</StackPanel>
</Page>
コードビハインドを以下のように編集します。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
272
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.Background;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace BackgroundTaskSampleApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// タスクの状態を監視する
var r = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(x => x.Name ==
"Sample timer task");
if (r != null)
{
r.Progress += this.BackgroundTask_Progress;
r.Completed += this.BackgroundTask_Completed;
}
}
273
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// タスクの監視を終了する
var r = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(x => x.Name ==
"Sample timer task");
if (r != null)
{
r.Progress -= this.BackgroundTask_Progress;
r.Completed -= this.BackgroundTask_Completed;
}
}
private async void BackgroundTask_Progress(BackgroundTaskRegistration sender,
BackgroundTaskProgressEventArgs args)
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.TextBlock.Text = $"Progress: {args.Progress}";
});
}
private async void BackgroundTask_Completed(BackgroundTaskRegistration sender,
BackgroundTaskCompletedEventArgs args)
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.TextBlock.Text = $"Completed time:
{ApplicationData.Current.LocalSettings.Values["TimerBackgroundTask"]}";
});
}
}
}
274
BackgroundTaskRegistration から Sample timer task を取り出し、Progress イベントと
Completed イベントを購読しています。イベントの中では Progress を表示するのと、
Completed イベントでは、LocalSettings の TimerBackgroundTask に設定された値を表示して
います。Progress イベントと Completed イベントは UI スレッド外で実行されるため、
Dispatcher を使って UI スレッド上で処理しています。このプログラムを実行して、バックグ
ラウンドタスクを実行すると以下のようになります。
Progress の状態が表示されます。
Progress が 100 を超えると、完了時刻が表示されます。
275
11.1.3.バ ッ クグ ラ ウ ン ド タス ク の 追加 情 報
バックグラウンドタスクのトリガーの種類によっては、バックグラウンドタスク内でタスクが
起動されたときの追加の情報を取得することが出来ます。詳細情報は、BackgroundTask の
Run メソッドの引数に渡される IBackgroundTaskInstance の TriggerDetails から取得できま
す。この TriggerDetails をトリガーの種類に応じて対応する型にキャストすることで詳細情報
にアクセスできます。この処理の詳細については後述する「12.1.4 トースト」で、トースト通
知をバックグラウンドで受け取る処理でコード例を示します。
11.1.4.シ ス テム イ ベ ン ト への 応 答
ここでは、SystemTrigger を紹介します。SystemTrigger は名前の通りシステムの様々なイベン
トに応答してバックグラウンドタスクを起動できるトリガーになります。バックグラウンドタ
スクを登録する処理でトリガーを設定するところで、SystemTrgger を設定することで使用可能
です。前述した SystemTriggerType とワンショットかどうかの指定をコンストラクタで指定し
ます。SystemTriggerType では、ユーザーが在籍しているとき、ネットワークのステータスが
276
変わったときなどの指定が可能です。指定可能なすべてのオプションについては以下のドキュ
メントを参照してください。
https://msdn.microsoft.com/ja-
jp/library/windows/desktop/windows.applicationmodel.background.systemtriggertype
12. タイルとトースト
タイルは、Windows 10 においてスタート画面を通じてユーザーに様々な追加情報を提供する
ための有用な場として使用することが出来ます。Windows 10 のデスクトップのタブレットモ
ードや Windows 10 Mobile などのようにスタート画面がデフォルト画面となる環境では、その
傾向が顕著です。タイルを有効に使うアプリケーションを作ることで、ユーザーに対して自分
のアプリケーションをスタート画面にピンどめしてもらうための 1 つの強い動機づけになりま
す。
トーストは、ユーザーに対してアプリから気付いてほしいものをユーザーに伝えるための手段
として使えます。適度なトーストは、ユーザーがアプリに戻ってくるためのきっかけを提供す
るための手段として使えます。(トーストを表示しすぎるとアンインストールされると思うの
でほどほどに)
12.1.1.タ イ ルや ト ー ス ト を作 成 す るた め の 補 助 ツー ル
UWP では、XML 形式でタイルや通知を定義します。この XML を作成するための補助ツール
として UWP アプリの Notifications Visualizer がおすすめです。ストアで検索してみてくださ
い。以下のように XML を定義しながらタイルや通知の状況を確認できます。
277
また、プログラムでこれらの XML を直接組み立てることも可能ですが、これを補助するライ
ブラリもリリースされています。NotificationsExtensions.Win10 で NuGet から入手可能です。
このライブラリを使うことでタイプセーフに XML を組み立てることが出来ます。
タイルの XML のスキーマについては以下のドキュメントを参照してください。
https://msdn.microsoft.com/ja-jp/windows/uwp/controls-and-patterns/tiles-and-notifications-
adaptive-tiles-schema
278
トーストの XML のスキーマについては以下のドキュメントを参照してください。
https://msdn.microsoft.com/ja-jp/windows/uwp/controls-and-patterns/tiles-and-notifications-
adaptive-interactive-toasts
12.1.2.ラ イ ブタ イ ル
ライブタイルは、アプリの起動のためのタイルに情報を表示する機能を追加するものです。タ
イルを作成するには、TileUpdateManager クラスの GetTileUpdaterForApplication 静的メソッ
ドで TileUpdater クラスのインスタンスを取得して、Update メソッドに TileNotification を渡
して呼び出します。TileNotification は引数にタイルの見た目を定義した XmlDocument を渡し
ます。AddToSchedule メソッドに ScheduledTileNotification を渡すことで、時間指定でタイ
ルを更新することが出来ます。ScheduledTileNotification クラスのコンストラクタにタイルの
見た目を定義した XmlDocument と表示時間の DateTimeOffset を渡します。Clear メソッドを
呼び出すと、ライブタイルに追加された更新やスケジュールなどがリセットされます。タイル
による通知は 5 つまで有効です。
例として、以下のような XML を通知として表示する場合の手順を示します。
<tile>
<visual>
<binding template="TileSmall" hint-textStacking="center">
<text hint-align="center">Mon</text>
<text hint-align="center" hint-style="body">22</text>
</binding>
<binding template="TileMedium" branding="name" displayName="Monday 22">
<text hint-wrap="true" hint-maxLines="2">Snowboarding with Mark</text>
<text hint-style="captionSubtle">Fri: 9:00 AM</text>
</binding>
<binding template="TileWide" branding="nameAndLogo" displayName="Monday 22">
<text>Snowboarding with Mark</text>
<text hint-style="captionSubtle">Mt. Baker</text>
279
<text hint-style="captionSubtle">Tomorrow: 9:00 AM – 5:00 PM</text>
</binding>
<binding template="TileLarge" branding="nameAndLogo" displayName="Monday 22">
<group>
<subgroup>
<text hint-wrap="true">Snowboarding with Mark</text>
<text hint-style="captionSubtle">Mt. Baker</text>
<text hint-style="captionSubtle">Tomorrow: 9:00 AM – 5:00 PM</text>
</subgroup>
</group>
<text />
<group>
<subgroup>
<text hint-wrap="true">Casper Baby Pants</text>
<text hint-style="captionSubtle">Hollywood Bowl</text>
<text hint-style="captionSubtle">Tomorrow: 8:00 PM – 11:00 PM</text>
</subgroup>
</group>
</binding>
</visual>
</tile>
この XML は、Notifications Visualizer の Calendar.xml の内容になります。これを
Notifications Extensions を使って組み立てます。コードを以下に示します。(コードが長くな
るので TileSmall のみの組み立てを示していますが他の部分も同じ要領で組み立てることが出
来ます。
public void Click()
{
var tile = new TileContent
{
280
Visual = new TileVisual
{
TileSmall = new TileBinding
{
Content = new TileBindingContentAdaptive
{
Children =
{
new AdaptiveText
{
HintAlign = AdaptiveTextAlign.Center,
Text = "Mon",
},
new AdaptiveText
{
HintAlign = AdaptiveTextAlign.Center,
Text = "22",
},
}
}
},
TileMedium = new TileBinding
{
// ...
},
TileLarge = new TileBinding
{
// ...
},
TileWide = new TileBinding
{
// ...
}
}
};
281
var tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
// クリアして追加する
tileUpdater.Clear();
tileUpdater.Update(new TileNotification(tile.GetXml()));
}
この Click メソッドが呼び出されるとタイルが以下のように表示されます。
12.1.3.セ カ ンダ リ タ イ ル
UWP では 1 つのアプリに複数のタイルを表示することが出来ます。セカンダリタイルを使う
と、アプリを特定画面から開いたり、アプリ起動時に特別な引数を渡すといったことが出来ま
す。セカンダリタイルを作るには、SecondaryTile クラスを作成して、
RequestCreateForSelectionAsync を呼び出します。SecondaryTile クラスにはコンストラクタで
以下の引数を指定します。
 tileId:タイルの識別 ID。削除時などに使用する。
 displayName:タイルに表示されるアプリの長い名前
 arguments:アプリ起動時に渡される引数
 square150x150Logo:タイルの正方形のロゴ
 desiredSize:タイルのサイズ
コンストラクタで基本設定を指定したあと、VisualElements プロパティを使ってワイドロゴな
どを指定できます。ボタンを押したときにセカンダリタイルを表示するには以下のようなコー
ドになります。
282
public async void Click()
{
// セカンダリタイルクラスの作成
var tile = new SecondaryTile(
"sampleTileId",
"表示名",
"argument",
new Uri("ms-appx:///Assets/Square150x150Logo.png"),
TileSize.Square150x150);
// ワイドタイルの設定
tile.VisualElements.Wide310x150Logo = new Uri("ms-appx:///Assets/Wide310x150Logo.png");
// 名前の表示・非表示を設定
tile.VisualElements.ShowNameOnSquare150x150Logo = true;
tile.VisualElements.ShowNameOnWide310x150Logo = true;
// ローミングするか設定
tile.RoamingEnabled = false;
// 前景色を設定
tile.VisualElements.ForegroundText = ForegroundText.Dark;
// タイルの表示をユーザーに要求
await tile.RequestCreateAsync();
}
このタイルを削除するコードは以下のようになります。存在確認をしてから削除コードを実行
している点がポイントです。
public async void UnpinClick()
{
// 存在を確認してから削除する
if (SecondaryTile.Exists("sampleTileId"))
{
var tile = new SecondaryTile("sampleTileId");
await tile.RequestDeleteAsync();
283
}
}
実行すると以下のようになります。ボタンを押すとタイルを表示するか尋ねられます。
「はい」を選択すると、タイルがスタート画面に作成されます。
タイルが存在する状態で Unpin のボタンを選択すると、タイルが削除されます。セカンダリタ
イルから起動したときは、OnLaunched メソッドの LaunchActivatedEventArgs の Arguments
プロパティに SecondaryTile 作成時に指定した引数がわたってきます。(今回のプログラム例
だと argument)この値を使って画面の遷移先や、ページの表示データを変えることでセカンダ
リタイルをショートカット的に使うことが出来ます。
284
12.1.4.ト ー スト
トーストは、タイルと同様に XML によって定義されます。visual でテキストや画像などを定義
して、actions でボタンや入力を定義して、audio で通知が表示されるときに再生される音楽を
指定できます。
トースト通知は、ToastNotificationManager クラスの CreateToastNotifier 静的メソッドで
ToastNotifier クラスを作成して、その Show メソッドに ToastNotification を渡すことで表示す
ることが出来ます。ToastNotification クラスは、コンストラクタにタイルと同様に
XmlDocument を渡します。トーストの XML も Notifications Visualizer で確認し Notifications
Extensions で組み立てることが出来ます。例として、以下のようなシンプルなトースト通知の
XML を Notifications Extensions で組み立てます。
<toast>
<visual>
<binding template="ToastGeneric">
<text>Hello World</text>
<text>This is a simple toast message</text>
</binding>
</visual>
</toast>
以下のようなコードになります。
using NotificationsExtensions;
using NotificationsExtensions.Toasts;
using Windows.UI.Notifications;
using Windows.UI.Xaml.Controls;
namespace ToastApp
{
public sealed partial class MainPage : Page
{
285
public MainPage()
{
this.InitializeComponent();
}
public void Click()
{
var toast = new ToastContent
{
Visual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText { Text = "Hello world" },
new AdaptiveText { Text = "This is a simple toast message" },
}
}
}
};
var toastNotifier = ToastNotificationManager.CreateToastNotifier();
toastNotifier.Show(new ToastNotification(toast.GetXml()));
}
}
}
実行して Click メソッドを呼び出すと以下のようにトーストが表示されます。
286
トーストから入力を受け取ってユーザーがボタンを押したときの操作に応答する例を以下に示
します。トーストを通知するコードは以下のようになります。
using NotificationsExtensions;
using NotificationsExtensions.Toasts;
using Windows.UI.Notifications;
using Windows.UI.Xaml.Controls;
namespace ToastApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public void Click()
{
var toast = new ToastContent
{
ActivationType = ToastActivationType.Foreground,
Visual = new ToastVisual
{
287
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText { Text = "Hello world" },
}
}
},
Actions = new ToastActionsCustom
{
Inputs =
{
new ToastTextBox("textBox")
{
PlaceholderContent = "Please input message."
}
},
Buttons =
{
new ToastButton("Send", "toast"),
},
}
};
var toastNotifier = ToastNotificationManager.CreateToastNotifier();
toastNotifier.Show(new ToastNotification(toast.GetXml()));
}
}
}
このコードで表示されるトーストは以下のようになります。
288
このとき Send ボタンを押すと App クラスの OnActivated メソッドが呼び出されます。トース
トから呼び出されたときは ToastNotificationActivatedEventArgs になっているため、型をチェ
ックして Argument を確認して UserInput から textBox への入力を取得してメッセージボック
スで表示しています。
protected override async void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
var toast = args as ToastNotificationActivatedEventArgs;
if (toast != null)
{
if (toast.Argument == "toast")
{
var message = toast.UserInput["textBox"] as string;
await new MessageDialog($"{message} が入力されました").ShowAsync();
}
}
}
実行してトーストにテキストを入れて Send ボタンを押すと以下のようにアプリケーションで
メッセージボックスが表示されます。
289
バックグラウンドタスクでも、トーストでボタンが押されたときの処理を行うことが出来ま
す。Windows ランタイムコンポーネントとしてバックグラウンドタスクを作成します。
using NotificationsExtensions;
using NotificationsExtensions.Tiles;
using Windows.ApplicationModel.Background;
using Windows.UI.Notifications;
namespace ToastSampleApp.Background
{
public sealed class ToastBackgroundTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
var toast = taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;
if (toast.Argument == "toast")
{
// 受け取ったメッセージでタイルをアップデート
var message = toast.UserInput["textBox"] as string;
var tileContent = new TileContent
{
290
Visual = new TileVisual
{
TileMedium = new TileBinding
{
Content = new TileBindingContentAdaptive
{
Children =
{
new AdaptiveText
{
Text = $"{message} が入力されました",
HintWrap = true,
}
}
}
}
}
};
TileUpdateManager.CreateTileUpdaterForApplication().Update(
new TileNotification(tileContent.GetXml()));
}
}
}
}
ToastNotificationActionTriggerDetail が渡されてきているので、そこから Argument と
UsreInput が取得できます。あとは、OnActivated のときと同じように処理が出来ます。ここ
ではタイルをトーストで受け取った内容をもとに更新しています。後は、OnLaunched の最初
でバックグラウンドタスクを登録します。
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
task.Value.Unregister(true);
}
var state = await BackgroundExecutionManager.RequestAccessAsync();
291
if (state != BackgroundAccessStatus.Denied)
{
var b = new BackgroundTaskBuilder();
b.Name = "ToastBackgroundTask";
b.TaskEntryPoint = "ToastSampleApp.Background.ToastBackgroundTask";
b.SetTrigger(new ToastNotificationActionTrigger());
b.Register();
}
最後に、Package.appxmanifest でバックグラウンドタスクを宣言しておきます。ポイントはシ
ステムイベントとして登録することです。
292
トースト通知のほうは、ToastButton に対して ActivationType に
ToastActivationType.Background を設定することで、ボタンが押されたときにバックグラウン
ドタスクが実行されるようになります。
public void Click()
{
var toast = new ToastContent
{
Visual = new ToastVisual
{
BindingGeneric = new ToastBindingGeneric
{
Children =
{
new AdaptiveText { Text = "Hello world" },
}
}
},
Actions = new ToastActionsCustom
{
Inputs =
{
new ToastTextBox("textBox")
{
PlaceholderContent = "Please input message."
}
},
Buttons =
{
new ToastButton("Send", "toast")
{
ActivationType = ToastActivationType.Background,
},
},
}
293
};
var toastNotifier = ToastNotificationManager.CreateToastNotifier();
toastNotifier.Show(new ToastNotification(toast.GetXml()));
}
実行して Click メソッドを呼び出すと以下のようにトースト通知が表示されます。
テキストを入力して Send ボタンを選択すると、以下のようにタイルが更新されることが確認
できます。
13. プッシュ通知
294
UWP アプリでは、Windows プッシュ通知サービス(WNS)を使用することで簡単にプッシュ
通知を実装することが出来ます。ストアに紐づけ(公開する必要はありません)られたアプリ
に対して発行される URI を使用して、任意のサービスから WNS を経由して UWP アプリにプ
ッシュ通知を発行することが出来ます。
PushNotificationApp という名前のアプリを作ったものとしてプッシュ通知をアプリに組み込
む方法を説明します。まず、プロジェクトの右クリックメニューから「ストア」→「アプリケ
ーションをストアと関連付ける」を選択します。(Windows デベロッパーセンターで開発者
登録をしてない人は、後述する URL から登録しておきましょう。
https://developer.microsoft.com/ja-jp/windows)
「次へ」を選択します。
295
Microsoft アカウントでのサインインが求められるのでサインインを行います。既に予約してい
るアプリ名のリストが表示されます。ここでは、新規のアプリ名を取得します。今回は
「okazukiPush」という名前でアプリ名を登録します。画面下部のテキストボックスに
okazukiPush と入力(実際はご自分のアプリ名を入力してください)して「予約」ボタンを選
択します。
296
予約できたら「次へ」を選択します。そして、「関連付け」を選択します。
297
次に Windows デベロッパーセンターにアクセスします。以下の URL へアクセスしてくださ
い。
https://developer.microsoft.com/ja-jp/windows
Microsoft アカウントでサインインしたら、画面右上にある「ダッシュボード」を選択します。
画面左のリストに先ほど予約した名前のアプリが追加されているので選択します。
298
「申請を開始する」を選択します。
画面左のリストにある「サービス」を選択します。
299
「プッシュ通知」を選択します。
300
画面中ほどにある「Live Services site」を選択します。
Microsoft アカウントでサインインするとアプリケーションシークレットと、パッケージ SID
とアプリケーション ID が取得できます。
301
アプリケーション ID を Package.appxmanifest に記述します。Package.appxmanifest を右クリ
ックして、「ファイルを開くアプリケーションの選択」を選んで XML エディターで開いてく
ださい。Package タグ直下に、アプリケーション ID に記載してある Identity タグと同じ物があ
ることを確認します。
次にプッシュ通知を送信するサーバーアプリケーションを作成します。通常はクラウド上など
に構築された Web アプリケーションなどのサーバーアプリケーションになりますが今回は簡
単にするためにコンソールアプリケーションとして作成します。
「PushNotificationApp.Server」という名前でコンソールアプリケーションを作成します。認証
のコードを以下に示します。
using System;
using System.Collections.Generic;
302
using System.Net.Http;
namespace PushNotificationApp.Server
{
class Program
{
static void Main(string[] args)
{
// 認証処理
var client = new HttpClient();
var content = new FormUrlEncodedContent(
new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", "ms-app://s-1-15-2-
3517753967-4196436483-1856480010-4052086479-2225032242-2789219409-18995796"),
new KeyValuePair<string, string>("client_secret",
"Mgg1XLMrsVjvxmaWcE/IJNxeTApuIHEq"),
new KeyValuePair<string, string>("scope", "notify.windows.com")
});
var res = client.PostAsync("https://login.live.com/accesstoken.srf", content).Result;
// 結果のダンプ
Console.WriteLine(res.Content.ReadAsStringAsync().Result);
}
}
}
client_id と client_secret が先ほどのサイトで取得したパッケージ SID とアプリケーションシー
クレットになります。結果は以下のようになります。
{"token_type":"bearer","access_token":"EgCdAQMAAAAEgAAADAAB1wy2M2asJ6I+qaCv7B/t7NzHg
63qvGsr4wBBFNcDGl4XI1tW+XzRPPy1q58m0IilekLr1f6IBuNgYHcjSKRjrsj3uXty691mqe5aZlya6mlbCm
RtoXECsxNJs66fZwN2aBv1p76u6dXI1TCR72ZiA8X+AZ6nb92khg2u35nlsrYjs3lQqFmC1HlJF229iZO0S
QGCmaQc+i1D6j6q99B/jPuYI6k7n0PE9lmWl5Atvdfds6snIGWmCd9gjkoa2kwht4ZfbVVKl0W7SQ4H1k5y
nzupQY3s423Iyy4dL9Kc8j1E5486d9g2Wq94iE8J57gUJmas8AdOqTa0aGWOSvjlxIwAWgCMAAAAAAAO
303
ERpA25B0V9uQdFfrSAQADwAxMjYuMjM3LjExNC4xMQAAAAAAXQBtcy1hcHA6Ly9zLTEtMTUtMi0zN
TE3NzUzOTY3LTQxOTY0MzY0ODMtMTg1NjQ4MDAxMC00MDUyMDg2NDc5LTIyMjUwMzIyNDItMjc4
OTIxOTQwOS0xODk5NTc5NgA=","expires_in":86400}
ここで取得した access_token がプッシュ通知のサーバーにアクセスするために必要なキーにな
ります。JSON.NET などでパースして取得します。ここではコピペで保管します。expires_in
で有効期限の時間(秒)が指定されています。上記レスポンスでは 24 時間と指定されていま
す。有効期限が切れるまでは、このアクセストークンを利用し、有効期限が切れた場合には再
度アクセストークンを取得します。
次に、UWP アプリでプッシュ通知を受け取るための URL を生成します。
PushNotificationApp の MainPage.xaml を以下のように編集します。URL を表示するための
TextBox と URL を生成するための Button を置いています。
<Page x:Class="PushNotificationApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PushNotificationApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox x:Name="TextBox" />
<Button Content="Request"
Click="{x:Bind Click}" />
</StackPanel>
</Page>
コードビハインドで URL を作成します。PushNotificationChannelManager クラスの
CreatePushNotificationChannelForApplicationAsync 静的メソッドで PushNotificationChannel
クラスのインスタンスが取得できます。このクラスの Uri プロパティでプッシュ通知送信先の
URL が取得できます。コードを以下に示します。
using System;
304
using Windows.Networking.PushNotifications;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace PushNotificationApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
try
{
var channel = await
PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
this.TextBox.Text = channel.Uri.ToString();
await new MessageDialog($"{channel.Uri}, 期限:
{channel.ExpirationTime}").ShowAsync();
}
catch(Exception ex)
{
await new MessageDialog(ex.Message).ShowAsync();
}
}
}
}
通常は、この URL をユーザーID などと紐づけてサーバーに送信してサーバー側で管理を行い
ます。CreatePushNotificationChannelForApplicationAsync メソッドの結果の URL が変わらな
い場合は、サーバーに再送信する必要はありません。変わっている場合は再送信する必要があ
305
ります。上記プログラムを実行して Request ボタンを選択すると以下のようにメッセージボッ
クスが表示されます。
この URL をメモしておきます。
プッシュ通知を送信するには、WNS のプロトコルに沿ったリクエストを送信します。ヘッダ
ーに指定する HTTP ヘッダー値の詳細は以下を参照してください。
https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/hh868245
今回は例としてトースト通知をしてみたいと思います。以下のコードを
PushNotificationApp.Server プロジェクトの Program.cs に記述します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
namespace PushNotificationApp.Server
{
class Program
{
306
static void Main(string[] args)
{
// 取得したアクセストークン
var token =
"EgCdAQMAAAAEgAAADAAB1wy2M2asJ6I+qaCv7B/t7NzHg63qvGsr4wBBFNcDGl4XI1tW+XzRPPy1
q58m0IilekLr1f6IBuNgYHcjSKRjrsj3uXty691mqe5aZlya6mlbCmRtoXECsxNJs66fZwN2aBv1p76u6dXI1T
CR72ZiA8X+AZ6nb92khg2u35nlsrYjs3lQqFmC1HlJF229iZO0SQGCmaQc+i1D6j6q99B/jPuYI6k7n0PE9l
mWl5Atvdfds6snIGWmCd9gjkoa2kwht4ZfbVVKl0W7SQ4H1k5ynzupQY3s423Iyy4dL9Kc8j1E5486d9g2W
q94iE8J57gUJmas8AdOqTa0aGWOSvjlxIwAWgCMAAAAAAAOERpA25B0V9uQdFfrSAQADwAxMjYuMj
M3LjExNC4xMQAAAAAAXQBtcy1hcHA6Ly9zLTEtMTUtMi0zNTE3NzUzOTY3LTQxOTY0MzY0ODMtM
Tg1NjQ4MDAxMC00MDUyMDg2NDc5LTIyMjUwMzIyNDItMjc4OTIxOTQwOS0xODk5NTc5NgA=";
var client = new HttpClient();
// トーストの内容を表す XML を送信する
var content = new StringContent(@"
<toast>
<visual>
<binding template=""ToastGeneric"">
<text>サンプルトースト通知</text>
</binding>
</visual>
</toast>");
// 認証ヘッダーを指定する
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", token);
// wns/toast でトースト通知であることを示す
content.Headers.Add("X-WNS-Type", "wns/toast");
content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
// UWP アプリで生成した URL に対して POST を投げる
var response =
client.PostAsync("https://hk2.notify.windows.com/?token=AwYAAAAqAPItmTeuRwSuosiz%2bP%2bnv
0lC4RyOwD5%2b9r9lyv73doSaehsleQNnUdnEpFyagREYOjxiFiBA4qEh%2beoes2uTe%2fDq3FwLk3%2bN
Wkpz4Px8PqYXwMQA%2bcYNTLZNLE9IlBE%3d", content).Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
}
307
}
このプログラムを実行すると、プッシュ通知サーバーを経由してトーストが表示されます。
X-WNS-Type ヘッダーを wns/tile にしてタイルの XML を送信することで、プッシュ通知でタ
イルの更新が可能です。
X-WNS-Type を wns/raw にして ContentType を application/octet-stream にすることで 5KB
までの任意のデータをプッシュ通知することが出来ます。送信側のコード例を以下に示しま
す。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
namespace PushNotificationApp.Server
{
class Program
{
static void Main(string[] args)
{
// 取得したアクセストークン
var token =
"EgCdAQMAAAAEgAAADAAB1wy2M2asJ6I+qaCv7B/t7NzHg63qvGsr4wBBFNcDGl4XI1tW+XzRPPy1
q58m0IilekLr1f6IBuNgYHcjSKRjrsj3uXty691mqe5aZlya6mlbCmRtoXECsxNJs66fZwN2aBv1p76u6dXI1T
CR72ZiA8X+AZ6nb92khg2u35nlsrYjs3lQqFmC1HlJF229iZO0SQGCmaQc+i1D6j6q99B/jPuYI6k7n0PE9l
308
mWl5Atvdfds6snIGWmCd9gjkoa2kwht4ZfbVVKl0W7SQ4H1k5ynzupQY3s423Iyy4dL9Kc8j1E5486d9g2W
q94iE8J57gUJmas8AdOqTa0aGWOSvjlxIwAWgCMAAAAAAAOERpA25B0V9uQdFfrSAQADwAxMjYuMj
M3LjExNC4xMQAAAAAAXQBtcy1hcHA6Ly9zLTEtMTUtMi0zNTE3NzUzOTY3LTQxOTY0MzY0ODMtM
Tg1NjQ4MDAxMC00MDUyMDg2NDc5LTIyMjUwMzIyNDItMjc4OTIxOTQwOS0xODk5NTc5NgA=";
var client = new HttpClient();
// 通知データを設定する
var content = new StringContent($"Send custom push notification
{DateTimeOffset.Now.ToString()}");
// 認証ヘッダーを指定する
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", token);
// wns/raw で直接配信であることを示す
content.Headers.Add("X-WNS-Type", "wns/raw");
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-
stream");
// UWP アプリで生成した URL に対して POST を投げる
var response =
client.PostAsync("https://hk2.notify.windows.com/?token=AwYAAAAqAPItmTeuRwSuosiz%2bP%2bnv
0lC4RyOwD5%2b9r9lyv73doSaehsleQNnUdnEpFyagREYOjxiFiBA4qEh%2beoes2uTe%2fDq3FwLk3%2bN
Wkpz4Px8PqYXwMQA%2bcYNTLZNLE9IlBE%3d", content).Result;
Console.WriteLine(response.StatusCode);
}
}
}
受信側は、PushNotificationChannel クラスの PushNotificationReceived イベントを購読するこ
とで、フォアグラウンドで受信できます。コード例を以下に示します。
using System;
using Windows.Networking.PushNotifications;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace PushNotificationApp
{
309
public sealed partial class MainPage : Page
{
private PushNotificationChannel Channel { get; set; }
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
try
{
this.Channel = await
PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
this.Channel.PushNotificationReceived += this.Channel_PushNotificationReceived;
this.TextBox.Text = this.Channel.Uri.ToString();
await new MessageDialog($"{this.Channel.Uri}, 期限:
{this.Channel.ExpirationTime}").ShowAsync();
}
catch(Exception ex)
{
await new MessageDialog(ex.Message).ShowAsync();
}
}
private async void Channel_PushNotificationReceived(PushNotificationChannel sender,
PushNotificationReceivedEventArgs args)
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async
() =>
{
await new MessageDialog(args.RawNotification.Content).ShowAsync();
});
}
310
}
}
UWP アプリ側を実行してボタンを押してイベントハンドラを登録したあとにコンソールアプ
リケーションを実行すると以下のようにプッシュ通知をフォアグラウンドで受け取りメッセー
ジボックスが表示されます。
wns/raw の通知はバックグラウンドタスクで受信することも出来ます。以下のように Windows
ランタイムコンポーネントのプロジェクトにバックグラウンドタスクを作成します。
using Windows.ApplicationModel.Background;
using Windows.Networking.PushNotifications;
using Windows.Storage;
namespace PushNotificationApp.Tasks
{
public sealed class PushNotificationTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
var notification = taskInstance.TriggerDetails as RawNotification;
311
ApplicationData.Current.LocalSettings.Values["Notification"] = $"{notification.Content}
を受信しました";
}
}
}
TriggerDetails は RawNotification が格納されています。
このバックグラウンドタスクを Package.appxmanifest に登録します。
そして、App クラスの OnLaunched メソッドでバックグラウンドタスクとして登録します。ト
リガーは PushNotificationTrigger クラスです。コードを以下に示します。
312
var status = await BackgroundExecutionManager.RequestAccessAsync();
if (status != BackgroundAccessStatus.Denied)
{
if (!BackgroundTaskRegistration.AllTasks.Values.Any(x => x.Name == "PushTask"))
{
var b = new BackgroundTaskBuilder
{
Name = "PushTask",
TaskEntryPoint = "PushNotificationApp.Tasks.PushNotificationTask"
};
b.SetTrigger(new PushNotificationTrigger());
b.Register();
}
}
最後にバックグラウンドタスクの処理が完了したときの処理に応答する処理を MainPage に書
きます。OnNavigatedTo で Completed イベントを購読してメッセージボックスを表示してい
ます。
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var r = BackgroundTaskRegistration.AllTasks.Values.First(x => x.Name == "PushTask");
r.Completed += async (_, __) =>
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
await new
MessageDialog((string)ApplicationData.Current.LocalSettings.Values["Notification"]).ShowAsync();
});
};
}
UWP アプリとコンソールアプリケーションを実行すると以下のようにバックグラウンドタス
ク経由のメッセージボックスが表示されます。
313
14. コルタナ連携
UWP アプリでは、Windows 10 の音声によるパーソナルアシスタントであるコルタナとアプリ
を連携させることが出来ます。ここでは、コルタナを使ってできることを紹介したいと思いま
す。
14.1.1.フ ォ アグ ラ ウ ン ド アプ リ の 起動
コルタナを使用してアプリを起動することが出来ます。コルタナと連携するアプリを作成する
流れは以下のようになります。
 VCD と呼ばれる XML 形式のファイルを作成します。このファイルにコルタナで、どのキ
ーワードに対して応答するかといったことが定義可能です。VCD ファイルのスキーマに
ついては以下を参照してください。
https://msdn.microsoft.com/library/windows/apps/dn706593
 アプリ起動時に上記 VCD ファイルをシステムに登録します。
 App クラスの OnActivated メソッドでコルタナからのアクティブ化を処理します。
314
まず、UWP アプリを作成(ここでは CortanaApp という名前で作成した前提で説明します)
します。そして、XML ファイルをプロジェクトに追加します。ファイル名は
VoiceCommands.xml にしました。このファイル名は任意でかまいません。これが VCD ファイ
ルになります。VCD ファイルを以下のように記述します。
<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
</VoiceCommands>
これ以降は Visual Studio がスキーマを読み込んで、ある程度インテリセンスが効くようになり
ます。VoiceCommands タグの下には CommandSet タグを定義します。CommandSet には
xml:lang で言語を、Name で名前を定義します。CommandSet タグの下には AppName と
Example と Command などを定義します。AppName にはアプリの名前を Example には例を
Command は音声認識のコマンドの詳細を定義していきます。Command の中には Example で
例を、ListenFor で認識する音声を、Feedback で音声認識の結果の応答と、そして Navigate で
画面遷移を行うという定義を行います。ListenFor の中では{}でくくることで任意のワードを定
義することが出来ます。ここで定義するワードは PhraseTopic か PhraseList で Command の下
に定義します。例として、以下のように VoiceCommands.xml を書き換えます。
<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<!-- -->
<CommandSet xml:lang="ja-jp" Name="LaunchAppSample_ja-jp">
<AppName>サンプル</AppName>
<Example>サンプル起動</Example>
<Command Name="LaunchAppCommand">
<Example>サンプル起動 ハローワールド</Example>
<ListenFor RequireAppName="BeforePhrase">起動 {keyword}</ListenFor>
<Feedback>{keyword}で、アプリを起動しています。</Feedback>
<Navigate />
</Command>
<Command Name="SearchAppCommand">
<Example>サンプル検索 ハローワールド</Example>
315
<ListenFor RequireAppName="BeforePhrase">検索 {keyword}</ListenFor>
<Feedback>{keyword}で、検索しています。</Feedback>
<Navigate />
</Command>
<PhraseTopic Label="keyword">
</PhraseTopic>
</CommandSet>
</VoiceCommands>
ListenFor で RequireAppName=”BeforePhrase”を定義すると、アプリ名を音声コマンドの前に
入力するという意味になります。上記 VoiceCommands.xml では、「サンプル起動 任意の言
葉」と「サンプル検索 任意の言葉」という 2 つの単語に反応するようにしています。任意の
言葉は keyword という単語で PhraseTopic として定義しています。
VoiceCommands ファイルが出来たら、このファイルをシステムに登録します。登録にはアプ
リ起動時などの任意のタイミングで以下のコードを実行します。今回は起動時に読み込ませた
いと思うので OnLaunched メソッドの先頭に追加しました。
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(
await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///VoiceCommands.xml")));
コルタナからアプリが起動されると App クラスの OnActivated メソッドが呼び出されます。イ
ベント引数には VoiceCommandActivatedEventArgs がわたってきます。
VoiceCommandActivatedEventArgs の Result の Text で認識した文字列が確認できます。
Result の RulePath[0]で認識した Command の Name が取得できます。Result の
SemanticInterpretion の Properties[“keyword”]で{keyword}として認識した文字列のリストを取
得できます。例えば以下のようなコードで、認識したコマンドの名前と keyword の中身を
CortanaPage というページに渡すことが出来ます。
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
if (args is VoiceCommandActivatedEventArgs)
{
316
var e = (VoiceCommandActivatedEventArgs)args;
var frame = Window.Current.Content as Frame;
if (frame == null)
{
frame = new Frame();
Window.Current.Content = frame;
}
frame.Navigate(typeof(CortanaPage), $"{e.Result.RulePath[0]}
{e.Result.SemanticInterpretation.Properties["keyword"].First()}");
}
Window.Current.Activate();
}
CortanaPage は以下のような XAML と
<Page x:Class="CortanaApp.CortanaPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CortanaApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="TextBlock"
Style="{StaticResource HeaderTextBlockStyle}" />
</Grid>
</Page>
以下のようなコードビハインドを持つシンプルなページです。
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace CortanaApp
317
{
public sealed partial class CortanaPage : Page
{
public CortanaPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
this.TextBlock.Text = (string)e.Parameter;
}
}
}
一度アプリを実行して、VoiceCommands.xml のインストール処理を走らせます。そして、
「サンプル起動こんにちは」と話しかけると以下のようにコルタナがアプリを認識してアプリ
が起動されます。
起動したアプリでは、どのコマンドで、どんなテキストが認識されたのか確認できます。
318
今回は PhraseTopic を使って任意の単語を入力として受け付けましたが、この単語をリストか
らの選択式にすることも出来ます。そのときは PhraseList を使います。例として「こんにち
は」「こんばんは」「さようなら」の 3 つの単語に応答するようにした VoiceCommands.xml
を以下に示します。
<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<!-- -->
<CommandSet xml:lang="ja-jp" Name="LaunchAppSample_ja-jp">
<AppName>サンプル</AppName>
<Example>サンプル起動</Example>
<Command Name="LaunchAppCommand">
<Example>サンプル起動 ハローワールド</Example>
<ListenFor RequireAppName="BeforePhrase">起動 {keyword}</ListenFor>
<Feedback>{keyword}で、アプリを起動しています。</Feedback>
<Navigate />
</Command>
319
<Command Name="SearchAppCommand">
<Example>サンプル検索 ハローワールド</Example>
<ListenFor RequireAppName="BeforePhrase">検索 {keyword}</ListenFor>
<Feedback>{keyword}で、検索しています。</Feedback>
<Navigate />
</Command>
<PhraseList Label="keyword">
<Item>こんにちは</Item>
<Item>こんばんは</Item>
<Item>さようなら</Item>
</PhraseList>
</CommandSet>
</VoiceCommands>
これでアプリを再度実行して VoiceCommands.xml をインストールさせてコルタナさんに「サ
ンプル検索さようなら」と話しかけると以下のようにアプリが起動します。
320
この PhraseList はアプリから更新することも出来ます。例として MainPage.xaml で入力したテ
キストに PhraseList の内容を置き換える例を示します。まず、MainPage.xaml を以下のように
して、複数行受け付ける TextBox と AppBarButton を置きます。
<Page x:Class="CortanaApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CortanaApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="Accept"
Label="Regist"
Click="{x:Bind Click}" />
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox x:Name="TextBox"
AcceptsReturn="True" />
</Grid>
</Page>
そして、Click メソッドでリストの内容を更新します。更新方法は
VoiceCommandDefinitionManager の InstalledCommandDefinitions の TryGetValue メソッド
で CommandSet に定義した Name 属性の値で VoiceCommandDefinition が取得できます。
VoiceCommandDefinition の SetPhraseListAsync メソッドで PhraseList の名前と、リストの中
身を渡すことでリストの内容を更新できます。コードを以下に示します。
using System;
using System.Linq;
using Windows.ApplicationModel.VoiceCommands;
using Windows.UI.Xaml.Controls;
321
namespace CortanaApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public async void Click()
{
if (string.IsNullOrWhiteSpace(this.TextBox.Text)) { return; }
var items = this.TextBox.Text.Split('n').Select(x => x.Trim());
VoiceCommandDefinition def;
if (VoiceCommandDefinitionManager.InstalledCommandDefinitions
.TryGetValue("LaunchAppSample_ja-jp", out def))
{
await def.SetPhraseListAsync("keyword", items);
}
}
}
}
アプリを実行して、以下のように入力して AppBarButton を選択します。
322
コルタナさんに対して「サンプル起動ミニチュアダックス」と話しかけると以下のようにアプ
リが起動します。PhraseList が更新されていることが確認できます。
323
14.1.2.バ ッ クグ ラ ウ ン ド アプ リ の 起動
コルタナを使うと、バックグラウンドタスクを使って処理をすることも出来ます。コルタナが
表示されたときのコルタナのマークの下の部分の余白(コルタナキャンバス)を使って一覧情
報などを出したりといったことが可能です。
コルタナに対応したバックグラウンドタスクを作成するために Windows ランタイムコンポー
ネントのプロジェクトを作成します。(ここでは CortanaApp.Tasks という名前で作成した前
提で話を進めます)CortanaApp プロジェクトに忘れないうちに CortanaApp.Tasks プロジェク
トへの参照を追加しておきます。
コルタナキャンバスにはアイコンを表示することが可能なため、そのアイコンのリソースも準
備しておきます。幅 x 高さが 68x68 と、68x92 と 280x140 の 3 種類が使用できます。ここで
は、logo68x68.png のような名前で Assets フォルダに追加しました。
バックグラウンドタスクのクラスを VoiceCommandTask という名前で作成します。そして、
Package.appxmanifest に登録をします。コルタナに関する設定は GUI が提供されていないた
324
め、Package.appxmanifest を XML エディターで開いて Extensions 属性の箇所を以下のように
編集します。
<Extensions>
<uap:Extension Category="windows.appService"
EntryPoint="CortanaApp.Tasks.VoiceCommandTask">
<uap:AppService Name="VoiceCommandTask"/>
</uap:Extension>
<uap:Extension Category="windows.personalAssistantLaunch" />
</Extensions>
EntryPoint が、先ほど作成したバックグラウンドタスクのクラス名である点と Category の名
前を間違えなければ問題ないと思います。次に、VoiceCommand 定義ファイルを作成します。
フォアグラウンドと異なる点は、Navigate タグが VoiceCommandService タグに変わったとこ
ろです。VoiceCommandService タグの Target で先ほど Package.appxmanifest で定義した
AppService の Name を指定します。
<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<CommandSet xml:lang="ja-jp" Name="LaunchAppSample_ja-jp">
<AppName>サンプル</AppName>
<Example>サンプル起動</Example>
<Command Name="BackgroundSample">
<Example>アイコンを見せて</Example>
<ListenFor RequireAppName="BeforePhrase">アイコンを見せて {size}</ListenFor>
<Feedback>アイコンを表示します</Feedback>
<VoiceCommandService Target="VoiceCommandTask"/>
</Command>
<PhraseList Label="size">
<Item>小さい</Item>
<Item>中ぐらい</Item>
<Item>大きい</Item>
</PhraseList>
</CommandSet>
</VoiceCommands>
325
フォアグラウンドと同じように、アプリ起動時などに、このファイルをシステムにインストー
ルします。
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(
await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///VoiceCommands.xml")));
そして、バックグラウンドタスクに処理を書きます。コードを以下に示します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.VoiceCommands;
using Windows.Storage;
namespace CortanaApp.Tasks
{
public sealed class VoiceCommandTask : IBackgroundTask
{
private BackgroundTaskDeferral Deferral { get; set; }
private VoiceCommandServiceConnection Connection { get; set; }
public async void Run(IBackgroundTaskInstance taskInstance)
{
var details = (AppServiceTriggerDetails)taskInstance.TriggerDetails;
if (details.Name != "VoiceCommandTask") { return; }
this.Deferral = taskInstance.GetDeferral();
taskInstance.Canceled += TaskInstance_Canceled;
this.Connection =
VoiceCommandServiceConnection.FromAppServiceTriggerDetails(details);
326
this.Connection.VoiceCommandCompleted += Connection_VoiceCommandCompleted;
var voiceCommand = await this.Connection.GetVoiceCommandAsync();
// Command の名前に応じて処理を分岐
switch (voiceCommand.CommandName)
{
case "BackgroundSample":
await
this.ProcessBackgroundSampleAsync(voiceCommand.Properties["size"].First());
break;
default:
var userMessage = new VoiceCommandUserMessage
{
DisplayMessage = "認識失敗",
SpokenMessage = "認識に失敗しました",
};
await
this.Connection.ReportFailureAsync(VoiceCommandResponse.CreateResponse(userMessage));
break;
}
this.Deferral.Complete();
}
private async Task ProcessBackgroundSampleAsync(string size)
{
var userMessage = new VoiceCommandUserMessage();
// 表示テキスト
userMessage.DisplayMessage = "アイコン表示を認識しました";
// コルタナさんがしゃべるテキスト
userMessage.SpokenMessage = "アイコンを表示します";
// サイズに応じてタイルを変える
var tiles = new Dictionary<string, VoiceCommandContentTile>
{
{
327
"小さい",
// 68x68
new VoiceCommandContentTile
{
// タイルのサイズ
ContentTileType =
VoiceCommandContentTileType.TitleWith68x68IconAndText,
// タイトルとテキスト
Title = "68x68 です",
TextLine1 = "一行目のデータです",
// アイコン画像
Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/logo68x68.png")),
// タイルをクリックしたときにアプリに渡す引数
AppLaunchArgument = "68x68",
}
},
{
"中ぐらい",
// 68x92
new VoiceCommandContentTile
{
ContentTileType =
VoiceCommandContentTileType.TitleWith68x92IconAndText,
Title = "68x92 です",
TextLine1 = "一行目のデータです",
Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/logo68x92.png")),
AppLaunchArgument = "68x92",
}
},
{
"大きい",
// 280x140
new VoiceCommandContentTile
328
{
ContentTileType =
VoiceCommandContentTileType.TitleWith280x140IconAndText,
Title = "280x140 です",
TextLine1 = "一行目のデータです",
TextLine2 = "二行目のデータです",
TextLine3 = "三行目のデータです",
Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-
appx:///Assets/logo280x140.png")),
AppLaunchArgument = "280x140",
}
},
};
// userMessage とタイルのリストからレスポンスを生成する
var response = VoiceCommandResponse.CreateResponse(
userMessage,
// ここではタイルは 1 つだけだけど複数表示可能
new[] { tiles[size] });
// 結果を通知する
await this.Connection.ReportSuccessAsync(response);
}
private void Connection_VoiceCommandCompleted(VoiceCommandServiceConnection
sender, VoiceCommandCompletedEventArgs args)
{
this.Deferral?.Complete();
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender,
BackgroundTaskCancellationReason reason)
{
this.Deferral?.Complete();
}
}
}
329
長いですがやっていることはシンプルです。taskInstance から GetDeferral を取得して Run メ
ソッドが終了してもタスクは実行中であることを示すようにします。次に、
VoiceCommandServiceConnection を TriggerDetails から作成しています。これがコルタナと
の接続をつかさどるクラスになります。そこから VoiceCommand を取得して、
CommandName プロパティを参照することで認識した Command の名前がわかります。
あとは、VoiceCommandUserMessage と VoiceCommandContentTile のリストを作成して
VoiceCommandResponse を作成して VoiceCommandServiceConnection の
ReportSuccessAsync で通知するだけです。VoiceCommandContentTile で指定した
AppLaunchArgument がアプリ起動時の引数に渡されます。これに応答するには、App クラス
の OnActivated メソッドに以下のように記述します。
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
if (args.Kind == ActivationKind.Protocol)
{
var e = (ProtocolActivatedEventArgs)args;
var frame = Window.Current.Content as Frame;
if (frame == null)
{
frame = new Frame();
Window.Current.Content = frame;
}
var decoder = new WwwFormUrlDecoder(e.Uri.Query);
frame.Navigate(typeof(CortanaPage),
$"{decoder.GetFirstValueByName("LaunchContext")}");
}
Window.Current.Activate();
}
Uri の LaunchContext というパラメータで飛んでくるので WwwFormUrlDecoder というパラ
メータを解析するクラスを利用して、値を取り出しています。
330
アプリケーションを 1 度実行して VoiceCommand 定義ファイルを登録します。登録したらコ
ルタナさんに「サンプル 大きいアイコンを見せて」などのように話しかけると、以下のように
アイコンがコルタナに表示されます。
アイコンをタップすると、アイコンのサイズが画面に表示されます。
331
15. アプリ間連携
ここでは、UWP のアプリ間連携について説明します。
15.1. AppService
最初に、別アプリのバックグラウンドタスクと通信できる AppService について説明します。
AppServiceSample.Server という名前で、プロジェクトを作成します。そして、
AppServiceSample.Server.Tasks という名前で Windows ランタイムコンポーネントを作成し
て、そこの中に以下のような IBackgroundTask の実装クラスを作成します。
using System;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Foundation.Collections;
namespace AppServiceSample.Server.Tasks
332
{
public sealed class SampleAppServiceTask : IBackgroundTask
{
private BackgroundTaskDeferral Deferral { get; set; }
public void Run(IBackgroundTaskInstance taskInstance)
{
this.Deferral = taskInstance.GetDeferral();
taskInstance.Canceled += this.TaskInstance_Canceled;
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
if (details != null && details.Name == "SampleAppServiceTask")
{
details.AppServiceConnection.RequestReceived +=
this.AppServiceConnection_RequestReceived;
}
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender,
BackgroundTaskCancellationReason reason)
{
this.Deferral?.Complete();
}
private async void AppServiceConnection_RequestReceived(AppServiceConnection sender,
AppServiceRequestReceivedEventArgs args)
{
// メッセージの処理開始
var messageDeferral = args.GetDeferral();
// パラメータを取得する
var command = (string)args.Request.Message["Command"];
// パラメータに応じてレスポンスを作成する
var responseMessage = new ValueSet();
switch (command)
{
case "Greet":
333
responseMessage.Add("Message", $"{DateTimeOffset.Now}: Hello world");
break;
default:
responseMessage.Add("Message", $"Unknown command: {command}");
break;
}
// レスポンスを返す
await args.Request.SendResponseAsync(responseMessage);
// メッセージの処理完了
messageDeferral.Complete();
}
}
}
まず、Run メソッドで GetDeferral を呼び出し、Run メソッドが終了しても終了しないように
しておきます。その後、TriggerDetails を AppServiceTriggerDetails にキャストして Name が
意図したものだったら、AppServiceConnection の RequestReceived イベントを受信するように
します。このイベントでアプリ間の通信の受信処理と応答の作成処理を行います。
RequestReceived イベントハンドラでは、イベント引数の GetDeferral を呼び出してメッセー
ジの処理の開始をします。イベント引数の Request の Message で渡された値が取得できるの
で、そこから Command というキーで値を取得しています。結果は ValueSet で作成して、Add
メソッドで値を追加できます。ここでは Greet という Command がわたってきたらメッセージ
を返し、そうでない場合はエラーメッセージを返すようにしています。最後に
SendResponseAsync でメッセージを送り返して、messageDeferral で処理の完了をしていま
す。
AppServiceSample.Server プロジェクトで AppServiceSample.Server.Tasks プロジェクトを参照
に追加して Package.appxmanifest で以下のように AppService を登録します。ここで指定する
名前の項目が、先ほどのプログラム内でチェックしている名前と同じにします。ここでは
334
「SampleAppServiceTask」です。あとは、エントリポイントにクラス名を設定します。
次に、作成した AppService を呼び出すために必要なアプリケーションのファミリ名を取得しま
す。これは、MainPage のコンストラクタあたりに以下のコードを書いて取得します。
Debug.WriteLine(Package.Current.Id.FamilyName);
アプリを実行すると、デバッグウィンドウに以下のようにファミリ名が表示されるので控えて
おきます。
ffe770b3-39b5-4b44-976e-50964885e963_bhx988h6hv66m
次に、この AppService を呼び出すアプリを作成します。AppServiceSample.Client という名前
で UWP アプリを作成します。画面に Button を置いて Click イベントのコードを以下のように
します。
private AppServiceConnection Connection { get; set; }
public async void Click()
{
if (this.Connection == null)
{
this.Connection = new AppServiceConnection
{
AppServiceName = "SampleAppServiceTask",
PackageFamilyName = "ffe770b3-39b5-4b44-976e-50964885e963_bhx988h6hv66m",
335
};
var status = await this.Connection.OpenAsync();
if (status != AppServiceConnectionStatus.Success)
{
this.Connection = null;
await new MessageDialog($"Error: {status}").ShowAsync();
return;
}
}
var message = new ValueSet();
message.Add("Command", "Greet");
var response = await this.Connection.SendMessageAsync(message);
await new MessageDialog($"{response.Status}: {response.Message["Message"]}").ShowAsync();
}
基本的に AppServiceConnection を作成して、AppServiceName(Package.appxmanivest で設定
したもの)と PackageFamilyName(Package.Current.Id.FamilyName で取得したもの)を設定
して OpenAsync を呼び出して、ValueSet で必要なパラメータを作成して SendMessageAsync
をするという流れです。戻り値は Status 結果の状態を、Message で戻り値の ValueSet が取得
できます。実行して、ボタンを押すと以下のように表示されます。
336
15.2. 別 ア プリ の 起 動
15.2.1.プ ロ トコ ル に よ る アプ リ の 起動
特定のプロトコル(sampleapp:など)で別アプリから起動されるアプリを作成できます。
Package.appxmanifest の「宣言」タブでプロトコルを追加して、ロゴと名前を設定します。名
前がプロトコルになります。小文字の英字にする必要があります。ここでは sampleapp にしま
した。
プロトコルによりアプリが起動された場合は、App クラスの OnActivated メソッドが呼び出さ
れます。引数の Kind が Protocol か確認して、引数を ProtocolActivatedEventArgs にキャスト
して使います。ProtocolActivatedEventArgs の Data プロパティに呼び出し側から渡された
ValueSet が入っています。ここから値を取得して使うことが出来ます。OnActivated メソッド
のコード例を以下に示します。
protected override void OnActivated(IActivatedEventArgs args)
{
if (args.Kind == ActivationKind.Protocol)
{
var frame = Window.Current.Content as Frame;
if (frame == null)
{
frame = new Frame();
Window.Current.Content = frame;
}
var protocolArgs = (ProtocolActivatedEventArgs)args;
337
var param = protocolArgs.Data["Param"] as string;
frame.Navigate(typeof(MainPage), param);
Window.Current.Activate();
}
}
次に、以下の 1 行を MainPage のコンストラクタに追加してパッケージファミリ名を取得しま
す。
Degug.WriteLine(Package.Current.Id.FamilyName);
呼び出し側では、以下のように LauncherOptions と ValueSet を作ってファミリ名とデータを
セットして Launcher クラスの LaunchUriAsync メソッドで呼び出しを行います。
private async void Button_Click(object sender, RoutedEventArgs e)
{
var options = new LauncherOptions();
options.TargetApplicationPackageFamilyName = "ee4dfeae-b250-4f32-b54d-
92a69b39cd21_bhx988h6hv66m";
var data = new ValueSet();
data.Add("Param", "Hello world");
await Launcher.LaunchUriAsync(new Uri("sampleapp:"), options, data);
}
15.2.2.結 果 を受 け 取 る プ ロト コ ル によ る ア プ リ の起 動
プロトコルによるアプリの起動では一方通行の突き放しでのアプリの起動でしたが、別アプリ
を起動して、結果を呼び出し元に返すということが出来ます。このときの呼び出し元は、以下
のように LaunchUriForResultsAsync で呼び出します。
var options = new LauncherOptions();
options.TargetApplicationPackageFamilyName = "ee4dfeae-b250-4f32-b54d-
92a69b39cd21_bhx988h6hv66m";
var data = new ValueSet();
338
data.Add("Param", "Hello world");
var result = await Launcher.LaunchUriForResultsAsync(new Uri("sampleapp:"), options, data);
if (result.Status == LaunchUriStatus.Success)
{
await new MessageDialog((string)result.Result["Message"]).ShowAsync();
}
結果が返ってくるので、Status プロパティで成功か失敗か確認して Result プロパティで結果の
入った ValueSet が取得できます。
呼び出される側は、App クラスの OnActivated メソッドの引数の Kind プロパティが
ProtocolForResults になっているので、それを確認して ProtocolForResultsActivatedEventArgs
に引数をキャストします。パラメータの受け取り方は、プロトコルによるアプリの起動と同じ
で Data プロパティから ValueSet が取得できます。この ProtocolForResultsActivatedEventArgs
は、あとで完了通知時に必要になるので、App クラスのフィールドに保持しておきます。
public ProtocolForResultsActivatedEventArgs ProtocolForResultsActivatedEventArgs { get; private
set; }
protected override void OnActivated(IActivatedEventArgs args)
{
if (args.Kind == ActivationKind.ProtocolForResults)
{
var frame = Window.Current.Content as Frame;
if (frame == null)
{
frame = new Frame();
Window.Current.Content = frame;
}
this.ProtocolForResultsActivatedEventArgs = (ProtocolForResultsActivatedEventArgs)args;
var param = this.ProtocolForResultsActivatedEventArgs.Data["Param"] as string;
frame.Navigate(typeof(MainPage), param);
Window.Current.Activate();
339
}
}
呼び出し元に結果を返すには、ProtocolForResultsActivatedEventArgs の
ProtocolForResultsOperation プロパティに対して ReportCompleted メソッドを呼び出しま
す。このとき引数に結果の入った ValueSet を渡します。コード例を以下に示します。
public void Click()
{
var args = ((App)App.Current).ProtocolForResultsActivatedEventArgs;
// 何か処理
var message = new ValueSet();
message.Add("Message", $"{DateTimeOffset.Now}: Hello world");
args.ProtocolForResultsOperation.ReportCompleted(message);
}
16. 複数デバイスに対応したアプリの作成
16.1. ア ダ プテ ィ ブ コ ー ド
UWP では、すべてのデバイスファミリで使える API だけでも豊富にありますが、個別のデバ
イスファミリでしか使えない API も豊富に提供されています。例えば、UWP アプリで参照の
追加をして「Universal Windows」の「拡張」を選択すると下のほうに「Windows ****
Extensions for the UWP」というのがあります。2016 年 7 月 2 日現在、著者の環境では
Desktop、IoT、Mobile、Team というものがあります。それぞれ、デスクトップ、IoT、モバ
イル、Surface Hub で動作するものになります。これらを参照することで、デバイスファミリ
固有の API が使えるようになります。
340
例えば、Mobile の参照を追加したからといって Windows 10 Mobile でしかアプリが動かなく
なるということはありません。この Mobile の中で定義されている API を呼び出したところを
実行時に処理が通過すると、実行時エラーになります。この実行時エラーを避けるために、デ
バイスファミリ固有の API を呼び出すときは事前に型やメソッドが提供されているか確認して
呼び出す必要があります。こうすることで、モバイルの時にはフル機能で動きつつ、デスクト
ップなら、デスクトップ用に動くということを実現できます。この様な実行環境に合わせて柔
軟に動くコードのことをアダプティブコードといいます。
例えば、Mobile の参照を追加して以下のようなコードを書くと、ハードウェアボタンのカメラ
ボタンを押されたときのみメッセージボックスが表示されるようになります。
using System;
using Windows.Foundation.Metadata;
using Windows.Phone.UI.Input;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
341
namespace AdaptiveCodeApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// 型の存在をチェックしてから使う
if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
HardwareButtons.CameraPressed += this.HardwareButtons_CameraPressed;
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
// 型の存在をチェックしてから使う
if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
HardwareButtons.CameraPressed -= this.HardwareButtons_CameraPressed;
}
}
private async void HardwareButtons_CameraPressed(object sender, CameraEventArgs e)
{
// モバイルのみ呼び出される
await new MessageDialog("カメラボタンが押されました").ShowAsync();
}
342
}
}
デスクトップではカメラボタンがないので何もおきません。
モバイルで起動して、カメラボタンを押すとメッセージボックスが表示されます。
343
型の有無を判定する IsTypePresent 以外にも、IsEnumNameValuePresent、IsEventPresent、
IsMethodPresent、IsPropertyPresent、IsReadOnlyPropertyPresent、
IsWritablePropertyPresent など、様々なものの存在有無をチェックするメソッドが提供されて
います。詳細については、以下の API ドキュメントを参照してください。
https://msdn.microsoft.com/library/windows/apps/dn949001
344
16.2. ア ダ プテ ィ ブ UI
UWP アプリはデスクトップやモバイルや TV など様々な大きさの画面で動作することが求め
られます。このような様々な大きさの画面に対して個別に最適なものを作るよりも、以下の画
面サイズをブレークポイントとして、画面を設計することが推奨されています。
 320epx
 640epx
 1024exp
 1366epx
詳細については、以下の公式ドキュメントを参照してください。
https://msdn.microsoft.com/ja-jp/windows/uwp/layout/screen-sizes-and-breakpoints-for-
responsive-design
これらの画面サイズに応じて見た目を変える方法について、ここでは説明したいと思います。
例として 640epx 以下だと縦並びレイアウトで、それ以上だと横並びレイアウトになるような
ものを実現したいと思います。
これを実現するためには RelativePanel と VisualStateManager と AdaptiveTrigger を使うのが
簡単です。RelativePanel で相対的な位置関係でレイアウトを行い、AdaptiveTrigger で画面サ
イズによって VisualState を切り替え、VisualState によって RelativePanel 内のコンテンツの相
対的な位置関係を調整するというやり方です。
まず、XAML で以下のように 2 つの Rectangle を横に並べます。RelativePanel で並べている点
がポイントです。
<Page x:Class="AdaptiveUIApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdaptiveUIApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
345
mc:Ignorable="d">
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Rectangle x:Name="RectangleHeader"
Width="320"
Height="320"
Fill="Red" />
<Rectangle x:Name="RectangleContent"
Width="320"
Height="400"
Fill="LightBlue"
RelativePanel.RightOf="RectangleHeader"/>
</RelativePanel>
</Page>
実行すると以下のようになります。
346
これに VisualStateManager を追加していきます。AdaptiveTrigger を使って 641epx までのとき
のステート(Wide)とそれ以下(Narrow)のステートの切り替えを定義しています。
RelativePanel のプロパティを書き換えることで、Rectangle を横並びから縦並びに変えていま
す。
<Page x:Class="AdaptiveUIApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdaptiveUIApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LayoutGroup">
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="641" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RectangleContent.(RelativePanel.RightOf)"
Value="" />
<Setter Target="RectangleContent.(RelativePanel.Below)"
Value="RectangleHeader" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
347
<Rectangle x:Name="RectangleHeader"
Width="320"
Height="320"
Fill="Red" />
<Rectangle x:Name="RectangleContent"
Width="320"
Height="400"
Fill="LightBlue"
RelativePanel.RightOf="RectangleHeader"/>
</RelativePanel>
</Page>
実行すると以下のようになります。
横幅が広いと横並びになります。
横幅が 640epx 以下になると縦並びになります。
348
モバイルで起動しても、同じように縦並びのレイアウトになります。
349
このようにして、画面サイズに応じてレイアウトを動的に切り替えます。
17. UserControl とテンプレートコントロール
UWP での再利用可能なコントロールの作成方法である UserControl とテンプレートコントロ
ールの作成方法について説明します。
17.1. UserControl
UserControl は Page を作成するのと同じ要領で再利用可能なコントロールを作成する機能で
す。非常に手軽に作成可能なため、再利用可能なコントロール作成の最初の選択肢としておす
すめです。ここでは NumericUpDown コントロールを UserControl で作成してみたいと思いま
す。UserControl を作成するには、「新しい項目の追加」で「ユーザー コントロール」を選択
します。ここでは NumericUpDown という名前で新規作成しました。
まず、値を管理するための依存関係プロパティを Value という名前で定義します。
350
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace UserControlApp
{
public sealed partial class NumericUpDown : UserControl
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new
PropertyMetadata(0));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public NumericUpDown()
{
this.InitializeComponent();
}
}
}
次に、XAML で見た目を定義します。
<UserControl x:Class="UserControlApp.NumericUpDown"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UserControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
351
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind Value, Mode=OneWay}"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}"
Grid.RowSpan="2"/>
<RepeatButton Content="Up"
Click="{x:Bind RepeatButtonUp_Click}"
Width="50"
Grid.Column="1" />
<RepeatButton Content="Down"
Click="{x:Bind RepeatButtonDown_Click}"
Width="50"
Grid.Row="1"
Grid.Column="1" />
</Grid>
</UserControl>
最後にイベントハンドラを作成します。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace UserControlApp
{
public sealed partial class NumericUpDown : UserControl
{
public static readonly DependencyProperty ValueProperty =
352
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new
PropertyMetadata(0));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public NumericUpDown()
{
this.InitializeComponent();
}
public void RepeatButtonUp_Click()
{
this.Value++;
}
public void RepeatButtonDown_Click()
{
this.Value--;
}
}
}
これで完成です。後は通常のコントロールと同じようにページに置くことが出来ます。例とし
て MainPage に置いてみます。
<Page x:Class="UserControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UserControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
353
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:NumericUpDown x:Name="NumericUpDown" />
<TextBlock>
<Run Text="入力値は" />
<Run Text="{x:Bind NumericUpDown.Value, Mode=OneWay}" />
<Run Text="です。" />
</TextBlock>
</StackPanel>
</Page>
実行すると以下のようになります。
非常に簡単に再利用可能なコントロールが作成可能だということが感じ取っていただけたと思
います。
17.2. テ ン プレ ー ト コ ン トロ ー ル
354
次に、もう 1 つの再利用可能なコントロール作成方法であるテンプレートコントロールについ
て説明したいと思います。テンプレートコントロールを作成するには、「新しい項目の追加」
で「テンプレート コントロール」を選択します。ここでは、UserControl と同じ
NumericUpDown をテンプレートコントロールで作ってみようと思います。まず、
UserControl と同じように Value 依存関係プロパティを作成します。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace TemplateControlApp
{
public sealed class NumericUpDown : Control
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new
PropertyMetadata(0));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public NumericUpDown()
{
this.DefaultStyleKey = typeof(NumericUpDown);
}
}
}
次に見た目を定義していきます。UserControl は Page と同じようにデザイナが提供されていま
したが、テンプレートコントロールでは、ぱっと見どこで見た目を定義するのかわからないと
思います。テンプレートコントロールでは、Themes/Generic.xaml で見た目を定義します。
Generic.xaml を開くと NumericUpDown に対する Style が定義されていると思います。ここの
355
Template を書き換えることで見た目を定義していきます。UserControl と同じように Grid を
置いて TextBlock と RepeatButton を置いていきます。違うのは、イベントハンドラとの紐づ
けは、ここでは行わないということです。
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TemplateControlApp">
<Style TargetType="local:NumericUpDown" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NumericUpDown">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Value, RelativeSource={RelativeSource
Mode=TemplatedParent}}"
Style="{StaticResource BodyTextBlockStyle}"
VerticalAlignment="Center"
Grid.RowSpan="2" />
<RepeatButton x:Name="ButtonUp"
Content="Up"
Width="50"
Grid.Column="1" />
356
<RepeatButton x:Name="ButtonDown"
Content="Down"
Width="50"
Grid.Column="1"
Grid.Row="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
見慣れない Binding の指定方法は、今回作成しているコントロールテンプレートのコントロー
ルのプロパティとバインドするための書き方になります。これで、NumericUpDown に定義し
た Value と TextBlock の Text プロパティを紐づけています。次に、ボタンの Click との処理の
紐づけを行います。これは、OnApplyTemplate というメソッドをオーバーライドして行いま
す。GetTemplateChild メソッドで、上記の Style 内で定義した x:Name の値でコントロールを
取得することが出来るのでボタンを取得します。取得できたら Click イベントと購読するよう
にします。このメソッドは複数回呼ばれる可能性があるので、クリーンアップ処理も入れてお
きます。Click イベントでは Value プロパティをインクリメント・デクリメントしています。
コード全体は以下のようになります。
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
namespace TemplateControlApp
{
[TemplatePart(Name = nameof(NumericUpDown.ButtonUp), Type = typeof(ButtonBase))]
[TemplatePart(Name = nameof(NumericUpDown.ButtonDown), Type = typeof(ButtonBase))]
public sealed class NumericUpDown : Control
{
357
private ButtonBase ButtonUp { get; set; }
private ButtonBase ButtonDown { get; set; }
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new
PropertyMetadata(0));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public NumericUpDown()
{
this.DefaultStyleKey = typeof(NumericUpDown);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.ButtonUp != null)
{
this.ButtonUp.Click -= this.ButtonUp_Click;
}
if (this.ButtonDown != null)
{
this.ButtonDown.Click -= this.ButtonDown_Click;
}
this.ButtonUp = this.GetTemplateChild("ButtonUp") as ButtonBase;
if (this.ButtonUp != null)
{
this.ButtonUp.Click += this.ButtonUp_Click;
358
}
this.ButtonDown = this.GetTemplateChild("ButtonDown") as ButtonBase;
if (this.ButtonDown != null)
{
this.ButtonDown.Click += this.ButtonDown_Click;
}
}
private void ButtonUp_Click(object sender, RoutedEventArgs e)
{
this.Value++;
}
private void ButtonDown_Click(object sender, RoutedEventArgs e)
{
this.Value--;
}
}
}
TemplatePart 属性は、この名前でこの型がテンプレート内に存在していることを外部に示すた
めの属性になります。なくてもエラーにはなりません。これで、Page に置いて普通のコントロ
ールのように使用が可能です。
<Page x:Class="TemplateControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TemplateControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:NumericUpDown x:Name="NumericUpDown" />
<TextBlock>
359
<Run Text="入力値は" />
<Run Text="{x:Bind NumericUpDown.Value, Mode=OneWay}" />
<Run Text="です。" />
</TextBlock>
</StackPanel>
</Page>
実行すると以下のような見た目になります。
UserControl よりもテンプレートコントロールのほうが、作成が難しく煩雑です。では、テン
プレートコントロールのメリットはなんでしょうか。それは、テンプレートコントロールは、
通常のコントロールと同じように Template プロパティを使って ControlTemplate を差し替え
ることが出来るという点です。UserControl では、これが出来ません。例として先ほどのペー
ジの NumericUpDown の見た目を差し替えてみたいと思います。XAML を以下に示します。
<Page x:Class="TemplateControlApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
360
xmlns:local="using:TemplateControlApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:NumericUpDown x:Name="NumericUpDown">
<local:NumericUpDown.Template>
<ControlTemplate TargetType="local:NumericUpDown">
<StackPanel>
<RepeatButton x:Name="ButtonUp"
Content="▲" />
<TextBlock>
<Run Text="[" />
<Run Text="{Binding Value, RelativeSource={RelativeSource
Mode=TemplatedParent}}" />
<Run Text="]" />
</TextBlock>
<RepeatButton x:Name="ButtonDown"
Content="▼" />
</StackPanel>
</ControlTemplate>
</local:NumericUpDown.Template>
</local:NumericUpDown>
<TextBlock>
<Run Text="入力値は" />
<Run Text="{x:Bind NumericUpDown.Value, Mode=OneWay}" />
<Run Text="です。" />
</TextBlock>
</StackPanel>
</Page>
実行すると以下のようになります。見た目が差し変わっていることが確認できます。
361
18. UWP でのアプリの設計
UWP アプリを含む XAML をベースとしたアプリケーションの開発では一般的に MVVM パタ
ーンという設計手法で開発が行われます。ここでは MVVM の基本的な考えと、UWP アプリ
に対応した MVVM フレームワークである Prism for Windows Runtime について簡単に紹介し
たいと思います。
18.1. MVVM パ タ ー ン とは
MVVM パターンとは、アプリケーションを Model と ViewModel と View に分割して考える設
計パターンです。Wikipedia の MVVM パターンのページを以下に示します。
https://ja.wikipedia.org/wiki/Model_View_ViewModel
Wikipedia の図を引用しますが、MVVM パターンは以下のように図示されます。
362
Wikipedia の MVVM パターンのページから引用
簡単に言うと Model は、アプリケーションのコアロジックを持ったレイヤーになります。
ViewModel から操作され、Model からイベント通知などで ViewModel にフィードバックをか
えします。ViewModel は、名前の通り View をモデル化したものです。画面に強く紐づいた画
面のための Model であるとも言えます。View は UWP アプリでは XAML とコードビハインド
で構成されます。ViewModel と対話しますが、一般的にデータバインディングを使って
ViewModel と紐づけます。
具体的には、Model クラスは INotifyPropertyChanged を実装して ViewModel に対してプロパ
ティの変更通知で状態変更を伝えます。他には、Model のメソッドの戻り値で結果を
ViewModel に通知します。ViewModel も INotifyPropertyChanged を実装して View に対して
変更通知を行います。ユーザーのアクションには ICommand を実装したクラスを公開したり、
View からのメソッド呼び出しによって応答します。
18.2. Prism for Windows Runtime
実際に MVVM パターンで簡単なアプリケーションを作ってみようと思います。MVVM パター
ンのアプリケーションを開発するためのフレームワークとして Prism と呼ばれるライブラリが
あります。Prism は、もとは WPF のために開発されたライブラリですが現在は、UWP や
Xamarin.Forms など XAML 系プラットフォームにひろく対応したライブラリになっていま
す。MVVM で開発を行うために必要な各種便利な機能が提供されていたり、オブジェクトの
インスタンスの持ち回りといった、めんどくさい問題を簡単に解決してくれる DI コンテナと
363
の連携機能ももっているため使用すると楽に開発が出来ます。Prism の提供する代表的な機能
を以下に示します。
 INotifyPropertyChanged を実装するのを楽にするための基本クラス
 デリゲートを渡すことで Execute メソッドや CanExecute メソッドに処理を渡すことが出
来る ICommand の実装クラス
 ViewModel での画面遷移の仕組み
 App クラスの基本クラス提供による App クラスの実装の簡略化
 中断時のデータ保存機能
 ViewModel での画面遷移のコールバック(OnNavigatedTo, OnNavigatedFrom)の提供
 DI コンテナ(Unity)との連携
 メッセンジャー機能
 入力値の検証機能
 View の DataContext に自動で ViewModel を設定する機能
18.2.1.足 し 算ア プ リ の 作 成
Prism の機能を確認するために、簡単な足し算機能をもったアプリを作成してみたいと思いま
す。最初の画面で 2 つの数字を入力してボタンを選択すると、画面遷移して足し算の結果が表
示されるというものです。PrismAddApp という名前で UWP アプリを作成します。そして、
NuGet から以下のパッケージをインストールして Prism 関連のライブラリをインストールしま
す。
 Prism.Unity
364
App.xaml を開いて以下のように書き換えます。
<Prism:PrismUnityApplication x:Class="PrismAddApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PrismAddApp"
xmlns:Prism="using:Prism.Unity.Windows"
RequestedTheme="Light">
</Prism:PrismUnityApplication>
Prism のアプリケーションの基本クラスは PrismUnityApplication になるため、そのように
XAML を構成しています。次に App.xaml.cs を開いて PrismUnityApplication を継承するよう
に書き換えます。
using Prism.Unity.Windows;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
namespace PrismAddApp
{
sealed partial class App : PrismUnityApplication
{
protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
this.NavigationService.Navigate("Main", args.Arguments);
return Task.CompletedTask;
365
}
}
}
基本的に、OnLaunchApplicationAsync メソッドをオーバーライドして使います。ここで
NavigationService プロパティで取得できる INavigationService インターフェースのインスタン
スに対して、Navigate メソッドを呼び出して画面遷移を行います。画面遷移は文字列ベースで
行います。Main という文字列を渡すと、デフォルトの挙動では Views 名前空間の MainPage
クラスへ遷移するルールになっています。では、遷移先の View を作成します。既存の
MainPage.xaml を削除して、Views 名前空間を作成して、そこに MainPage.xaml を作成しま
す。
この MainPage に ViewModel を DataContext に紐づけます。DataContext への ViewModel の
紐づけは、XAML に Prism.Windows.Mvvm 名前空間にある ViewModelLocator というクラス
の AutoWireViewModel 添付プロパティを設定します。コードは以下のようになります。
<Page x:Class="PrismAddApp.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PrismAddApp.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Mvvm="using:Prism.Windows.Mvvm"
Mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
</Grid>
</Page>
この設定を行うことで ViewModels 名前空間の MainPageViewModel というクラスが自動的に
設定されるようになります。デフォルトでは ViewModels 名前空間のページ名 ViewModel とい
うルールですが、このルールはカスタマイズすることも出来ます。ViewModels 名前空間を作
成して、MainPageViewModel クラスを作成して以下のように記述します。ViewModel は基本
366
クラスである ViewModelBase クラスが提供されているため、それを継承します。うまく動い
ているか確認するために Message プロパティを定義しています。この Message プロパティのよ
うに SetProperty メソッドを使うことで、PropertyChanged が自動で発行されるようになりま
す。
using Prism.Windows.Mvvm;
namespace PrismAddApp.ViewModels
{
public class MainPageViewModel : ViewModelBase
{
private string message = "Hello world";
public string Message
{
get { return this.message; }
set { this.SetProperty(ref this.message, value); }
}
}
}
MainPage.xaml に Message プロパティにバインドした TextBlock を置きます。
<Page x:Class="PrismAddApp.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PrismAddApp.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Mvvm="using:Prism.Windows.Mvvm"
Mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
367
<TextBlock Text="{Binding Message}"
Style="{StaticResource HeaderTextBlockStyle}" />
</Grid>
</Page>
実行すると、Hello world と表示されます。App クラスで画面遷移がされて MainPage に遷移し
ます。そして、MainPage の DataContext に MainPageViewModel が設定されて Message プロ
パティがバインドされて表示されています。
View と ViewModel のひな型が出来たので Model を作成していきます。Models 名前空間(こ
この名前は任意でかまいませんし、別アセンブリにわけてもかまいません。デフォルトで特に
ルールなどはありません)に AppModel クラスを作成して以下のように記述します。
using Prism.Windows.Validation;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace PrismAddApp.Models
368
{
public class AppModel : ValidatableBindableBase
{
private string lhs = "0";
[Required(ErrorMessage = "Lhs is required")]
[RegularExpression("[0-9]+", ErrorMessage = "Lhs is number")]
public string Lhs
{
get { return this.lhs; }
set { this.SetProperty(ref this.lhs, value); }
}
private string rhs = "0";
[Required(ErrorMessage = "Rhs is required")]
[RegularExpression("[0-9]+", ErrorMessage = "Rhs is number")]
public string Rhs
{
get { return this.rhs; }
set { this.SetProperty(ref this.rhs, value); }
}
private double answer;
public double Answer
{
get { return this.answer; }
set { this.SetProperty(ref this.answer, value); }
}
public void Add()
{
if (this.GetAllErrors().Any())
{
369
this.Answer = double.NaN;
return;
}
this.Answer = int.Parse(this.Lhs) + int.Parse(this.Rhs);
}
}
}
この AppModel クラスを Prism の管理している DI コンテナ(Unity)の中に登録します。こう
することで、ViewModel へ自動的にインスタンスを渡したりといったことが可能になります。
App クラスで ConfigureContainer メソッドをオーバーライドして、DI コンテナのに登録を行
っています。RegisterTypeIfMissing メソッドが DI コンテナへの登録を行うメソッドで第一引
数が登録する実体の型、第二引数がコンテナから取得するときに指定する型(インターフェー
スなどを実装しているクラスなどの場合には、ここでインターフェースの型を渡します)、第
三引数がシングルトンで管理するかどうかになります。今回は、AppModel 型をシングルトン
で登録しています。
using Prism.Unity.Windows;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Microsoft.Practices.Unity;
using PrismAddApp.Models;
namespace PrismAddApp
{
sealed partial class App : PrismUnityApplication
{
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.RegisterTypeIfMissing(typeof(AppModel), typeof(AppModel), true);
}
370
protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
this.NavigationService.Navigate("Main", args.Arguments);
return Task.CompletedTask;
}
}
}
次に MainPageViewModel を作成します。AppModel から必要なだけプロパティを公開してい
ます。そして、DelegateCommand という ICommand の実装クラスを使って View に対して
ViewModel が出来る操作を公開しています。Command は、Lhs プロパティと Rhs プロパティ
を監視して CanExecuteChanged イベントを発行するという指定をしています。
using Prism.Commands;
using Prism.Windows.Mvvm;
using Prism.Windows.Navigation;
using PrismAddApp.Models;
using System.Linq;
namespace PrismAddApp.ViewModels
{
public class MainPageViewModel : ViewModelBase
{
private AppModel Model { get; }
private INavigationService NavigationService { get; }
public string Lhs
{
get { return this.Model.Lhs; }
set { this.Model.Lhs = value; this.OnPropertyChanged(nameof(Lhs)); }
}
public string Rhs
{
371
get { return this.Model.Rhs; }
set { this.Model.Rhs = value; this.OnPropertyChanged(nameof(Rhs)); }
}
public DelegateCommand AddCommand { get; }
public MainPageViewModel(AppModel model, INavigationService navigationService)
{
this.Model = model;
this.NavigationService = navigationService;
this.AddCommand = new DelegateCommand(() =>
{
this.NavigationService.Navigate("Answer", null);
},
() => !this.Model.GetAllErrors().Any())
.ObservesProperty(() => this.Lhs)
.ObservesProperty(() => this.Rhs);
}
}
}
Model の入力エラーが無い状態で AddCommand が実行されると画面遷移を行っています。画
面遷移は、コンストラクタ引数に INavigationService を受け取ることで自動的に画面遷移に使
うクラスのインスタンスが注入されます。
View 側の実装を行います。View でコンパイル時バインディングを使うために DataContext を
型を指定して公開するプロパティをコードビハインドに定義します。
using PrismAddApp.ViewModels;
using Windows.UI.Xaml.Controls;
namespace PrismAddApp.Views
{
public sealed partial class MainPage : Page
372
{
public MainPageViewModel ViewModel => this.DataContext as MainPageViewModel;
public MainPage()
{
this.InitializeComponent();
}
}
}
そして、画面で Lhs プロパティと Rhs プロパティをバインドするための TextBox と
AddCommand をバインドするための AppBarButton を定義します。
<Page x:Class="PrismAddApp.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PrismAddApp.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Mvvm="using:Prism.Windows.Mvvm"
Mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Icon="Add"
Label="Add"
Command="{x:Bind ViewModel.AddCommand}" />
</CommandBar>
</Page.BottomAppBar>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Header="Lhs"
Text="{Binding Lhs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Header="Rhs"
Text="{Binding Rhs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
373
</Page>
次に、AnswerPage を作成します。Views 名前空間に AnswerPage を作って、ViewModels 名前
空間に AnswerPageViewModel を作成します。AnswerPageViewModel は画面遷移時に
AppModel の Add メソッドを呼び出して計算をしています。そして、計算結果を View に公開
するためのプロパティを定義しています。このプロパティの変更があったことを通知するため
に、AppModel の PropertyChanged を購読して、必要に応じて自分の PropertyChanged イベン
トを発火しています。
using Prism.Windows.Mvvm;
using Prism.Windows.Navigation;
using PrismAddApp.Models;
using System.Collections.Generic;
namespace PrismAddApp.ViewModels
{
public class AnswerPageViewModel : ViewModelBase
{
private AppModel Model { get; }
public double Answer => this.Model.Answer;
public AnswerPageViewModel(AppModel model)
{
this.Model = model;
}
public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary<string, object>
viewModelState)
{
base.OnNavigatedTo(e, viewModelState);
this.Model.PropertyChanged += this.Model_PropertyChanged;
this.Model.Add();
}
374
public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string,
object> viewModelState, bool suspending)
{
base.OnNavigatingFrom(e, viewModelState, suspending);
if (!suspending)
{
this.Model.PropertyChanged -= this.Model_PropertyChanged;
}
}
private void Model_PropertyChanged(object sender,
System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(AppModel.Answer):
this.OnPropertyChanged(nameof(Answer));
break;
}
}
}
}
View は、コードビハインドで ViewModel プロパティの定義と答えを表示するための
TextBlock が置いてあるだけのシンプルな画面です。
using PrismAddApp.ViewModels;
using Windows.UI.Xaml.Controls;
namespace PrismAddApp.Views
{
public sealed partial class AnswerPage : Page
{
public AnswerPageViewModel ViewModel => this.DataContext as AnswerPageViewModel;
public AnswerPage()
375
{
this.InitializeComponent();
}
}
}
XAML は以下のようになります。
<Page x:Class="PrismAddApp.Views.AnswerPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PrismAddApp.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Mvvm="using:Prism.Windows.Mvvm"
Mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{x:Bind ViewModel.Answer, Mode=OneWay}"
Style="{StaticResource HeaderTextBlockStyle}" />
</Grid>
</Page>
実行すると以下のようになります。入力値が不正な時は、AppBarButton が押せなくなってい
ます。
376
数字を入力すると Add ボタンが押せるようになります。
377
Add ボタンを押すと答えの画面に遷移します。
378
18.2.2.基 本 的な ク ラ ス の 使い 方
ここでは、基本的な Prism のクラスの使い方を説明します。
18.2.2.1.BindableBase クラス
INotifyPropertyChanged インターフェースを実装したクラスになります。以下のようにプロパ
ティを定義することで、PropertyChanged を発行してくれるプロパティが定義できます。
private string input;
public string Input
{
get { return this.input; }
set { this.SetProperty(ref this.input, value); }
}
18.2.2.2.ValidatableBindableBase クラス
379
DataAnnotations による入力値の検証をサポートするクラスです。基本的な使い方は
BindableBase と同じですが、プロパティに DataAnnotations の各種検証用属性をつけることで
入力値の検証を行うことが出来ます。Input というプロパティが必須入力のクラスの定義例を
以下に示します。
using Prism.Windows.Validation;
using System.ComponentModel.DataAnnotations;
namespace ValidatableBindableBaseApp.ViewModels
{
class InputDataViewModel : ValidatableBindableBase
{
private string input;
[Required(ErrorMessage = "入力してください")]
public string Input
{
get { return this.input; }
set { this.SetProperty(ref this.input, value); }
}
}
}
デフォルトでは、プロパティに値が設定されたタイミングで値の検証が走ります。 そのほかに
任意のプロパティの値の検証や、全てのプロパティの値の検証を行うメソッドが提供されてい
ます。
 ValidateProperty:プロパティ名指定での検証
 ValidateProperties:全プロパティの検証
メソッドの戻り値は bool 型で、false が返るとエラーがあることを示しています。
エラー結果の取得は Errors プロパティで取得します。このクラスにはインデクサが定義されて
いて、そこにプロパティ名を渡すことでエラーメッセージのコレクションが取得できます。こ
380
れを画面で Binding することで、エラーメッセージのコレクションが取得できます。
ViewModel に Input という名前で先ほどのクラスを定義した画面でエラーメッセージの表示を
実現する方法を以下に示します。
<Page x:Class="ValidatableBindableBaseApp.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ValidatableBindableBaseApp.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mvvm="using:Prism.Windows.Mvvm"
mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="入力項目"
Style="{StaticResource CaptionTextBlockStyle}" />
<TextBox Text="{x:Bind ViewModel.InputData.Input, Mode=TwoWay}" />
<ItemsControl ItemsSource="{Binding InputData.Errors[Input], Mode=OneWay}"
Foreground="Red" />
<Button Content="チェック"
Click="{x:Bind ViewModel.Alert}" />
</StackPanel>
</Page>
以下のような感じの画面になります。
381
エラーの有無の判定は、GetAllErrors メソッドに要素が含まれているかどうかで判定できま
す。 コード例を以下に示します。
public async void Alert()
{
if (this.InputData.GetAllErrors().Any())
{
var dlg = new MessageDialog("HasError");
await dlg.ShowAsync();
}
else
{
var dlg = new MessageDialog("NoError");
await dlg.ShowAsync();
}
}
382
通常はプロパティがセットされたタイミングで値の検証がされますが、これを無効化すること
ができます。 IsValidationEnabled プロパティを false に設定することで無効化できます。
IsValidationEnabled を false に設定したときは ValidateProperties を呼び出すことで強制的に全
プロパティの検証を行えます。
18.2.2.3.DelegateCommand クラス
コンストラクタに Execute 時に実行される処理と CanExecute 時に実行される処理を渡して使
う ICommand の実装クラスです。RaiseCanExecuteChanged メソッドを呼び出すことで
CanExecuteChanged イベントを発行します。ObserveProperty メソッドを使うことで、指定し
たプロパティを監視して、プロパティに変更があったタイミングで RaiseCanExecuteChanged
を実行するように指定が可能です。
public DelegateCommand AddCommand { get; }
public コンストラクタ()
{
// メソッドを渡してもラムダ式を渡しても OK
this.AddCommand = new DelegateCommand(this.AddExecute, this.AddCanExecute)
// Hoge プロパティを監視する
.ObserveProperty(() => this.Hoge);
}
private void AddExecute() { … }
private bool AddCanExecute() { … }
コマンド実行時に CommandParameter を受け取る DelegateCommand<T>というクラスもあ
ります。Execute と CanExecute の処理が引数を受け取るようになる以外の使い方は同じになり
ます。
18.2.2.4.ViewModelBase クラス
ViewModel の基本クラスになります。BindableBase に対して画面遷移時のコールバックメソッ
ドを追加したものになります。OnNavigatedTo メソッドが画面に来たときの処理で
OnNavigatedFrom メソッドが画面から去るときの処理になります。OnNavigatedFrom メソッ
383
ドは、suspending 引数で画面から去る理由が画面遷移のせいなのか、サスペンドに入るためな
のかを確認することが出来ます。また、引数の viewModelState を使うことで、サスペンド時に
データを一時保存するための領域が提供されています。サスペンド時に viewModelState にデー
タを格納して OnNavigatedTo メソッドで値を取り出して使用します。
また、画面遷移時に渡されるパラメータは NavigatedToEventArgs の Parameter を使って取得
できます。
public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary<string, object>
viewModelState)
{
base.OnNavigatedTo(e, viewModelState);
var parameter = e.Parameter;
// 画面に来た時の処理
}
public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string, object>
viewModelState, bool suspending)
{
base.OnNavigatingFrom(e, viewModelState, suspending);
if (!suspending)
{
// 画面から離れる時の処理
}
else
{
// サスペンド時の処理
}
}
この他に、ViewModelBase クラスを継承したクラスのプロパティに RestorableState 属性をつ
けることで、サスペンド時に自動で保存を行って復帰時に自動で値を復元してくれるといった
機能もあります。単純な値の保存などは、viewModelState を使うよりも、そちらのほうが簡単
にできます。
384
18.2.2.5.INavigationService インターフェース
Prism の管理している DI コンテナ(Unity)の中で管理されているインターフェースです。
Navigate メソッドで画面名とパラメータを渡して画面遷移を行います。GoBack メソッド、
GoFoward メソッドで画面遷移の履歴を戻ったり進んだりできます。CanGoBack メソッド、
CanGoFoward メソッドで戻ったり進めたりできるかといった判定が出来ます。
INavigationService を使うには、ViewModel クラスなどのコンストラクタで INavigationService
を受け取るようにして使用します。
private INavigationService NavigationService { get; }
public コンストラクタ(INavigationService navigationService)
{
this.NavigationService = navigationService;
}
public void Foo()
{
// NextPage へ 10 を引数に渡して遷移する
this.NavigationService.Navigate(“Next”, “10”);
}
18.2.2.6.ISessionStateService インターフェース
ISessionStateService インターフェースも Prism の DI コンテナ(Unity)の中で管理されてい
るインターフェースです。使用するには ViewModel クラスなどのコンストラクタで受け取る
ようにします。SessionState プロパティが Dictionary<string, object>型なので、ここに任意の
値(シリアライズ可能であることと後述する条件を満たすことが必要です)を格納すること
で、サスペンド時にも保存されるように値を管理してくれます。あくまで一時保存領域のた
め、永続的に保存したい値などは別途 ApplicationData クラスを使って保存してください。
プリミティブ型以外の独自のクラスを格納する場合は、App クラスの
OnRegisterKnownTypesForSerialization メソッドで以下のように RegisterKnownType で登録
する必要があります。
protected override void OnRegisterKnownTypesForSerialization()
385
{
base.OnRegisterKnownTypesForSerialization();
this.SessionStateService.RegisterKnownType(typeof(Hoge));
this.SessionStateService.RegisterKnownType(typeof(Fuga));
}
18.2.2.7.IDeviceGestureService インターフェース
IDeviceGestureService インターフェースも Prism の DI コンテナ(Unity)の中で管理されて
いるインターフェースです。使用するには ViewModel クラスなどのコンストラクタで受け取
るようにします。戻るボタンへの対応やデスクトップでのタイトルバーの戻るボタンの表示・
非表示対応や、電話のハードウェアの戻るボタンカメラボタンへの対応サポート機能がありま
す。
IDeviceGestureService には、各種ハードウェアの有無を問い合わせるための以下のプロパティ
が定義されています。
 IsHardwareCameraButtonPresent
 IsHardwareBackButtonPresent
 IsKeyboardPresent
 IsMousePresent
 IsTouchPresent
名前の通り上から順番に、カメラボタン、戻るボタン、キーボード、マウス、タッチが提供さ
れているか確認できます。さらに、ハードウェアボタンとマウスに関しては各イベントが提供
されています。
 GoBackRequested
 CameraButtonPressed
 CameraButtonHalfPressed
386
 CameraButtonReleased
 MouseMoved
順番に戻る処理、カメラボタンが押されたか、半押しされたか、離されたか、マウスが動いた
かといったイベントが取れます。
また、このクラスは Prism のデフォルトの挙動のデスクトップで動かすとタイトルバーに戻る
ボタンが表示されるといった機能も司っています。これを非表示にするには App クラスで
DeviceGestureService の作成機能を以下のようにオーバーライドする必要があります。
// App.xaml.cs
protected override IDeviceGestureService OnCreateDeviceGestureService() =>
new DeviceGestureService { UseTitleBarBackButton = false };
18.2.2.8.IEventAggregator インターフェース
IEventAggregator インターフェースも Prism の DI コンテナ(Unity)の中で管理されているイ
ンターフェースです。使用するには ViewModel クラスなどのコンストラクタで受け取るよう
にします。IEventAggregator は、イベントの仲介役のクラスです。このクラスを仲介役にして
クラス間で疎結合なイベントのやり取りが行えます。IEventAggregator は、T GetEvent<T>()
メソッドを持つだけのシンプルなインターフェースです。GetEvent メソッドの型引数には、通
常 PubSubEvent<T>クラスの派生クラスを指定します。以下のようなクラスを定義します。
using Prism.Events;
namespace EventAggregatorSample.Infrastructure
{
public class InputValueEvent : PubSubEvent<InputValue>
{
}
public class InputValue
{
public int Value { get; set; }
}
387
}
このクラスをキーにしてイベントのやり取りを行います。IEventAggregator でイベントを発行
するには、以下のようなコードを記述します。
this.EventAggregator
.GetEvent<InputValueEvent>()
.Publish(new InputValue { Value = this.Random.Next(100) });
イベントの購読側では Subscribe メソッドを使ってイベントの購読を行います。Subscribe メソ
ッドの戻り値に対して Dispose メソッドを呼び出すことでイベントの購読解除を行えます。
this.EventAggregator
.GetEvent<InputValueEvent>()
.Subscribe(this.InputValueSubscribe, ThreadOption.UIThread);
// 受信メソッド
private void InputValueSubscribe(InputValue x) { … }
19. まとめ
簡単にですが、UWP アプリを開発するうえで必要になるであろうことを書き綴ってきまし
た。ここから先、何を見ていけばいいのかを書いて最後のまとめとしたいと思います。
まず、公式ドキュメントです。これを書くにあたっても参考にしました。ここで書いていない
ことも載っていますので、あんなことが出来ないか?と思ったら目次を展開してみてくださ
い。
https://developer.microsoft.com/ja-jp/windows/develop
そして、GitHub にあがっている公式のサンプルプログラムも是非ダウンロードして確認して
ください。このサンプルプログラムは正直難解ですが、かなりの機能が網羅されているサンプ
ルプログラム集になっています。XAML と名前のつくものからやるのが、見た目に直結してい
たわかりやすいと思うのでお勧めします。その他のものも、時間のあるときに、どんなものが
あるのか起動だけでもして確認しておくと引き出しが増えると思います。
https://github.com/Microsoft/Windows-universal-samples
388
以上、簡単にではありますがまとめとします。最後まで読んでいただきありがとうございまし
た。

More Related Content

PDF
WPF4.5入門
PDF
Xamarin.forms入門
PDF
Reactive extensions入門v0.1
PDF
ドメイン駆動設計 基本を理解する
PDF
関数プログラミング入門
PDF
がんばらなくても C# で Single Page Web アプリケーションが書けてしまう「Blazor」とは
PDF
オブジェクト指向の設計と実装の学び方のコツ
PDF
世界でいちばんわかりやすいドメイン駆動設計
WPF4.5入門
Xamarin.forms入門
Reactive extensions入門v0.1
ドメイン駆動設計 基本を理解する
関数プログラミング入門
がんばらなくても C# で Single Page Web アプリケーションが書けてしまう「Blazor」とは
オブジェクト指向の設計と実装の学び方のコツ
世界でいちばんわかりやすいドメイン駆動設計

What's hot (20)

PPTX
Msを16倍出し抜くwpf開発1回目
PDF
WPF開発での陥りやすい罠
PDF
XAML入門
PPTX
Msを16倍出し抜くwpf開発2回目
PDF
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
PPTX
Photon Fusionのはじめの一歩
PDF
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
PDF
【Unite 2018 Tokyo】『CARAVAN STORIES』のアセットバンドル事例
PDF
MagicOnion入門
PDF
macOSの仮想化技術について ~Virtualization-rs Rust bindings for virtualization.framework ~
PDF
XunitとMoq 公開用
PDF
Unityでオンラインゲーム作った話
PDF
【BS4】時は来たれり。今こそ .NET 6 へ移行する時。
PDF
.NET 最新ロードマップと今押さえておきたい技術要素
PDF
Unity開発で使える設計の話+Zenjectの紹介
PDF
【Unite Tokyo 2019】Render Streaming - WebRTC を用いたストリーミングソリューション
PDF
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
PDF
[CEDEC 2021] 運用中タイトルでも怖くない! 『メルクストーリア』におけるハイパフォーマンス・ローコストなリアルタイム通信技術の導入事例
PDF
年の瀬!リアルタイム通信ゲームサーバ勉強会
PDF
【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~
Msを16倍出し抜くwpf開発1回目
WPF開発での陥りやすい罠
XAML入門
Msを16倍出し抜くwpf開発2回目
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
Photon Fusionのはじめの一歩
【Unite 2018 Tokyo】そろそろ楽がしたい!新アセットバンドルワークフロー&リソースマネージャー詳細解説
【Unite 2018 Tokyo】『CARAVAN STORIES』のアセットバンドル事例
MagicOnion入門
macOSの仮想化技術について ~Virtualization-rs Rust bindings for virtualization.framework ~
XunitとMoq 公開用
Unityでオンラインゲーム作った話
【BS4】時は来たれり。今こそ .NET 6 へ移行する時。
.NET 最新ロードマップと今押さえておきたい技術要素
Unity開発で使える設計の話+Zenjectの紹介
【Unite Tokyo 2019】Render Streaming - WebRTC を用いたストリーミングソリューション
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
[CEDEC 2021] 運用中タイトルでも怖くない! 『メルクストーリア』におけるハイパフォーマンス・ローコストなリアルタイム通信技術の導入事例
年の瀬!リアルタイム通信ゲームサーバ勉強会
【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~
Ad

Similar to かずきのUWP入門 (20)

PDF
20150530 めとべや東京8 universal windows platform appの画面開発
PPTX
わんくま名古屋 #37 (20151114) Windows 10 UWP アプリ開発入門(実践編)
PDF
Visual Studio 2015 リリース記念 勉強会 universal windows platform app
PPTX
Universal Windows app 入門
PDF
わんくま名古屋#36 (20150725) Windows 10 ユニバーサル Windows アプリ開発入門
PPTX
Uwpアプリケーション開発入門
PPTX
仙台IT文化祭(2017年) ユニバーサル windows プラッ トフォーム (uwp) アプリ開発概要
PDF
初心者でも Windows 10 Mobile アプリを作りたい!
PPTX
Universal windows platformの新機能をおさえよう
PPTX
Windows 8時代のアプリ開発
PDF
[MR08] 知っておくべき UWP アプリ開発の A to Z
PDF
PDF
ユニバーサル Windowsプラットフォーム(UWP)アプリの開発と配布
PDF
Universal Apps (UWP)
PDF
はじめての UWP アプリ開発
PDF
Windows 10 対応のデスクトップアプリを 作る技術(事前公開版)
PPTX
Uwpハンズオン参加レポート
PDF
第8回 業開中心会議 「Windows 10 ユニバーサルアプリの概要」
PDF
Slug 3-windows phone7helloworld-classmethod-ryuichi-nonaka
PPT
20050903
20150530 めとべや東京8 universal windows platform appの画面開発
わんくま名古屋 #37 (20151114) Windows 10 UWP アプリ開発入門(実践編)
Visual Studio 2015 リリース記念 勉強会 universal windows platform app
Universal Windows app 入門
わんくま名古屋#36 (20150725) Windows 10 ユニバーサル Windows アプリ開発入門
Uwpアプリケーション開発入門
仙台IT文化祭(2017年) ユニバーサル windows プラッ トフォーム (uwp) アプリ開発概要
初心者でも Windows 10 Mobile アプリを作りたい!
Universal windows platformの新機能をおさえよう
Windows 8時代のアプリ開発
[MR08] 知っておくべき UWP アプリ開発の A to Z
ユニバーサル Windowsプラットフォーム(UWP)アプリの開発と配布
Universal Apps (UWP)
はじめての UWP アプリ開発
Windows 10 対応のデスクトップアプリを 作る技術(事前公開版)
Uwpハンズオン参加レポート
第8回 業開中心会議 「Windows 10 ユニバーサルアプリの概要」
Slug 3-windows phone7helloworld-classmethod-ryuichi-nonaka
20050903
Ad

More from 一希 大田 (20)

PDF
.NET 7 での ASP.NET Core Blazor の新機能ピックアップ
PDF
Power Apps + C#
PDF
Azure Static Web Apps を試してみた!
PDF
Visual studio 2019 updates pickup!
PPTX
.NET 5 and Windows app dev
PDF
Uno Platform 触ってみた
PDF
WPF on .NET Core 3.1 で Windows 10 アプリ開発
PDF
.NET Core 3.0 + Windows 10 で WPF 開発
PDF
はじめよう Azure Functions
PDF
Windows 10 対応のデスクトップアプリを作る技術(事前公開版v2)
PDF
Xamarin.Forms アプリケーション 設計パターン
PPTX
WPF on .NET Core 3.0
PDF
Visual Studio 2019 の個人的なお勧め機能(発表時点)
PDF
Visual Studio 2019 の個人的なお勧め機能
PDF
Windows 10 に対応した デスクトップ アプリを作る技術(事前公開版 v2)
PDF
事前公開版 Windows 10 に対応したデスクトップ アプリを作る技術 v1
PDF
スマートスピーカーのバックエンドで Azure を使う方法
PPTX
Visual Studio App center 概要
PDF
はじめての HoloLens セッションの集大成お見せします!
PDF
ペッパソン東の陣 Microsoft 提供 API のご紹介
.NET 7 での ASP.NET Core Blazor の新機能ピックアップ
Power Apps + C#
Azure Static Web Apps を試してみた!
Visual studio 2019 updates pickup!
.NET 5 and Windows app dev
Uno Platform 触ってみた
WPF on .NET Core 3.1 で Windows 10 アプリ開発
.NET Core 3.0 + Windows 10 で WPF 開発
はじめよう Azure Functions
Windows 10 対応のデスクトップアプリを作る技術(事前公開版v2)
Xamarin.Forms アプリケーション 設計パターン
WPF on .NET Core 3.0
Visual Studio 2019 の個人的なお勧め機能(発表時点)
Visual Studio 2019 の個人的なお勧め機能
Windows 10 に対応した デスクトップ アプリを作る技術(事前公開版 v2)
事前公開版 Windows 10 に対応したデスクトップ アプリを作る技術 v1
スマートスピーカーのバックエンドで Azure を使う方法
Visual Studio App center 概要
はじめての HoloLens セッションの集大成お見せします!
ペッパソン東の陣 Microsoft 提供 API のご紹介

かずきのUWP入門

  • 2. 2 目次 1. 本書について........................................................................................................................ 10 2. Universal Windows Platform とは ....................................................................................... 10 2.1. UWP アプリまでの歴史............................................................................................... 11 2.1.1. .NET Framework 3.0 ............................................................................................ 11 2.1.2. Silverlight.............................................................................................................. 11 2.1.3. Windows Phone 7.x .............................................................................................. 11 2.1.4. Windows 8 ............................................................................................................ 12 2.1.5. Universal app........................................................................................................ 12 2.1.6. Universal Windows Platform ................................................................................ 12 2.2. 開発の前に ................................................................................................................... 12 2.3. デバイスファミリ ........................................................................................................ 13 2.4. アダプティブ UI........................................................................................................... 14 2.5. Hello world................................................................................................................... 14 2.5.1. 前準備................................................................................................................... 14 2.5.2. プロジェクトの作成 ............................................................................................. 15 2.5.3. 画面の作成............................................................................................................ 17 2.5.4. プログラムの実行................................................................................................. 21 2.5.5. まとめ................................................................................................................... 25 3. XAML................................................................................................................................... 25
  • 3. 3 4. UWP のコントロールの基本クラス..................................................................................... 30 4.1. DependencyObject....................................................................................................... 30 4.1.1. 依存関係プロパティ ............................................................................................. 31 4.1.2. 添付プロパティ..................................................................................................... 35 4.1.3. スレッド操作 ........................................................................................................ 36 4.2. FrameworkElement ...................................................................................................... 37 4.3. Control ......................................................................................................................... 39 4.4. ContentControl ............................................................................................................ 39 4.5. ItemsControl ................................................................................................................ 39 4.6. Panel ............................................................................................................................ 40 5. データバインディング ......................................................................................................... 41 5.1. 実行時データバインディング ...................................................................................... 41 5.2. コンパイル時データバインディング............................................................................ 48 6. アプリケーションライフサイクル ....................................................................................... 53 6.1. アプリケーションの起動シーケンス............................................................................ 53 6.2. サスペンド時の処理..................................................................................................... 55 6.3. サスペンドからの復帰 ................................................................................................. 56 6.4. アプリケーションライフサイクルに対応したサンプル............................................... 56 7. ローカルデータとローミングデータ ................................................................................... 61 7.1. ローカルアプリデータ ................................................................................................. 62
  • 4. 4 7.2. ローミングデータ ........................................................................................................ 65 7.3. 一時アプリデータ ........................................................................................................ 66 8. Advanced XAML .................................................................................................................. 66 8.1. Style.............................................................................................................................. 66 8.1.1. 定義済みのスタイル ............................................................................................. 69 8.2. アニメーション ............................................................................................................ 70 8.2.1. キーフレームを使ったアニメーション ................................................................ 75 8.2.2. ThemeAnimation.................................................................................................. 76 8.2.3. ThemaTransition.................................................................................................. 79 8.3. Visual State Manager.................................................................................................... 82 8.4. Behavior ....................................................................................................................... 90 8.4.1. インストール ........................................................................................................ 90 8.4.2. 組み込み Behavior ................................................................................................ 91 8.4.3. Behavior の使い方 ................................................................................................ 92 8.4.4. Behavior の作成.................................................................................................... 95 8.4.5. Behavior の細かい使い方 ................................................................................... 103 8.5. DataTemplate ............................................................................................................ 103 8.5.1. ContentControl 系での使用................................................................................ 103 8.5.2. ItemsControl 系での使用.................................................................................... 110 8.6. ControlTemplate........................................................................................................ 114
  • 5. 5 8.6.1. クリック可能なテキストの作成 ......................................................................... 114 8.6.2. コントロールの Visual State............................................................................... 116 9. 代表的なコントロール ....................................................................................................... 120 9.1. レイアウトパネル ...................................................................................................... 120 9.1.1. StackPanel .......................................................................................................... 120 9.1.2. Grid .................................................................................................................... 122 9.1.3. Canvas ................................................................................................................ 126 9.1.4. RelativePanel ...................................................................................................... 128 9.2. Border ........................................................................................................................ 131 9.3. TextBlock ................................................................................................................... 133 9.4. Button ........................................................................................................................ 135 9.5. TextBox...................................................................................................................... 138 9.6. CheckBox ................................................................................................................... 140 9.7. RadioButton ............................................................................................................... 141 9.8. RepeatButton ............................................................................................................. 145 9.9. ToggleSwitch.............................................................................................................. 147 9.10. ToggleButton ......................................................................................................... 149 9.11. ComboBox.............................................................................................................. 150 9.12. CalendarDatePicker ............................................................................................... 153 9.13. CalendarView ......................................................................................................... 156
  • 6. 6 9.14. DatePicker.............................................................................................................. 158 9.15. TimePicker............................................................................................................. 160 9.16. FlipView ................................................................................................................. 162 9.17. Frame ..................................................................................................................... 168 9.18. CommandBar ......................................................................................................... 170 9.19. ListView と GridView............................................................................................. 175 9.19.1. 選択項目の操作................................................................................................... 180 9.20. Image...................................................................................................................... 191 9.20.1. XAML 内での URL............................................................................................. 191 9.20.2. 画像の表示例 ...................................................................................................... 192 9.20.3. ユーザーの指定したファイルの表示.................................................................. 194 9.20.4. 画像の表示時の拡大方法方法............................................................................. 198 9.21. InkCanvas............................................................................................................... 200 9.21.1. ペン以外での入力の受け付け............................................................................. 201 9.21.2. ペンの色・太さを変える .................................................................................... 202 9.21.3. 文字認識 ............................................................................................................. 204 9.22. MapControl ............................................................................................................ 206 9.23. MediaElement ........................................................................................................ 215 9.24. Povot ...................................................................................................................... 217 9.25. PasswordBox .......................................................................................................... 221
  • 7. 7 9.26. ProgressBar ............................................................................................................ 223 9.27. ProgressRing .......................................................................................................... 225 9.28. ScrollViewer ........................................................................................................... 226 9.29. Slider ...................................................................................................................... 231 9.30. WebView ................................................................................................................ 232 9.31. AugoSuggestBox..................................................................................................... 237 9.32. SplitView ................................................................................................................ 248 9.32.1. ハンバーガーメニューの実装............................................................................. 249 10. 共有................................................................................................................................ 253 10.1.1. 共有の送信側の作成 ........................................................................................... 253 10.1.2. 共有の受信側の作成 ........................................................................................... 260 11. バックグラウンドタスク ............................................................................................... 265 11.1.1. バックグラウンドタスクの作成と登録 .............................................................. 265 11.1.2. バックグラウンドタスクとフォアグランドの連携 ............................................ 269 11.1.3. バックグラウンドタスクの追加情報.................................................................. 275 11.1.4. システムイベントへの応答 ................................................................................ 275 12. タイルとトースト .......................................................................................................... 276 12.1.1. タイルやトーストを作成するための補助ツール................................................ 276 12.1.2. ライブタイル ...................................................................................................... 278 12.1.3. セカンダリタイル............................................................................................... 281
  • 8. 8 12.1.4. トースト ............................................................................................................. 284 13. プッシュ通知.................................................................................................................. 293 14. コルタナ連携.................................................................................................................. 313 14.1.1. フォアグラウンドアプリの起動 ......................................................................... 313 14.1.2. バックグラウンドアプリの起動 ......................................................................... 323 15. アプリ間連携.................................................................................................................. 331 15.1. AppService.............................................................................................................. 331 15.2. 別アプリの起動 ...................................................................................................... 336 15.2.1. プロトコルによるアプリの起動 ......................................................................... 336 15.2.2. 結果を受け取るプロトコルによるアプリの起動................................................ 337 16. 複数デバイスに対応したアプリの作成.......................................................................... 339 16.1. アダプティブコード ............................................................................................... 339 16.2. アダプティブ UI..................................................................................................... 344 17. UserControl とテンプレートコントロール ................................................................... 349 17.1. UserControl............................................................................................................ 349 17.2. テンプレートコントロール .................................................................................... 353 18. UWP でのアプリの設計................................................................................................. 361 18.1. MVVM パターンとは............................................................................................. 361 18.2. Prism for Windows Runtime .................................................................................. 362 18.2.1. 足し算アプリの作成 ........................................................................................... 363
  • 9. 9 18.2.2. 基本的なクラスの使い方 .................................................................................... 378 19. まとめ ............................................................................................................................ 387
  • 10. 10 1. 本書について 本書は、Windows 10 で追加された Universal Windows Platform(以下 UWP)についての入門 書になります。以下の開発環境を前提としています。 1. Windows 10 2. Visual Studio 2015 Update 3 開発言語としては、XAML と C#を使用しています。本書では、C#の言語面での解説は行いま せん。C#についての入門は別途書籍やサイトなどを参考にしてください。 また、UWP を実行する OS は以下のものを想定しています。 1. Windows 10 2. Windows 10 Mobile 2. Universal Windows Platform とは UWP とは、Windows 10 で追加された、プラットフォームで以下の特徴を持ちます。 1. Windows 10 が動くすべてのデバイス上でワンバイナリでアプリが動く (ア) Windows 10 の動くデスクトップ (イ) Windows 10 の動くタブレット (ウ) Windows 10 Mobile の動く電話 (エ) Surface Hub (オ) HoloLens (カ) Windows 10 IoT の動くラズベリーパイなどの IoT デバイス 2. Windows ストアで配布が可能
  • 11. 11 UWP 上で動くアプリのことを UWP アプリといいます。 2.1. UWP ア プ リ ま で の 歴史 ここで少し余談ですが、UWP アプリに至るまでの歴史を著者の主観が入った状態ですが少し 見ていきたいと思います。さかのぼれば何処までもさかのぼれますが、ここでは、.NET Framework 3.0 で追加された WPF からの歴史を見ていきたいと思います。 2.1.1. .NET Framework 3.0 .NET Framework 3.0 で、Windows Presentation Foundation(以下 WPF)が追加されました。 これは、デスクトップアプリケーションを開発するためのプラットフォームで XAML(ザム ル)と呼ばれる XML をベースとした言語で画面を構築して、C#でロジックを記述するという 開発スタイルが登場しました。この開発スタイルは、UWP でも受け継がれています。 2.1.2. Silverlight 次に、Rich Internet Application(以下 RIA)と呼ばれるキーワードが持てはやされた時代が来ま す。この時は、現在のように HTML5 を中心とした開発ではなく Flash や Siliverlight と呼ばれ るブラウザプラグイン上でアプリケーションを構築することが主流だったと私は感じていま す。(HTML/CSS/JavaScript でも開発出来たが、今よりも凄く大変だった) Silverlight は、勢いのあったころは WPF を駆逐するのではないかと錯覚させるほど、機能追加 が活発で、OutOfBrowser というブラウザプラグインなのに、サンドボックス内にありつつデ スクトップアプリケーションのように動作するモードなども追加されました。UWP も大ざっ ぱにみると、サンドボックスの中で動く XAML と C#によって開発されたアプリが動くという ことで、とても似たような雰囲気があります。 2.1.3. Windows Phone 7.x 次に、Windows Phone です。これは、Silverlight がスマートフォンアプリとして動くという点 が特徴です。この時は、Silverlight がこのまま全プラットフォームにひろがっていくのではな いかと感じていました。(現実はそうではありませんでしたが…)
  • 12. 12 2.1.4. Windows 8 Silverlight の波も去り、Microsoft がタッチファーストという考えで作り出し、世間的には Vista 並みに失敗したと認識されている(私は好きですよ)Windows 8 が登場します。 Windows 8 では、ストアアプリと呼ばれる UWP の前身となるアプリケーションプラットフォ ームが登場します。COM の上に構築された Windows Runtime 上で動くアプリケーションがこ こで登場しました。Windows Runtime は、UWP でも使用されています。 2.1.5. Universal app Windows 8.1 と Windows Phone 8.1 で 95%くらい API が共通化されて、ほぼ同一ソースで Windows 8.1 上と Windows Phone 8.1 上で動くアプリケーションのバイナリを、それぞれ作成 することが出来るようになりました。ソースコードレベルとはいえ、同じコードが電話でもパ ソコンでも動くという大きな衝撃を与えてくれたテクノロジです。 2.1.6. Universal Windows Platform そして、UWP に繋がります。Universal app がソースコードの共有だったものを、UWP で は、同一バイナリが Windows 10 の動く様々なデバイスで動くということを実現しました。開 発手法は、WPF が登場してから一貫して XAML と C#による開発です。 HoloLens への対応や Windows 10 への力の入れ具合などから、現在、そして今後も大きく Microsoft が投資していくプラットフォームであることが伺えます。現に XAML などの開発言 語も WPF と比べて機能強化が積極的に行われています。本書では、この WPF から歴史を積み 上げて、現在の最新のプラットフォームである UWP についての開発について取り上げます。 2.2. 開 発 の前 に UWP の開発の話しに入る前に、UWP のアプリの作法であるガイドラインを紹介しておきま す。このガイドラインは、守らなければいけないというものではないですが、UWP のアプリ を作る上で、よくあるナビゲーションパターンや画面パターンなど、多くの有益な情報が記載 されています。
  • 13. 13 是非、Windows ストアなどに提出するようなアプリを作る場合には一読しておくことをお勧め しておきます。 Windows アプリ UX デザイン ガイドライン https://msdn.microsoft.com/ja-jp/mt634411.aspx 上記ページの中にある、「Windows 10 アプリ UX デザイン ガイド」が、それになります。 2.3. デ バ イス フ ァ ミ リ UWP を開発するうえで知っておいたほうがいい概念としてデバイスファミリというものがあ ります。デバイスファミリとは、アプリが動作するプラットフォームのようなものです。 Universal app が Windows 8.1 と Windows Phone 8.1 などの OS を対象としていたのに対し て、UWP ではデバイスファミリを対象とします。デスクトップパソコンでは、デスクトップ デバイスファミリに基づいて実行し、モバイル端末ではモバイルデバイスファミリに基づいて 実行されます。ユニバーサルデバイスファミリと呼ばれる特別なデバイスファミリも存在し、 ユニバーサルデバイスファミリで提供される、API は、全てのデバイスファミリで動作するこ とが保証されています。 デバイスファミリの関係をあらわした図を以下に示します。 各デバイスファミリには、プラットフォームに固有の API が提供されています。これらの API は、実行時に動的に存在を確認して呼び出すアダプティブコードを記述することが出来ます。 デバイスファミリを選択するということは、呼び出せる API 群を決定するということです。先 ほども言った通り、アダプティブコードを記述することでデバイスファミリ固有の API を使用 しつつ、他のデバイスファミリ上でも動作させるといったことも出来るようになっています。
  • 14. 14 必要があれば、デスクトップデバイスファミリ上でしか動作させないといったことも可能で す。 2.4. ア ダ プテ ィ ブ UI UWP は、様々なデバイス上で動作するアプリケーションを、ワンバイナリで作ることが出来 ます。逆に言うと、ワンバイナリで様々な画面サイズで動作するアプリケーションを作らない といけないということになります。このような要件に対応するため、アダプティブ UI を実現 しなければなりません。アダプティブ UI は、画面サイズに応じて最適な画面レイアウトを提 供する UI です。 以下のような小さなモバイル端末から、ホワイトボードサイズの Surface Hub まで様々なデバ イスに対応する必要があります。 (https://msdn.microsoft.com/ja-jp/library/windows/apps/dn894631.aspx より引用) アダプティブ UI のパターンについても、「開発の前に」で紹介したガイドラインに記載があ りますので、併せて確認してみてください。 2.5. Hello world 最初の UWP アプリとして Hello world を作成してみましょう。ボタンを押すと、Hello world というメッセージボックスが表示されます。 2.5.1. 前 準 備 UWP のアプリを開発するためには、Visual Studio 2015 のインストール時に UWP の開発環境 をインストールするように構成する必要があります。選択をせずに、インストールした場合は
  • 15. 15 コントロールパネルからプログラムの追加と削除を選んで、Visual Studio 2015 を選択して変更 を押してから、UWP の開発環境をインストールしてください。 また、最初にやる設定として、パソコンを開発者モードにする必要があります。設定(Win + i)を表示して「更新とセキュリティ」を選んで、「開発者向け」を選択して「開発者モード」 にします。 2.5.2. プ ロ ジェ ク ト の 作 成 メニューの「ファイル」→「新規作成」→「プロジェクト」を選択して、「テンプレート」の 「Visual C#」の「Windows」の「ユニバーサル」にある「空白のアプリ(ユニバーサル Windows)」を選択します。名前の項目に HelloWorld と入力して「OK」を選択してくださ い。
  • 17. 17 1. Assets フォルダ インストール後のプログラムフォルダに表示されるアイコンや、タイルなどに表示される アイコンが入っています。 2. App.xaml/App.xaml.cs アプリケーションのエントリーポイントです。 3. ApplicationInsights.config ApplicationInsights の構成ファイルです。プロジェクト新規作成時に ApplicationInsights を使用しない構成にしていた場合は作成されません。無くても問題ありません。 4. HelloWorld_TemporaryKey.pfx アプリケーションのインストールパッケージを作成するときに使用される鍵です。 5. MainPage.xaml/MainPage.xaml.cs アプリケーション起動時に最初に表示される画面です。 6. Package.appxmanifest アプリケーションのマニフェストファイルです。権限の設定などを行います。 7. project.json NuGet のライブラリの依存関係が記載されています。 2.5.3. 画 面 の作 成
  • 18. 18 Hello world の画面を作成します。MainPage.xaml をダブルクリックして開くとデザイナが表示 されます。 デザイナの下部には、XAML と呼ばれる画面を記述する言語が表示されています。デザイナの 左側にツールボックスが表示されていて、そこにボタンなどの各種コントロールがあります。
  • 20. 20 Button を選択した状態で、プロパティウィンドウを見ると Button の設定を変更できることが わかります。Content プロパティに Say hello と入力すると Button のテキストが Say hello にな ります。
  • 21. 21 Button をデザイナ上でダブルクリックすることで、Button がクリックされたときの処理が生 成されます。MainPage.xaml.cs が以下のようになります。 public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private void button_Click(object sender, RoutedEventArgs e) { } } このように XAML に紐づく C#のファイルをコードビハインドと言います。コードビハインド に処理を書きましょう。button_Click メソッドを以下のように書き換えます。 private async void button_Click(object sender, RoutedEventArgs e) { var dialog = new MessageDialog("Hello world"); await dialog.ShowAsync(); } MessageDialog は、名前空間がデフォルトで using されていないので、「Ctrl + .」を押して using を追加してください。UWP の API は、非同期のものがほとんどです。少しでも処理に 時間がかかるものは全て非同期メソッドとして提供されています。async/await は頻繁に使うの で、知らない方はマスターしておきましょう。 2.5.4. プ ロ グラ ム の 実 行 最後にプログラムの実行を行います。Visual Studio のツールバーを以下のように「x86」が選 択されていることを確認して「▶ローカルコンピューター」を選択します。
  • 22. 22 実行すると Button が置かれた Window が表示されます。Button をクリックすると以下のよう にダイアログが表示されます。 続けて Windows 10 Mobile で動作させてみましょう。「▶ローカルコンピューター」の横の▼ をクリックすると以下のように動作対象のターゲットが選べます。
  • 23. 23 その中から、「Mobile Emulator 10.0.10586 WVGA 4 inch 512MB」(細かいバージョン番号 は異なっている可能性があります)を選択します。選択したあと、再度クリックすると Windows 10 Mobile のエミュレータが起動して Hello world が実行されます。
  • 25. 25 詳しい手順は紹介しませんが、CPU を「ARM」にして「▶Device」にすることで開発者モー ドにした USB で接続された Windows 10 Mobile の実機にアプリを転送して動かすことが出来 ます。Windows 10 Mobile をお持ちの方は試してみてください。 2.5.5. ま と め プロジェクトの作成から、アプリケーションの実行までを紹介しました。また、デザイナを使 って画面をデザイン出来ることや、C#を使って処理を書けることを示しました。そして、 UWP の醍醐味でもある、デスクトップとモバイルで同じアプリケーションが動くということ も示しました。ちょっとした Hello world ですが、色々な物が詰まっています。 3. XAML ここでは、UWP の画面構築に使用する XAML という言語についてみていきます。XAML は、 Hello world の章でデザイナの下に表示されていたもので、XML をベースにして作られた言語 になります。XAML は、画面構築専用の言語ではなくて、汎用的に使えるオブジェクトを組み 立てるための言語になります。ツリー構造を持ったオブジェクトの構築に必要な機能が色々詰 まっています。画面は、Page をルートとしたツリー構造のオブジェクトのため、XAML で記 述するのに、とても適している言語です。 XAML は、XML 名前空間を C#の名前空間に、タグをクラス名に、属性をプロパティ名に対応 させています。また、XML 名前空間には、いくつかの組み込みのものが定義されていて、それ
  • 26. 26 らには x や d や mc などの名前を付けることが一般的です。一般的な Page(UWP での画面を あらわすクラス)の XAML での定義は以下のようになります。 <Page x:Class="XamlBasics.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> </Page> 4 つの XML 名前空間が定義されています。まずは、XAML で定義する名前空間と、様々な便 利機能が定義されている x 名前空間と、デザイナ向けの機能を提供する d 名前空間です。mc 名前空間は、mc:Ignorable で実行時には無視する名前空間を指定するために定義されていま す。x:Class 属性は、コードビハインドクラスのクラス名をあらわしています。この、x:Class 属性で、XAML と裏のロジックのクラスの結び付けが行われています。この形が UWP で扱う XAML の基本的な形になります。 この XAML に紐づくコードビハインドは、以下のようになっています。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace XamlBasics { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } } }
  • 27. 27 XAML のタグが Page タグなので、Page クラスを継承している点がポイントです。また、 XAML で定義された設定を有効化するために、コンストラクタで InitializeComponent メソッ ドを呼び出している点もポイントになります。 以下に XAML の基本機能を使った XAML を示します。 <Page x:Class="XamlBasics.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlBasics" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Content> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.Children> <Button Content="Hello world" Click="Button_Click" /> </Grid.Children> </Grid> </Page.Content> </Page> まず、Button タグに注目してみましょう。Button の Content 属性に Hello world を指定してい ます。これは、Button クラスのインスタンスの Content プロパティに Hello world という文字 列を設定していることになります。このように、とても直感的に属性を使ってプロパティを設 定可能です。また、もう 1 つ Click 属性がありますが、こちらはイベントになります。イベン トでは、コードビハインドで対応するメソッドの名前を定義することで、イベントに対してメ ソッドを追加することが出来ます。 次に、Page と Grid の関係について注目してみましょう。先ほどの Button の例のように、単純 な文字列や数値の場合は属性を使ってプロパティの値を指定できますが、プロパティに設定し たいものが Button や Grid といった他のクラスのインスタンスだった場合に、特別な命名規約 に沿ったタグを使うとプロパティを設定できます。命名規約は、「クラス名.プロパティ名」と
  • 28. 28 いうタグになります。このような命名規約のタグを使うことで、タグの子要素のオブジェクト をプロパティに設定することができます。この構文のことを、「プロパティ要素の構文」とい います。 次に、Grid の Background 属性で指定されている{}で囲まれた部分です。これは「マークアッ プ拡張」といって単純な文字列では指定できないようなオブジェクトを設定するための構文に なります。StaticResource は、後程説明しますが、UWP のリソースと呼ばれるところから、オ ブジェクトを取得してきます。その他に{x:Null}などで null を渡すといったことも可能です。 次にコンテンツ構文です。コンテンツ構文は、オブジェクトに対して 1 つだけ指定可能なコン テンツプロパティというものを、タグの直下に書くことでコンテンツプロパティに自動で設定 出来るものです。例えば、Page では Content プロパティがコンテンツプロパティに指定されて います。また、Grid は Children プロパティがコンテンツプロパティに指定されています。そ のため、先ほどの XAML は、以下のようにシンプルに記述できます。 <Page x:Class="XamlBasics.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlBasics" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Hello world" Click="Button_Click" /> </Grid> </Page> その他の構文として、コレクション構文というものがあります。例えば、Grid の Children は、コレクション型なのですが、ここに複数のタグを追加するとコレクションに複数格納され ます。例えば、Grid に Button を 2 つ以上置くことが出来ます。例えば以下のように書きま す。 <Page x:Class="XamlBasics.MainPage"
  • 29. 29 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlBasics" xmlns:d=http://schemas.microsoft.com/expression/blend/2008tugini xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Hello world" Click="Button_Click" /> <Button VerticalAlignment="Top" Content="Button2" /> <Button VerticalAlignment="Bottom" Content="Button3" /> </Grid> </Page> 次に、任意の名前空間にあるオブジェクトを XAML に組み込む方法を紹介します。XAML は、XML 名前空間を C#の名前空間と対応づけることができます。対応づけかたは、上記の XAML 内の local という XML 名前空間にあるとおり、「using:名前空間」という形の書式にな ります。例えば XamlBasics という名前空間に Person という名前のクラスがある場合、以下の ように記述できます。 <local:Person /> 次に、添付プロパティという機能を紹介します。添付プロパティは、名前の通りオブジェクト 自体には定義されていないプロパティを追加で設定できる機能になります。UWP 上では、主 にコントロールのレイアウトに必要な情報をコントロールに追加するために使用されていま す。例えば、Grid コントロールは、行と列を指定してコントロールを何処に配置するのか指定 できるのですが、そこで、以下のように使用されています。 <Page x:Class="XamlBasics.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlBasics" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  • 30. 30 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <!-- 行の定義 --> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <!-- Grid の 0 行目、1 行目に配置 --> <Button Content="Layout sample" Grid.Row="0" /> <Button Content="Hello world" Grid.Row="1" /> </Grid> </Page> Button タグの Grid.Row という属性で指定している箇所が添付プロパティにあたります。 Button に Grid が持っている Row という添付プロパティの値を設定しています。このように、 他のクラスで定義されたプロパティの値を設定できる点が特徴になります。 最後に、マークアップ拡張について説明します。マークアップ拡張は、複雑なオブジェクトを 簡単に記述するための記法になります。{}で囲んで記述します。例えば{x:Null}とすることで、 null をあらわすことが出来ます。その他に、データバインディングの章で紹介しますが、 {Binding Path=Name}などのようなデータバインディングオブジェクトの生成や、リソースの 参照を行うための{StaticResource KeyName}のようなものがあります。マークアップ拡張のそ れぞれの意味については、各章で後述します。 4. UWP のコントロールの基本クラス ここでは、UWP のコントロールの基本クラスである、DependencyObject について説明を行い ます。 4.1. DependencyObject
  • 31. 31 DependencyObject は、UWP のコントロールに必要な基本的な機能を提供します。提供する機 能は以下の通りです。 1. 依存関係プロパティ 2. 添付プロパティ 3. スレッド操作 順に説明していきます。 4.1.1. 依 存 関係 プ ロ パ テ ィ 依存関係プロパティは、Windows Runtime で使用される特殊なプロパティで、デフォルト値の 提供や、プロパティの値に変更があったときに別のプロパティへの値の伝搬や、アニメーショ ンのサポートや、データバインディングのサポートなど様々な機能を持ったプロパティです。 具体的には、以下のように実装されます。 public class Range : Windows.UI.Xaml.DependencyObject { // プロパティのキー public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(Range), new PropertyMetadata(0, ValueChanged)); // CLR のプロパティとしてラッピング public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } private static void ValueChanged(Windows.UI.Xaml.DependencyObject d, DependencyPropertyChangedEventArgs e) { // 変更があったときの処理
  • 32. 32 } } まず、クラスが DependencyObject クラスを継承していることが必要となります。そして、 DependencyProperty クラスの Register メソッドで依存関係プロパティを登録します。引数 は、プロパティ名、プロパティの型、プロパティを所有する型、メタデータになります。メタ データは、PropertyMetadata 型でコンストラクタの引数は、プロパティのデフォルト値と、プ ロパティに変更があったときのコールバック(コールバックの指定はオプション)になりま す。コールバックは、static なメソッドで、第一引数に値が変更されたオブジェクトと、プロ パティの変更後の値と変更前の値を持ったイベント引数というシグネチャになります。依存関 係プロパティ自体には必須ではないのですが、CLR のプロパティの形(要は普通の C#のプロ パティ)にラップするのが一般的です。こうすることで、コード上から自然にアクセスが可能 になります。CLR のプロパティの形式を使用しない場合と使用する場合のコードを以下に示し ます。 var r = new Range(); // CLR のプロパティを使用しない場合 r.SetValue(Range.ValueProperty, 10); var value = (int)r.GetValue(Range.ValueProperty); // CLR のプロパティのラッパを使用する場合 r.Value = 100; var value2 = r.Value; 依存関係プロパティの PropertyMetadata のコールバックを使うことで、プロパティの値に変更 があったときに、他のプロパティの値に状態を伝搬するなどの処理を書くことが出来ます。例 として Range クラスで、Min と Max プロパティを追加して、Value が必ず Min と Max の間に あるように調整する処理を追加したコードを以下に示します。 public class Range : Windows.UI.Xaml.DependencyObject { // プロパティのキー public static readonly DependencyProperty ValueProperty =
  • 33. 33 DependencyProperty.Register("Value", typeof(int), typeof(Range), new PropertyMetadata(0, ValueChanged public static readonly DependencyProperty MinProperty = DependencyProperty.Register("Min", typeof(int), typeof(Range), new PropertyMetadata(int.MinValue, Min public static readonly DependencyProperty MaxProperty = DependencyProperty.Register("Max", typeof(int), typeof(Range), new PropertyMetadata(int.MaxValue, Max // CLR のプロパティとしてラッピング public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public int Min { get { return (int)GetValue(MinProperty); } set { SetValue(MinProperty, value); } } public int Max { get { return (int)GetValue(MaxProperty); } set { SetValue(MaxProperty, value); } } // コールバック private static void ValueChanged(Windows.UI.Xaml.DependencyObject d, DependencyPropertyChangedEventArgs e { ((Range)d).CourceValue(); }
  • 34. 34 private static void MinChanged(Windows.UI.Xaml.DependencyObject d, DependencyPropertyChangedEventArgs e) { ((Range)d).CourceMin(); ((Range)d).CourceValue(); } private static void MaxChanged(Windows.UI.Xaml.DependencyObject d, DependencyPropertyChangedEventArgs e) { ((Range)d).CourceMax(); ((Range)d).CourceValue(); } // 値を調整するメソッド private void CourceMax() { if (this.Max < this.Min) { this.Max = this.Min; } } private void CourceMin() { if (this.Min > this.Max) { this.Min = this.Max; } } private void CourceValue() { if (this.Max < this.Value)
  • 35. 35 { this.Value = this.Max; } if (this.Min > this.Value) { this.Value = this.Min; } } } このクラスを使って以下のようなコードを記述して動作を確認してみます。 var range = new Range(); Debug.WriteLine($"Min: {range.Min}, Max: {range.Max}, Value: {range.Value}"); range.Min = 0; range.Max = 10; range.Value = 100; Debug.WriteLine($"Min: {range.Min}, Max: {range.Max}, Value: {range.Value}"); range.Max = -1; Debug.WriteLine($"Min: {range.Min}, Max: {range.Max}, Value: {range.Value}"); 実行するとデバッグ時の出力ウィンドウに以下の結果が表示されます。 Min: -2147483648, Max: 2147483647, Value: 0 Min: 0, Max: 10, Value: 10 Min: 0, Max: 0, Value: 0 4.1.2. 添 付 プロ パ テ ィ 次に、XAML でも紹介した添付プロパティについて説明します。添付プロパティは XAML で 説明した通り、オブジェクトに対して別のクラスで定義されたプロパティを取得または設定で きる機能になります。DependencyObject では、以下のようにして添付プロパティを定義しま す。
  • 36. 36 public class AttachedPropertySample { public static readonly DependencyProperty SampleValueProperty = DependencyProperty.RegisterAttached("SampleValue", typeof(int), typeof(AttachedPropertySample), new PropertyMetadata(0)); public static int GetSampleValue(Windows.UI.Xaml.DependencyObject obj) { return (int)obj.GetValue(SampleValueProperty); } public static void SetSampleValue(Windows.UI.Xaml.DependencyObject obj, int value) { obj.SetValue(SampleValueProperty, value); } } DependencyProperty クラスの RegisterAttached メソッドで DependencyProperty クラスのイ ンスタンスを作成する点と、添付プロパティは、CLR の形式のプロパティではなく、static な SetXXX と GetXXX という名前でラッパーを作るという点が異なります。また、添付プロパテ ィを定義するクラスは、必ずしも DependencyObject を継承する必要はありません。 先ほどの Range クラスに、SampleValue 添付プロパティの値を設定して取得するコードは以下 のようになります。 var range = new Range(); // 添付プロパティの値の設定と取得 AttachedPropertySample.SetSampleValue(range, 100); var sampleValue = AttachedPropertySample.GetSampleValue(range); 添付プロパティも PropertyMetadata を使ってデフォルト値とコールバックが指定できます。コ ールバックを使うことで依存関係プロパティと同じように、別のプロパティに対して変更を伝 搬することが出来ます。 4.1.3. ス レ ッド 操 作
  • 37. 37 DependencyObject は、生成されたスレッドに紐づくという特徴があります。別スレッドから 依存関係プロパティなどの操作を行うと例外が発生します。そのため、UWP のコントロール は UI 用のスレッド(UI スレッド)に紐づけられていて、その他のスレッドから操作すること が出来なくなっています。例えば、バックグラウンドでデータを読み込んで UI に反映すると いった処理で、この特徴が問題になってきます。そのようなケースに対応するため、 DependencyObject には、自分が生成されたスレッドに紐づく CoreDispatcher というクラスを 保持しています。このオブジェクトには DependencyObject の Dispatcher プロパティからアク セスが出来ます。CoreDispatcher クラスには、CoreDispatcher が紐づくスレッドで処理を行う ための RunAsync メソッドが定義されていて、以下のように記述することでスレッドを切り替 えて処理をすることが出来ます。 await range.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { // DependencyObject の紐づけられたスレッドで処理を行える }); 第一引数の CoreDiapatcherPriority は処理の実行の優先度で以下の値を指定できます。  Idle 一番優先度が低い。アイドル状態のときに実行される。  Low 低い優先順位。自分より高い優先順位の処理がないと実行される。  Normal 普通の優先順位。順番に実行されます。  High 最高の優先順位。ユーザーアプリケーションでの使用はしない。 4.2. FrameworkElement UWP のコントロールの多くがこのクラスを継承しています。このクラスは、レイアウトや XAML を使ったプログラミングで重要になるデータバインディングや、見た目を共通化するた めの Style や、リソースと呼ばれるデータを定義しておく場所の機能などを提供しています。
  • 38. 38 レイアウトに関するプロパティとして Margin というプロパティがあります。このプロパティ を指定することで、コントロールの内側に余白を設けることが出来ます。例えば Button に余 白を設ける場合は以下のように XAML を記述します。 <Button Margin=”10,5,10,5” Content=”Hello” /> マージンは、左、上、右、下の順番でカンマで区切って指定します。以下のように余白が設定 されます。(黒枠がマージンの外側) リソースは、Resources プロパティとして定義され、そこにブラシや Style などの様々なオブジ ェクトの名前を付けて登録することが出来ます。リソースに定義されたオブジェクトは、 StaticResource マークアップ拡張によって XAML から取得できます。例えば、SolidColorBrush と言う単色塗りつぶしのブラシを定義して、ボタンから参照するコードは以下のようになりま す。 <Page x:Class="XamlBasics.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlBasics" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <SolidColorBrush x:Key="Brush" Color="Red" /> </Page.Resources> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Hello" Background="{StaticResource Brush}" />
  • 39. 39 </Grid> </Page> 赤色のブラシを Brush という名前(x:Key で指定)で定義して、それをボタンの背景色に StaticResource マークアップ拡張を使って指定しています。StaticResource で参照できる値は、 StaticResource を使う場所よりも親のコントロールで定義されている必要があります。例え ば、上記の Brush という名前のリソースを取得する場合は、Button よりも上の階層である Page の Resources で定義されているため取得できています。また、StaticResource マークアッ プ拡張は、親へ親へ Resources を探索していくので、同じ名前のリソースが定義されていた場 合は、より自分に近い場所のリソースを参照します。Page まで親へさかのぼって見つからない 場合は、App クラスの Resources で定義されたものを参照します。App クラスでも見つからな い場合は、組み込みで定義されているリソースを参照します。 4.3. Control FrameworkElement を拡張し、コントロールに必要な基本的な機能を提供します。自分でカス タムコントロールを提供する場合に、このクラスを継承することがあります。主な提供機能は コントロールの見た目を自由に変更することが出来る Template という機能になります。 4.4. ContentControl 単一の要素を表示するためのコントロールになります。Page や Button や UserControl など多 くのコントロールが、このコントロールを継承しています。ContentControl は、Content プロ パティに設定されたデータを非常に柔軟に表示する能力を持っていて、文字列が設定されてい れば文字列を表示し、コントロールが設定されていれば、コントロールを表示します。任意の オブジェクトを表示することも可能で、その場合 ContentTemplate というテンプレートを使っ て見た目を定義できます。また、ContentTemplateSelector などを使うことでオブジェクトを どのように表示するかということをカスタマイズすることも可能です。 4.5. ItemsControl ItemsControl は複数の要素を表示するためのコントロールになります。ItemsSource プロパテ ィに設定された IEnumerable に対して要素を表示します。表示される 1 要素ずつは ContentControl を継承したクラスにホストされ ContentControl で説明したように柔軟に要素
  • 40. 40 を表示することが出来ます。ListBox、ListView などの複数の要素を表示するコントロールは 基本的にこのコントロールを継承しています。 ItemsControl は、INotifyCollectionChanged を実装しているコレクション(厳密には IList も必 要)を ItemsSource に設定することで、コレクションの要素の追加や削除に表示を追随させる ことが出来ます。この INotifyCollectionChanged のデフォルトの実装クラスとして System.Collections.ObjectModel.ObservableCollection<T>クラスが提供されています。このク ラスを ItemsSource に設定することで、コレクションの追加・削除に簡単に追随させることが 出来るようになります。 基本的には、要素は縦に並ぶように配置されますが、ItemsPanel を変更することで縦ならびを 横ならびに変更できます。具体的には以下のような XAML を記述します。 <ItemsControl> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <!-- 横並びにする --> <StackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> このように、ItemsControl は ContentControl のような柔軟な表示をしつつ、ItemsPanel を使 って並びを柔軟に対応できるような作りになっています。 4.6. Panel Panel は、複数のコントロールをホストすることが出来るコントロールです。UWP のレイアウ トシステムによって、ホストしたコントロールを柔軟に配置することが出来ます。Canvas(絶 対座標での要素の配置)、Grid(テーブルレイアウト)、StackPanel(縦や横に積み上げる形 で配置)、RelativePanel(相対位置による配置)などのコントロールがあります。これらのコ ントロールは Children というプロパティにコントロールを格納して使います。Children プロパ ティは XAML で説明したコンテンツコントロールになっているため、以下のようにタグの下に 直接別のコントロールを記述することが出来ます。
  • 41. 41 <StackPanel> <Button Content=”Button1” /> <Button Content=”Button2” /> <Button Content=”Button3” /> </StackPanel> 5. データバインディング 著者が UWP(XAML を使った)アプリケーションで非常に重要な要素と考えるデータバイン ディングについて説明します。この機能を使いこなすことで、見た目とロジックを綺麗に切り 離したアプリケーションを作ることが出来ます。 UWP のデータバインディングには 2 種類のものが存在します。WPF の頃から存在する実行時 データバインディングと、UWP に追加されたコンパイル時データバインディングになりま す。順番に解説していきます。 5.1. 実 行 時デ ー タ バ イ ンデ ィ ン グ 実行時データバインディングは、FrameworkElement で実装されている機能になります。 DataContext というプロパティに格納されたオブジェクトと、コントロールのプロパティの値 の同期をとる機能になります。(厳密にいうと Binding の Source に設定されたオブジェクトの プロパティと、コントロールのプロパティの同期をとる機能になります。何も指定しないとデ フォルトの Binding の Source が DataContext になります。)同期の取り方には、以下の 3 種 類が存在します。 1. OneWay(デフォルト) DataContext に設定されたオブジェクトのプロパティ(ソースと言います)からコントロ ールのプロパティ(ターゲットと言います)に対する 1 方向の値の同期になります。ソー スの値の変更を感知したらターゲットの値が更新されます。ソースの値の変更の感知は、 DependencyProperty の場合は自動的に、そうでない場合は INotifyPropertyChanged の PropertyChanged イベントにより検知を行います。
  • 42. 42 2. TwoWay ソースからターゲットの間を双方向に同期を取ります。ソースからターゲットへの同期の タイミングは OneWay と同じです。 3. OneTime 初回の 1 度だけソースからターゲットに対して値の同期を取ります。 実行時データバインディングには Binding マークアップ拡張を使います。例えば以下のような Person という名前のクラスがあるとします。 public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string name; public string Name { get { return this.name; } set { this.name = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } } private int age; public int Age { get { return this.age; } set { this.age = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age)));
  • 43. 43 } } } Name と Age プロパティを持っていて、INotifyPropertyChanged インターフェースによる変更 通知を実装しています。これを Page の DataContext に以下のように設定します。 <Page x:Class="RuntimeDataBinding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:RuntimeDataBinding" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.DataContext> <local:Person /> </Page.DataContext> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> </Grid> </Page> Grid を StackPanel(子要素を縦や横に並べて表示するコントロール)に置き換えて、以下のよ うに TextBox と TextBlock を置いてデータをバインドさせます。 <Page x:Class="RuntimeDataBinding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:RuntimeDataBinding" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.DataContext> <local:Person /> </Page.DataContext>
  • 44. 44 <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="{Binding Path=Name}" /> <TextBox Text="{Binding Path=Age, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="{Binding Path=Age}" /> </StackPanel> </Page> 最初の TextBox と TextBlock の組が Name プロパティへのデータバインディングで、2 つ目の TextBox と TextBlock の組が Age プロパティへのデータバインディングになります。実行する と TextBox と TextBlock の値が Person クラスを通して同期されていることが確認できます。 Binding マークアップ拡張には、以下のようなプロパティが設定出来ます。  Path ソースのプロパティを指定します。「プロパティ 1.プロパティ 2」のようにオブジェクト
  • 45. 45 を辿るように Path を記述することが出来ます。また、配列などの「プロパティ 1[0]」記 法にも対応しています。Path プロパティは、Binding マークアップ拡張の最初に書く場合 は、省略することが可能です。つまり先ほどのコードの例は「{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}」のように記述することが出来 ます。  Mode OneWay, TwoWay, OneTime を指定します。  UpdateSourceTrigger Default, PropertyChanged, Explicit を指定します。通常、Default は PropertyChanged と 同様の意味になります。TextBox の Text に Binding するときは、Default はフォーカスが 外れた時に値の同期をとります。テキストが変化する度に値の同期をとる場合は上記の例 のように明示的に PropertyChanged を指定します。Explicit を指定した場合は BindingExpression の UpdateSource メソッドを呼び出したタイミングで同期が取られま す。  Converter ソースからターゲットと、ターゲットからソースへ値が同期されるときに値の変換ロジッ クをあらわす IValueConverter を実装したクラスを指定します。  ConverterParameter Converter に渡すパラメータを指定します。  ElementName データバインディングのソースに x:Name で名前を付けたコントロールを指定する場合に 使用します。  FallbackValue バインディングで値を返せない場合に返す値を指定します。  TargetNullValue ターゲットの値が null の時に返す値を指定します。
  • 46. 46  RelativeSource バインディングのソースを相対的に指定します。以下の 2 種類の指定方法があります。 {Binding …, RelativeSource={RelativeSource TemplatedParent}} {Binding …, RelativeSource={RelativeSource Self}} TemplatedParent は、ControlTemplate と呼ばれるコントロールの見た目を定義する場所 で使用します。コントロール自身がソースとなります。 Self は、ターゲットの要素をバインディングのソースに指定します。  Source バインディングのソースを指定します。何も指定しない場合はデフォルトで DataContext プロパティの値が使われます。 ElementName などを使ったデータバインディングの例を以下に示します。まず、以下のよう な IValueConverter インターフェースを実装したクラスを定義します。 public class SanConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { // ソースからターゲット return $"{value}さん"; } public object ConvertBack(object value, Type targetType, object parameter, string language) { // ターゲットからソース方向 // ここでは未サポート throw new NotSupportedException(); } } Converter は、FrameworkElement で紹介したリソースに定義して使用します。では、TextBox に入力した値を使って「○○さん」と表示する TextBlock を作成してみます。 <Page x:Class="RuntimeDataBinding.MainPage"
  • 47. 47 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:RuntimeDataBinding" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <local:SanConverter x:Key="SanConverter" /> </Page.Resources> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="TextBox" /> <TextBlock Text="{Binding Text, ElementName=TextBox, Converter={StaticResource SanConverter}}" /> </StackPanel> </Page> 実行すると、以下のように TextBox の入力に同期して、TextBlock の値が書き換わります。 あまりやる機会はないですが、実行時データバインディングは C#のコードからも設定するこ とが出来ます。例えば、Name というプロパティと TextBox の Text をバインディングする場 合は以下のようなコードになります。 var binding = new Binding { Path = new PropertyPath("Name"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
  • 48. 48 this.TextBox.SetBinding(TextBox.TextProperty, binding); Binding クラスのインスタンスを生成して、Path や Mode などのプロパティを設定したあと に、ターゲットとなるオブジェクトの SetBinding メソッドで、バインディングのターゲットと なる依存関係プロパティ(添付プロパティも可)と、Binding オブジェクトを指定します。 5.2. コ ン パイ ル 時 デ ー タバ イ ン ディ ン グ 実行時データバインディングは、バインディングを実行時に解決しますが、コンパイル時デー タバインディングは、コンパイル時にバインディングの解決を行います。コンパイル時データ バインディングの特徴を以下に示します。  実行時ではなくコンパイル時にバインディングの情報が確定するため実行時データバイン ディングに比べて高速  バインディングのソースが、DataContext ではなく Page になっている  デフォルトの Mode が OneTime になっている 実行時データバインディングとの大きな違いは以上のようになります。コンパイル時データバ インディングには、{x:Bind …}というマークアップ拡張を使います。TextBox と TextBlock を バインドするコードの例を以下に示します。 <Page x:Class="CompileTimeDataBinding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CompileTimeDataBinding" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="TextBox" /> <TextBlock Text="{x:Bind TextBox.Text, Mode=TwoWay}" /> </StackPanel> </Page>
  • 49. 49 x:Name 属性を使って、TextBox を Page のメンバーとして定義しています。そのため、 TextBlock で定義している x:Bind から TextBox という名前で参照できます。実行すると、以下 のようになります。 コンパイル時データバインディングで指定可能なプロパティは以下の通りです。  Path  Mode  TargetNullValue  FallbackValue  Converter  ConverterParameter 各パラメータについては実行時データバインディングと、ほぼ同じになります。また、x:Bind の特徴として、イベントハンドラをバインドできるという点があります。イベントハンドラの バインドは、以下のシグネチャのメソッドに対して行えます。 void Foo(); void Foo(object sender, object eventArgs); void Foo(object sender, XXXXEventArgs eventArgs); // 各イベントのイベント引数の型 例として、以下のようなクラスをバインドしてみたいと思います。 public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged;
  • 50. 50 private string name; public string Name { get { return this.name; } set { this.name = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } } public void ResetName() { this.Name = null; } } XAML を以下に示します。 <Page x:Class="CompileTimeDataBinding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CompileTimeDataBinding" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <local:Person x:Name="Person" /> </Page.Resources> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Text="{x:Bind Person.Name, Mode=TwoWay, TargetNullValue='未入力'}" /> <TextBlock Text="{x:Bind Person.Name, Mode=OneWay}" /> <Button Content="Reset"
  • 51. 51 Click="{x:Bind Person.ResetName}" /> </StackPanel> </Page> Page の Resources で x:Name を使って Page のメンバーとして Person を定義しています。コー ドビハインドで以下のように定義するのと同じようなイメージになります。 public sealed partial class MainPage : Page { public Person Person { get; } = new Person(); } そして、TextBox や TextBlock と Name プロパティをバインドしています。イベントのバイン ドは Button の Click イベントで行っています。Person クラスに定義した ResetName メソッド をバインドしています。実行すると以下のようになります。 TextBox に入力すると、Person クラスの Name プロパティを介して TextBlock に値が同期され ます。(コンパイル時データバインディングの TextBox の Text プロパティへのバインディング は特殊で、フォーカスが外れたときに値の同期が行われます)
  • 53. 53 この他に、コンパイル時データバインディングは実行時にコードから設定できないという点が 実行時データバインディングと異なります。 6. アプリケーションライフサイクル UWP アプリは、デスクトップアプリケーションよりスマートフォンアプリケーションのよう なライフサイクルで動作します。具体的に言うと、デスクトップではアプリケーションは最小 化すると休止状態に移行して、メモリの使用状況によっては OS によってアプリケーションが 終了されることがあります。この終了はアプリケーションから検知することが出来ないため、 休止状態に入るときに終了されてもいいようにアプリケーションで備える必要があります。モ バイルでは、バックグラウンドに回ったときに、休止状態に入ります。この UWP のライフサ イクルを図にすると以下のようになります。 アプリケーションの起動は、プロジェクトの App クラスの OnLaunched メソッドがエントリ ーポイントとなります。このとき、引数の LaunchActivatedEventArgs に直前のアプリの状態 (終了状態だったのかなど)が入っているので、それを見て必要に応じて復帰処理などを行い ます。 6.1. ア プ リケ ー シ ョ ン の起 動 シ ーケ ン ス
  • 54. 54 アプリケーションは、LaunchActivatedEventArgs の PreviousExecutionState の値を見て起動時 の処理を適切に行う必要があります。PreviousExecutionState には、以下の値が格納されてい ます。  NotRunning アプリケーションは実行されていなかった。  Running アプリケーションは実行中。  Suspended アプリケーションは中断状態だった。  Terminated アプリケーションは中断後に終了された。  ClosedByUser アプリケーションはユーザーによって終了された。 アプリケーションの起動時に行わないといけないことは、新規作成したひな形の App クラスに 定義されています。OnLaunched メソッドから不要な処理やコメントを除いたものを以下に示 します。 protected override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; if (rootFrame == null) { rootFrame = new Frame(); if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: 以前中断したアプリケーションから状態を読み込みます }
  • 55. 55 Window.Current.Content = rootFrame; } if (e.PrelaunchActivated == false) { if (rootFrame.Content == null) { rootFrame.Navigate(typeof(MainPage), e.Arguments); } Window.Current.Activate(); } } UWP アプリは、Window.Current を通じてアプリの Window にアクセスできます。この Window の Content プロパティに Frame という画面遷移機能を持ったコントロールを配置し て、その中で画面遷移を行います。Window.Current.Content に値が設定されていないという ことは、終了状態からの起動ということなので、Frame を新規作成して設定します。さらに先 ほど説明した PreviousExecutionState を確認して Terminated の時に、直前の中断時に保存し たデータを使って復元を行います。最後に PrelaunchActivated で直前のアクティブ化状態を確 認して、アクティブでない場合は、必要に応じて画面遷移(Frame の Navigate メソッド)を行 い、自分自身をアクティブ化します。 以上が、アプリケーションの基本的な起動シーケンスになります。 6.2. サ ス ペン ド 時 の 処 理 アプリケーションがサスペンドされるときには、App クラスで Suspending イベントが発生し ます。デフォルトで作成されている App クラスではコンストラクタで Suspending イベントを 購読して、OnSuspending メソッドが呼ばれるようになっています。 public App() { this.InitializeComponent(); this.Suspending += OnSuspending;
  • 56. 56 } private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); //TODO: アプリケーションの状態を保存してバックグラウンドの動作があれば停止します deferral.Complete(); } e.SuspendingOperation.GetDeferral メソッドの呼び出しから Complete メソッドの間で保存処 理を行います。この GetDeferral で取得したオブジェクトの Complete メソッドを呼び出すと 中断時の状態保存が終わったということを通知したことになります。 6.3. サ ス ペン ド か ら の 復帰 あまり利用することはありませんが、サスペンド状態からアプリケーションが終了されないま まフォアグラウンドに復帰したときに呼び出される処理があります。App クラスで Resuming イベントを購読することで処理が出来ます。 6.4. ア プ リケ ー シ ョ ン ライ フ サ イク ル に 対 応 した サ ン プル ここでは、簡単な中断処理に対応したアプリケーションを作成したみたいと思います。 LifecycleApp という名前でアプリケーションを作ります。そして、以下のようなクラスを作成 します。 using System.ComponentModel; namespace LifecycleApp { public class LifecycleAppModel : INotifyPropertyChanged { public static LifecycleAppModel Instance { get; } = new LifecycleAppModel(); public event PropertyChangedEventHandler PropertyChanged; private string value;
  • 57. 57 public string Value { get { return this.value; } set { this.value = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); } } } } このクラスを使って簡単なページを作成します。TextBox にデータを表示するだけの単純なペ ージにします。MainPage.xaml.cs に以下のようなプロパティを定義します。 public sealed partial class MainPage : Page { public LifecycleAppModel Model { get; } = LifecycleAppModel.Instance; public MainPage() { this.InitializeComponent(); } } MainPage.xaml を以下のように編集して LifecycleAppModel の Value と TextBox の Text をバ インドします。 <Page x:Class="LifecycleApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:LifecycleApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 58. 58 mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Text="{x:Bind Model.Value, Mode=TwoWay}" /> </StackPanel> </Page> この状態でアプリケーションが中断されたときにどう動くか確認してみます。アプリケーショ ンをデバッグ起動して、Visual Studio のツールバーで右クリックして「デバッグの場所」にチ ェックを入れます。(チェックが入っている場合は、そのままで大丈夫です)
  • 60. 60 MainPage の TextBox に任意の文字列を入力して「中断とシャットダウン」をします。そし て、再度アプリケーションを起動すると、TextBox の文字が消えていることが確認できます。 ユーザーからしたら、最小化(モバイルの場合はバックグラウンドに回した)したアプリの入 力がクリアされてしまったという挙動になります。これを中断処理に対応して改善します。ま ず、App クラスの OnSuspending メソッドで ApplicationData.Current.LocalSettings を使って データの保存を行います。ApplicationData.Current.LocalSettings.Values(ローカルにデータを 保存するための Dictionary)に InputValue をキーにして LifecycleAppModel の Value プロパテ ィの値を保存します。 private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); ApplicationData.Current.LocalSettings.Values["InputValue"] = LifecycleAppModel.Instance.Value; deferral.Complete(); } 次に、OnLaunched メソッドの Terminated の if 文に、この保存したデータを読み込む処理を 追加します。 if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { if (ApplicationData.Current.LocalSettings.Values.ContainsKey("InputValue")) { LifecycleAppModel.Instance.Value = (string) ApplicationData.Current.LocalSettings.Values["InputValue"];
  • 62. 62  一時アプリデータ アプリデータの保存方法には、設定とファイルという 2 種類があります。設定は、int や string などの基本的な型や DateTime 型や GUID 型などを保存することが出来るディクショナリのよ うな形で提供されます。ファイルは、任意のファイル形式でデータを保存することが出来ま す。 7.1. ロ ー カル ア プ リ デ ータ ローカルアプリデータは、ApplicationData.Current.LocalSettings で設定にアクセスできます。 ApplicationData.Current.LocalFolder でファイルを保存するためのフォルダにアクセスできま す。まず、設定についてみていきます。 設定の一番簡単な使い方は、Values プロパティにディクショナリのようにアクセスすることで す。コード例を以下に示します。 var settings = ApplicationData.Current.LocalSettings; settings.Values["Key"] = "Value"; var value = (string)settings.Values["Key"]; if (settings.Values.ContainsKey("Key")) { // Key が存在する場合 } else { // Key が存在しない場合 } // 削除 settings.Values.Remove(“Key”); ApplicationDataCompositeValue というクラスを使うことで、値をまとめて管理することが出 来ます。以下のようなコードになります。 var settings = ApplicationData.Current.LocalSettings; var composite = new ApplicationDataCompositeValue();
  • 63. 63 composite["Key1"] = "Value1"; composite["Key2"] = "Value2"; settings.Values["compositeValue"] = composite; この他に、ApplicationDataContainer を使って、データを階層化して管理することが出来ま す。この階層は 32 階層まで作成することが出来ます。 var settings = ApplicationData.Current.LocalSettings; // 作成 var container = settings.CreateContainer("Container1", ApplicationDataCreateDisposition.Always); // 値の設定 container.Values["Key1"] = "Value1"; // 存在確認 if (settings.Containers.ContainsKey("Container1")) { // 存在するとき } else { // 存在しないとき } // 削除 settings.DeleteContainer("Container1"); 設定は手軽に使えますが、ファイルを使って任意の形式でローカルアプリデータにデータを保 存することもできます。ファイルにアクセスするコードは以下のようになります。 // フォルダを取得 var folder = ApplicationData.Current.LocalFolder; // ファイルを作成する(存在する場合は置き換える) var file = await folder.CreateFileAsync("sample.txt", CreationCollisionOption.ReplaceExisting); // ファイルに出力する await FileIO.WriteTextAsync(file, "Hello world"); // ファイルから読み込む var data = FileIO.ReadTextAsync(file);
  • 64. 64 ApplicationData.Current.LocalFolder に対して、CreateFileAsync を呼び出すことでファイルを 作成できます。第一引数がファイル名で、第二引数でファイルが存在したときの挙動などを指 定します。上記コードでは、ReplaceExisting を指定して、ファイルが既に存在する場合は、新 しいもので置き換えるという指定をしています。 ファイルの読み書きは FileIO クラスの WriteTextAsync メソッドと ReadTextAsync を使いま す。JSON.NET などの.NET のクラスライブラリと連携するためには System.IO.Stream が必要 になるケースがあります。これは、ファイルの拡張メソッドを使うことで取得が可能になって います。 // 書き込み using (var stream = await file.OpenStreamForWriteAsync()) { // JSON.NET などを使う } // 読み込み using (var stream = await file.OpenStreamForReadAsync()) { // JSON.NET などを使う } アプリケーションのバージョンアップなどにより、設定やファイルに保存しているデータに非 互換が発生してしまった場合は、ApplicationData.Current.SetVersionAsync メソッドを使用し てバージョン管理を行うことが出来ます。SetVersionAsync メソッドは、第一引数にバージョ ン番号を指定し、第二引数にコールバックを指定します。コールバックでは、以下のように現 在のバージョンと要求されたバージョンが確認できます。 await ApplicationData.Current.SetVersionAsync(0, r => { var d = r.GetDeferral(); Debug.WriteLine($"CurrentVersion: {r.CurrentVersion}, DesiredVersion: {r.DesiredVersion}"); // バージョンが違ってたら必要に応じて変換ロジックを走らせる d.Complete();
  • 65. 65 }); SetVersionAsync を呼び出した場合は、バージョン番号が変わっていなくてもコールバックが 呼ばれるので、その点は注意する必要があります。 7.2. ロ ー ミン グ デ ー タ ローカルアプリデータは、デバイスのローカルストレージに保存されます。ここで紹介するロ ーミングデータは、同じアプリのデータを複数のデバイスで同期をとります。この機能を使う ことで、例えばユーザーがデスクトップで途中まで呼んでいた書籍を、外出先で続きから読む といったシナリオが実現可能になります。ローミングデータにも、設定とファイルが存在し て、以下のようにアクセスを行います。 // 設定 var roamingSettings = ApplicationData.Current.RoamingSettings; // ファイル var roamingFolder = ApplicationData.Current.RoamingFolder; RoamingSettings も RoamingFolder も、ローカルアプリデータと同じ方法で使用できます。ロ ーミングデータは、ApplicationData.RoamingStorageQuota で指定されるサイズ(KB 単位)以 上のデータがある場合は、ローミングが実行されません。この値は、Windows 10 Version 1511 で確認したところ 100 という値が反ってきました。 RoamingData がアップデートされたことを検知するには、 ApplicationData.Current.DataChanged イベントを使用します。以下のようなコードになりま す。 public MainPage() { this.InitializeComponent(); ApplicationData.Current.DataChanged += Current_DataChanged; } private void Current_DataChanged(ApplicationData sender, object args) {
  • 66. 66 // RoamingData がアップデートされた } 7.3. 一 時 アプ リ デ ー タ 一時アプリデータは、設定は存在せずにファイルのみになります。以下のようにして一時アプ リデータのフォルダを取得します。 // 一時アプリフォルダを取得する var folder = ApplicationData.Current.TemporaryFolder; フォルダ取得後は、ローカルアプリデータで示した通りファイルへのアクセスが可能です。 8. Advanced XAML ここでは、UWP を作るうえで必須となる XAML の高度な機能を紹介したいと思います。 8.1. Style UWP には、Style と呼ばれるコントロールのプロパティの設定を外だしにして定義する仕組み があります。これを使うことで HTML の CSS のように見た目の定義を別にすることが出来ま す。HTML の CSS と異なる点は、XAML という統一された言語でコントロールのツリー構造 と見た目の定義ができるという点です。HTML の CSS を知っている方向けに説明すると、 HTML の CSS がセレクタによって適用対象を指定して、見た目を定義するのに対して、 XAML の Style は名前を付けてリソースに定義して、コントロールから、それを参照して設定 します。(コントロールのスタイルにインラインで書くことも出来ますが、それならコントロ ールに直接プロパティを設定したほうがいいと思われます) Style の定義は、以下のように Page や App クラスの Resources に Style タグを使って定義しま す。 <Page x:Class="ControlsApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlsApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  • 67. 67 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <!-- MyTextBlock という名前で TextBlock のスタイルを定義 --> <Style x:Key="MyTextBlock" TargetType="TextBlock"> <!-- FontStyle プロパティに Italic を指定 --> <Setter Property="FontStyle" Value="Italic" /> <!-- FontWeight に Bold を指定 --> <Setter Property="FontWeight" Value="Bold" /> </Style> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <!-- StaticResource を使って Style を設定 --> <TextBlock Text="Hello world" Style="{StaticResource MyTextBlock}"/> </Grid> </Page> Style タグの TargetType で、何の型に対する Style なのか指定します。そして、Style タグの中 に Setter タグを使って何のプロパティに、どんな値を設定するのかを指定します。定義した Style は、コントロールにある Style プロパティに StaticResource マークアップ拡張を使って指 定します。上記の XAML で、以下のように TextBlock が太字のイタリック体になります。 Style は、別のスタイルをベースに拡張して作ることも出来ます。Style の BaseOn にベースと なる Style を StaticResource マークアップ拡張で指定します。先ほどの例の MyTextBlock スタ
  • 68. 68 イルを拡張して赤色の設定を追加した ExTextBlock という名前のスタイルを定義した XAML を以下に示します。 <Page x:Class="ControlsApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlsApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <!-- MyTextBlock という名前で TextBlock のスタイルを定義 --> <Style x:Key="MyTextBlock" TargetType="TextBlock"> <!-- FontStyle プロパティに Italic を指定 --> <Setter Property="FontStyle" Value="Italic" /> <!-- FontWeight に Bold を指定 --> <Setter Property="FontWeight" Value="Bold" /> </Style> <!-- MyTextBlock を拡張して赤色にする --> <Style x:Key="ExTextBlock" TargetType="TextBlock" BasedOn="{StaticResource MyTextBlock}"> <Setter Property="Foreground" Value="Red" /> </Style> </Page.Resources> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <!-- StaticResource を使って Style を設定 --> <TextBlock Text="Hello world" Style="{StaticResource MyTextBlock}" /> <TextBlock Text="Hello world2" Style="{StaticResource ExTextBlock}" />
  • 69. 69 </StackPanel> </Page> この XAML を表示すると以下のようになります。ExTextBlock が、MyTextBlock に対して赤 色を追加したものになっていることが確認できます。 8.1.1. 定 義 済み の ス タ イ ル UWP には、いくつかの Style が予め定義されています。この定義は「C:Program Files (x86)Windows Kits10DesignTimeCommonConfigurationNeutralUAP10.0.10586.0Generic」フォルダ の generic.xaml でされています。よく使うものをいくつか紹介します。  HeaderTextBlockStyle ヘッダー用の大きな TextBlock 用 Style です。  SubheaderTextBlockStyle サブヘッダー用のヘッダーより一回り小さな TextBlock 用の Style です。  TitleTextBlockStyle タイトル用の TextBlock 用の Style です。  SubtitleTextBlockStyle サブタイトル用の TextBlock 用の Style です。  BodyTextBlockStyle 本文用の TextBlock 用の Style です。
  • 70. 70  CaptionTextBlockStyle キャプション用の TextBlock 用の Style です。  TextBlockButtonStyle TextBlock の見た目を持ったボタンを定義するための Button 用の Style です。  NavigationBackButtonNormalStyle 戻るボタン用の Button 用の Style です。  NavigationBackButtonSmallStyle 小さな戻るボタン用の Button 用の Style です。 8.2. ア ニ メー シ ョ ン UWP はアニメーションを組み込みでサポートしています。UWP のアニメーションは、指定し た依存関係プロパティを指定した時間内で、指定した変化量で変化させ続ける仕組みになりま す。単純な Canvas.Left(レイアウト用の Canvas コントロールの左端の位置を指定する添付プ ロパティ)をアニメーションさせる XAML を以下に示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <Storyboard x:Key="MoveRectStoryboard"> <DoubleAnimation Storyboard.TargetName="Rect" Storyboard.TargetProperty="(Canvas.Left)" To="100" Duration="0:0:5" /> </Storyboard> </Page.Resources> <Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 71. 71 <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" Canvas.Top="10" Canvas.Left="10" /> </Canvas> </Page> アニメーションは、通常 Storyboard というものにまとめられます。Storyboard は、Resources で定義を行うのが一般的です。上記の例では、Storyboard 内に、Page に定義されている Rectangle コントロールの左の座標を 5 秒かけて 100 に変化させるというアニメーションが定 義されています。DoubleAnimation がその定義になります。DoubleAnimation に対して Storyboard の添付プロパティである TargetName でアニメーションのターゲットを指定して、 TargetProperty で何のプロパティをアニメーションさせるか指定します。 アニメーションは、定義しただけでは再生されないので、再生するコードを追加したいと思い ます。Page 読み込み時に呼び出される Loaded イベントハンドラを定義します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Loaded="Page_Loaded"> … </Page> コードビハインドで、Resources から Storyboard を取得して再生するコードを書きます。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation;
  • 72. 72 namespace XamlAdvApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private void Page_Loaded(object sender, RoutedEventArgs e) { // Storyboard を取得して再生する var storyboard = (Storyboard)this.Resources["MoveRectStoryboard"]; storyboard.Begin(); } } } Storyboard に対して Begin メソッドを呼び出すとアニメーションが開始されます。実行すると 以下のように時間の経過とともに矩形が右へ移動していきます。
  • 73. 73 アニメーションの値の指定方法で一番単純なのは From と To を使って指定する方法になりま す。この例では、From を省略(省略すると現在の値が開始の値となります)して To だけ指定 しています。そのため、結果として 10 から 100 まで 5 秒かけてアニメーションするようにな っています。この他にも To のかわりに By を指定する方法があります。By は、From から By の値だけ変化させるという意味になります。From が 10 で By が 20 の場合は 10 + 20 で 30 ま で移動するという形になります。XAML の例を以下に示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Loaded="Page_Loaded"> <Page.Resources> <Storyboard x:Key="MoveRectStoryboard"> <DoubleAnimation Storyboard.TargetName="Rect" Storyboard.TargetProperty="(Canvas.Left)" By="100" Duration="0:0:5" /> </Storyboard> </Page.Resources> <Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Rectangle x:Name="Rect"
  • 74. 74 Width="100" Height="100" Fill="Red" Canvas.Top="10" Canvas.Left="10" /> </Canvas> </Page> このコードだとアニメーション開始時点で Canvas.Left が 10 で、5 秒かけて 110 まで移動する という動きをします。 デフォルトでは、アニメーションは 1 度終了すると再度手動で実行するまで停止しています。 アニメーションに繰り返しを指定すると、指定した回数、指定した方法でアニメーションを繰 り返させることが出来ます。AutoReverse プロパティを指定すると、アニメーションが終了し たあと、逆方向のアニメーションを再生するか指定できます。サンプルで示した矩形が右に移 動するアニメーションに指定すると、右に移動が完了したあと元の位置に向かって左方向にア ニメーションするようになります。また、RepeatBehavior にアニメーションを繰り返す時間を 指定できます。ここで指定した時間だけ、アニメーションを再生し続けることが出来ます。 AutoReverse と組み合わせることで、行ったり来たりというアニメーションを実行させること も出来ます。色に対して指定すると点滅させるという効果も持たせることが出来ます。 TimeSpan 型なので Duration プロパティと同様に「時:分:秒」の書式で指定しますが、Forever を指定することで無限にアニメーションを再生し続けることが出来ます。 以下にアニメーショ ンの繰り返しの指定例を示します。 <Storyboard x:Key="MoveRectStoryboard"> <DoubleAnimation Storyboard.TargetName="Rect" Storyboard.TargetProperty="(Canvas.Left)" By="100" Duration="0:0:5" RepeatBehavior="0:0:13" AutoReverse="True"/> </Storyboard> この例では、アニメーションが 13 秒間繰り返しを続けます。
  • 75. 75 ここまでは、DoubleAnimation を例にアニメーションを説明してきました。UWP のアニメー ションは、DoubleAnimation 以外にも以下の Animation が定義されています。  PointAnimation 点のアニメーションを行います。  ColorAnimation 色のアニメーションを行います。 8.2.1. キ ー フレ ー ム を 使 った ア ニ メー シ ョ ン ここまでのアニメーションは、時間に応じて滑らかに値が変わっていくアニメーションでし た。UWP では、このほかにキーフレームを使った、指定した時間にパッと切り替わるアニメ ーションも提供しています。ObjectAnimationUsingKeyFrame を使って定義します。 ObjectAnimationUsingKeyFrame クラスの KeyFrames プロパティに対して指定した時間に、ど の値を指定するのかを設定します。DiscreteObjectKeyFrame に KeyTime で時間、Value で値 を設定してアニメーションを定義します。以下に 2 秒で 50 に、5 秒で 150 に、10 秒で 250 に パッパッと移動するアニメーションの定義を示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Loaded="Page_Loaded"> <Page.Resources> <Storyboard x:Key="MoveRectStoryboard"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Rect" Storyboard.TargetProperty="(Canvas.Left)" Duration="0:0:10"> <ObjectAnimationUsingKeyFrames.KeyFrames> <DiscreteObjectKeyFrame KeyTime="0:0:2"> <DiscreteObjectKeyFrame.Value>
  • 76. 76 50.0 </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> <DiscreteObjectKeyFrame KeyTime="0:0:5"> <DiscreteObjectKeyFrame.Value> 150.0 </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> <DiscreteObjectKeyFrame KeyTime="0:0:10"> <DiscreteObjectKeyFrame.Value> 250.0 </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames.KeyFrames> </ObjectAnimationUsingKeyFrames> </Storyboard> </Page.Resources> <Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" Canvas.Top="10" Canvas.Left="10" /> </Canvas> </Page> 8.2.2. ThemeAnimation テーマアニメーションを使うと、UWP のアプリっぽい標準のアニメーションを簡単に組み込 むことが出来ます。組み込みアニメーションには以下のようなものがあります。  DragItemThemeAnimation ドラッグされている項目要素に適用されているアニメーションをあらわします。
  • 77. 77  DragOverThemeAnimation ドラッグされている要素の下にある要素に適用されるアニメーションをあらわします。  DropTargetItemThemeAnimation ドロップ先となりうる要素に適用されるアニメーションです。  FadeInThemeAnimation フェードインするアニメーションをあらわします。  FadeOutThemeAnimation フェードアウトするアニメーションをあらわします。  PointerDownThemeAnimation タップまたはクリックするユーザー操作のアニメーションをあらわします。  PointerUpThemeAnimation 要素をタップして指を離すときのアニメーションをあらわします。  PopInThemeAnimation ポップインをあらわすアニメーションをあらわします。  PopOutThemeAnimation ポップインが閉じたり削除されたりするときに適用されるアニメーションをあらわしま す。  RepositionThemeAnimation 位置が変更されたときのアニメーションをあらわします。  SplitCloseThemeAnimation スプリットのアニメーションで非表示にするアニメーションをあらわします。  SplitOpenThemeAnimation スプリットのアニメーションで表示するアニメーションをあらわします。  SwipeBackThemeAnimation スワイプ後に要素がもとに戻るアニメーションをあらわします。
  • 78. 78  SwipeHintThemeAnimation スワイプが可能であることを示すアニメーションをあらわします。 様々なアニメーションが定義されています。このアニメーションを使うことで他の UWP のア プリのコントロールと同じような動きをさせることが出来ます。例えばフェードアウトさせる アニメーションは以下のように定義します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Loaded="Page_Loaded"> <Page.Resources> <Storyboard x:Key="FadeAnimation"> <FadeOutThemeAnimation TargetName="Rect" /> </Storyboard> </Page.Resources> <Canvas Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" Canvas.Top="10" Canvas.Left="10" /> </Canvas> </Page> コードビハインドは以下のようになります。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Animation;
  • 79. 79 namespace XamlAdvApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private void Page_Loaded(object sender, RoutedEventArgs e) { // Storyboard を取得して再生する var storyboard = (Storyboard)this.Resources["FadeAnimation"]; storyboard.Begin(); } } } これを実行すると矩形がフェードアウトして消える動きをします。 8.2.3. ThemaTransition ThemeTransisiton は、コントロールが表示されたりドラッグされたりしたときに適用されるア ニメーションになります。ThemeTransition を使うと、簡単に一般的な組み込みのアニメーシ ョンを自動で再生させたりすることが出来ます。以下のような ThemeTransition があります。  AddDeleteThemeTransition ItemsControl の子要素が追加・削除されたときのアニメーションです。  ContentThemeTransition AddDeleteThemeTransition に追加して適用します。コンテンツに変化があるとき(よく わからない)に適用されるアニメーションです。  EdgeUIThemeTransition 小さい UI の変化のアニメーションです。(よくわからない)
  • 80. 80  EntranceThemeTransition コントロールが最初に表示されるときのアニメーションです。  PaneThemeTransition パネルの切り替えのアニメーションです。(よくわからない)  PopupThemeTransition ポップアップが表示されるときのアニメーションです。  ReorderThemeTransition ListView の要素の並び替えのアニメーションです。  RepositionThemeTransition コントロールの位置が変わったときのアニメーションです。 これらの ThemeTransition を適用するには、コントロールの Transitions プロパティか、 ItemsControl の ItemsContainerTransitions プロパティに設定します。前者はコントロール単品 に、校舎はコントロールの子要素に対して適用されます。例えば、表示されたタイミングでフ ワッと表示されるようにするには以下のような XAML になります。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Hello world" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button.Transitions> <TransitionCollection> <EntranceThemeTransition /> </TransitionCollection>
  • 81. 81 </Button.Transitions> </Button> </Grid> </Page> ItemsContainer の子要素が初期表示のタイミングでフワッと出てくるようにするには以下のよ うになります。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ItemsControl> <ItemsControl.ItemContainerTransitions> <TransitionCollection> <EntranceThemeTransition /> </TransitionCollection> </ItemsControl.ItemContainerTransitions> <Button Content="Hello" /> <Button Content="Hello" /> <Button Content="Hello" /> <Button Content="Hello" /> <Button Content="Hello" /> </ItemsControl> </Grid> </Page> ThemeTransition の特徴は、アニメーションの開始と終了まで自動で組み込まれている点で す。特に理由がない場合は、ThemeTransition→ThemeAnimation→カスタムの Animation の順 番で採用を検討するとよいと思います。
  • 82. 82 8.3. Visual State Manager ここでは、Visual State Manager について説明します。Visual State Manager は、名前のとおり 見た目の状態を管理するための機能です。もっと簡単に言ってしまうとアニメーションをグル ーピングして管理するための機能になります。例えば、マウスがコントロールの上に乗った状 態では、こういうアニメーションをする、マウスがコントロールの上に無い場合は、こういう アニメーションをするといったように、状態とアニメーションを紐づけます。 Visual State Manager は、VisualStateManager クラスの VisualStateGroups 添付プロパティで定 義します。VisualStateGroups 添付プロパティには VisualStateGroup を x:Name で名前を付け て設定します。VisualStateGroup の中には VisualState を x:Name で名前を付けて設定します。 この VisualState の中にアニメーションで解説した Storyboard を設定して VisualState になった ときのアニメーションを設定します。同じ VisualStateGroup 内の VisualState は、同時に設定 できないという特徴があります。例えば、A というグループに Hoge と Foo という名前の VisualState があった場合、同時に Hoge と Foo のステートにすることは出来ません。同じタイ ミングでステートを設定したい場合はグループをわける必要があります。A というグループに Hoge、B というグループに Foo というステートがある場合は、Hoge と Foo ステートを同時に 設定することが出来ます。VisualState の切り替えは、VisualStateManager.GoToState メソッド を使います。VisualState を切り替えるコントロールと、切り替え先の VisualState 名と、トラ ンジション(後述します)の有無を指定します。 通常時は赤色で、マウスを上に持っていくと青色になる矩形を VisualStateManager で実現した 場合のコード例を以下に示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups>
  • 83. 83 <VisualStateGroup x:Name="CommonState"> <VisualState x:Name="Normal" /> <VisualState x:Name="Blue"> <Storyboard> <ColorAnimation Storyboard.TargetName="Rect" Storyboard.TargetProperty="(Rect.Fill).(SolidColorBrush.Color)" Duration="0:0:0" To="Blue" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" PointerEntered="Rect_PointerEntered" PointerExited="Rect_PointerExited"/> </Grid> </Page> コードビハインドは以下のようになります。GoToState で Visual State Manager を切り替えて いるだけです。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace XamlAdvApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent();
  • 84. 84 } private void Rect_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) { VisualStateManager.GoToState(this, "Blue", true); } private void Rect_PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) { VisualStateManager.GoToState(this, "Normal", true); } } } Visual State Manager は、Storyboard を使って VisualState の切り替え時に柔軟に表示を切り替 えることができます。しかし、単純な値の変更だけ(今回の赤から青の切り替えるといったよ うなもの)のためにアニメーションを定義するのはメンドクサイです。アニメーションが不要 で、単純に値を設定するだけの場合には、Setter という機能を使うことで簡単に記述すること が出来ます。Setter を指定するには、VisualState の Setters プロパティに Setter を指定しま す。Setter には Target プロパティで指定するオブジェクトとプロパティを表すパスを設定しま す。そして、Value に設定したい値を指定します。コード例を以下に示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonState"> <VisualState x:Name="Normal" />
  • 85. 85 <VisualState x:Name="Blue"> <VisualState.Setters> <Setter Target="Rect.Fill" Value="Blue" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" PointerEntered="Rect_PointerEntered" PointerExited="Rect_PointerExited"/> </Grid> </Page> 実行すると、マウスを Rectangle の上に持って行ったタイミングで赤から青色に変わります。 これまで、Visual State Manager での VisualState の切り替えはコードから GoToState を実行し て行ってきました。UWP の Visual State Manager では、StateTrigger を使うことで、XAML で VisualState の切り替えを行うことが出来ます。StateTrigger は、VisualState の StateTriggers プロパティに設定して使います。デフォルトでは AdaptiveTrigger という名前の Window サイズによって VisualState を切り替える機能が提供されています。AdaptiveTrigger の MinWindowWidth や MinWindowHeight を設定することで、その Window の幅や高さを超 えた時に、該当する VisualState に自動で切り替えてくれます。以下にコードを示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 86. 86 <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonState"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Target="Rect.Fill" Value="Red" /> </VisualState.Setters> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="1" /> </VisualState.StateTriggers> </VisualState> <VisualState x:Name="Blue"> <VisualState.Setters> <Setter Target="Rect.Fill" Value="Blue" /> </VisualState.Setters> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="750" /> </VisualState.StateTriggers> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" /> </Grid> </Page> 上記コードだと、Window 幅が 750 以上の場合に青色の矩形になり、それより小さい場合は赤 色の矩形になります。AdaptiveTrigger のような StateTrigger は自作することが出来ます。こ こでは作成方法は述べませんが、以下の GitHub 上のリポジトリに様々な便利な StateTrigger が定義されています。カスタムの StateTrigger を作成するときの参考や、NuGet からインスト ールも可能なので、使用してみると良いと思います。
  • 87. 87 WindowsStateTriggers https://github.com/dotMorten/WindowsStateTriggers 最後にトランジションについて説明をします。トランジションは、VisualState が切り替わると きに任意のアニメーションを挟むことが出来る機能になります。VisualStateGroup の Transitions に設定できます。以下のような XAML を記述することで、Normal という VisualState と Blue という VisualState の切り替え時に矩形が黒色になるというアニメーション が実行されるようになります。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonState"> <VisualStateGroup.Transitions> <VisualTransition> <Storyboard> <ColorAnimation Storyboard.TargetName="Rect" Storyboard.TargetProperty="(Rect.Fill).(SolidColorBrush.Color)" To="Black" /> </Storyboard> </VisualTransition> </VisualStateGroup.Transitions> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Target="Rect.Fill" Value="Red" /> </VisualState.Setters>
  • 88. 88 <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="1" /> </VisualState.StateTriggers> </VisualState> <VisualState x:Name="Blue"> <VisualState.Setters> <Setter Target="Rect.Fill" Value="Blue" /> </VisualState.Setters> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="750" /> </VisualState.StateTriggers> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="Rect" Width="100" Height="100" Fill="Red" /> </Grid> </Page> 実行すると以下のようになります。幅が 750 より小さい時には以下のように赤色の矩形になり ます。
  • 89. 89 そして、画面の幅を 750 以上にすると、以下のように 1 度矩形が黒くなるアニメーションが実 行されます。 その後青色の矩形になります。
  • 90. 90 8.4. Behavior UWP の標準機能ではありませんが、Visual Studio と同時にインストールされる Blend for Visual Studio でサポートされている Behavior というものがあります。Behavior は、UI に閉じ たロジックを部品化するためのテクノロジになります。OSS で開発されている点も特徴です。 以下の GitHub で管理されています。 XAML Behaviors https://github.com/Microsoft/XamlBehaviors 8.4.1. イ ン スト ー ル Behavior は、NuGet で配布されていて、簡単に導入することが出来ます。 Microsoft.Xaml.Behaviors.Uwp.Managed パッケージを NuGet パッケージマネージャーからイ ンストールします。
  • 91. 91 次に、XML 名前空間を定義します。以下のような Interactivity 名前空間と Core 名前空間を使 うのが一般的です。 xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" 8.4.2. 組 み 込み Behavior Behavior には以下のものが定義されています。  CallMethodAction 指定したメソッドを呼び出す Action  ChangePropertyAction 指定したプロパティの値を変更する Action  DataTriggerBehavior データが指定した条件に合致したときに Action を実行する Trigger  EventTriggerBehavior 指定したイベントが発行されたときに Action を実行する Trigger  GoToStateAction 指定した VisualState に遷移する Action  IncrementalUpdateBehavior ListView などの ItemTemplate でレンダリングの順番を制御するための Behavior  InvokeCommandAction 指定した Command を実行する Action  NavigateToPageAction 指定した Page へ画面遷移する Action  PlaySoundAction 指定した音楽を再生する Action
  • 92. 92 8.4.3. Behavior の 使 い 方 Behavior の使い方を説明します。Behavior は Behavior 単品で使用するものと、Trigger と Action を組み合わせて使うものの 2 種類があります。組み込みの Behavior では IncrementalUpdateBehavior が Behavior 単品で動作するもので、残りのものは TriggerBehavior という名前で終わっているものが Trigger で、Action という名前で終わってい るものが Action になります。TriggerBehavior は、条件を満たしたときに配下の Action を実行 するイメージです。 以下に Behavior の使用例を示します。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="PointerEntered"> <Core:ChangePropertyAction PropertyName="Background" Value="Blue" /> </Core:EventTriggerBehavior> <Core:EventTriggerBehavior EventName="PointerExited"> <Core:ChangePropertyAction PropertyName="Background" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}" /> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </Grid> </Page>
  • 93. 93 EventTriggerBehavior でイベントが発生したときに Action を実行するようにしています。そし て、ChangePropertyAction で背景色を指定しています。上記のコードでは、ポインターが画面 内に入ったときに青色の背景色を指定して、ポインターが画面外に出たときに ApplicationPageBackgroundThemeBrush で定義された色に変更しています。実行すると以下 のようになります。 実行直後は、通常の白色の画面が表示されます。 マウスを画面上に持ってくると青色に変化します。
  • 94. 94 このように、Behavior を使うことで簡単なプレゼンテーションロジックは XAML で定義する ことが出来るようになります。例えば、TextBox に入力された値が p@ssw0rd のときに Button が押せるようになる XAML は以下のようになります。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="TextBox"> <Interactivity:Interaction.Behaviors>
  • 95. 95 <Core:DataTriggerBehavior Binding="{Binding Text, ElementName=TextBox, UpdateSourceTrigger=PropertyChanged}" ComparisonCondition="Equal" Value="p@ssw0rd"> <Core:ChangePropertyAction TargetObject="{Binding ElementName=Button}" PropertyName="IsEnabled" Value="True" /> </Core:DataTriggerBehavior> <Core:DataTriggerBehavior Binding="{Binding Text, ElementName=TextBox, UpdateSourceTrigger=PropertyChanged}" ComparisonCondition="NotEqual" Value="p@ssw0rd"> <Core:ChangePropertyAction TargetObject="{Binding ElementName=Button}" PropertyName="IsEnabled" Value="False" /> </Core:DataTriggerBehavior> </Interactivity:Interaction.Behaviors> </TextBox> <Button x:Name="Button" Content="Click" /> </StackPanel> </Page> GoToStateAction と組み合わせて Visual State Manager を切り替えることで、もっと複雑なこ とも XAML だけで完結するようになります。 8.4.4. Behavior の 作 成 Behavior を使うことで、XAML で宣言的なロジックが定義できるようになります。Behavior を作成することで、さらにロジックの幅をひろげることができます。 Behavior を作成する場合は、DependencyObject を継承して IBehavior を実装したクラスを定 義することになります。例えば、ボタンに設定可能で、クリック時にメッセージボックスを出 す Behavior の実装例を以下に示します。 using System;
  • 96. 96 using Microsoft.Xaml.Interactivity; using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace XamlAdvApp { public class AlertBehavior : DependencyObject, IBehavior { // Behavior のプロパティは添付プロパティとして定義すると // Binding などが出来るので特に理由がない場合は添付プロパティがいい public static readonly DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof(string), typeof(AlertBehavior), new PropertyMetadata("Hello world")); public string Message { get { return (string)GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } // IBehavior で定義されているプロパティ public DependencyObject AssociatedObject { get; private set; } // IBehavior で定義されているメソッド // Behavior がコントロールに紐づけられたときに呼ばれる public void Attach(DependencyObject associatedObject) { this.AssociatedObject = associatedObject; var button = this.AssociatedObject as Button; if (button != null) {
  • 97. 97 button.Click += this.Button_Click; } } // IBehavior で定義されているメソッド // Behavior がコントロールから切り離されるときに呼ばれる public void Detach() { var button = this.AssociatedObject as Button; if (button != null) { button.Click -= this.Button_Click; } } private async void Button_Click(object sender, RoutedEventArgs e) { var dialog = new MessageDialog(this.Message); await dialog.ShowAsync(); } } } コメントにも書いていますが、Behavior を作成するにあたり実装しなければならないのは以下 の 1 つのプロパティと 2 つのメソッドになります。  AssociatedObject プロパティ Behavior が割り当てられたオブジェクトを取得します。通常は後述する Attach プロパテ ィの引数で渡されたオブジェクトを格納します。  Attach メソッド Behavior がオブジェクトに割り当てられたときに呼び出されます。初期化処理をここで行 います。
  • 98. 98  Detach メソッド Behavior のオブジェクトへの割り当てが解除されたときに呼び出されます。後始末処理を ここで行います。 この Behavior は、通常の Behavior と同じように以下のように XAML で定義して使えます。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Click"> <Interactivity:Interaction.Behaviors> <local:AlertBehavior Message="こんにちは世界" /> </Interactivity:Interaction.Behaviors> </Button> </StackPanel> </Page> 実行してボタンを選択すると以下のようにメッセージボックスが表示されます。
  • 99. 99 TriggerBehavior の作成方法は、以下のような Actions プロパティを持つ Behavior を定義する ことで作成可能です。 using Microsoft.Xaml.Interactivity; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Markup; namespace XamlAdvApp { // Actions をコンテンツプロパティとして設定する属性 [ContentProperty(Name = "Actions")] public class ClickTriggerBehavior : DependencyObject, IBehavior { // Actions という名前の添付プロパティを作成する public static readonly DependencyProperty ActionsProperty = DependencyProperty.Register( "Actions", typeof(ActionCollection), typeof(ClickTriggerBehavior), new PropertyMetadata(null));
  • 100. 100 public ActionCollection Actions { get { // 値がなければ作成する var result = (ActionCollection)GetValue(ActionsProperty); if (result == null) { result = new ActionCollection(); SetValue(ActionsProperty, result); } return result; } } public DependencyObject AssociatedObject { get; private set; } public void Attach(DependencyObject associatedObject) { this.AssociatedObject = associatedObject; var button = this.AssociatedObject as Button; if (button != null) { button.Click += this.Button_Click; } } public void Detach() { var button = this.AssociatedObject as Button; if (button != null) { button.Click -= this.Button_Click; } }
  • 101. 101 private void Button_Click(object sender, RoutedEventArgs e) { // Actions を実行する Interaction.ExecuteActions(this, this.Actions, e); } } } Action は、DependencyObject クラスを継承して IAction インターフェースを実装して作成し ます。 using Microsoft.Xaml.Interactivity; using Windows.UI.Popups; using Windows.UI.Xaml; namespace XamlAdvApp { public class AlertAction : DependencyObject, IAction { public static readonly DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof(string), typeof(AlertAction), new PropertyMetadata("Hello world")); public string Message { get { return (string)GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } public object Execute(object sender, object parameter) {
  • 102. 102 var dialog = new MessageDialog(this.Message); var ignore = dialog.ShowAsync(); return null; } } } この Trigger と Action は以下のように定義して使うことができます。 <Page x:Class="XamlAdvApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Click"> <Interactivity:Interaction.Behaviors> <local:ClickTriggerBehavior> <local:AlertAction Message="こんにちは世界" /> </local:ClickTriggerBehavior> </Interactivity:Interaction.Behaviors> </Button> </StackPanel> </Page> 実行してボタンを選択すると、以下のようにメッセージボックスが表示されます。
  • 103. 103 8.4.5. Behavior の 細 か い 使 い 方 Behavior の細かな使い方は、英語情報になりますが以下の GitHub のページから参照できま す。一通り目を通しておくと XAML でできることの幅がひろがるのでお勧めです。 https://github.com/Microsoft/XamlBehaviors/wiki 上記サイトの右側のメニューの Behaviors Reference の箇所がリファレンスになります。 8.5. DataTemplate UWP では、テンプレートという機能を使ってデータを柔軟に可視化することが出来ます。こ こでは、そのための DataTemplate について説明します。DataTemplate は、名前の通りデータ を表示するためのテンプレートになります。ContentControl と、それを継承した Button など のコントロールや、ItemsControl と、それを継承した ListView や GridView や ListBox など 様々なコントロールで使用できます。 8.5.1. ContentControl 系 で の 使 用 最初に単一要素を表示するための ContentControl 系のコントロールでの使用方法について説明 します。ContentControl は、Content プロパティに任意のオブジェクトが設定可能です。コン
  • 104. 104 トロール以外を設定した場合は、ここで説明する DataTemplate を使って、表示方法が決定さ れます。(DataTemplate が存在しない場合は ToString の結果が表示されます) DataTemplate は、ContentControl 系のコントロールの場合は ContentTemplate プロパティに 設定します。例として、以下のような Person クラスがあるとします。 namespace XamlAdvApp { public class Person { public string Name { get; set; } public int Age { get; set; } } } これを Content プロパティに設定した ContentControl を XAML で定義すると以下のようにな ります。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ContentControl> <local:Person Name="Tanaka" Age="35" /> </ContentControl> </Grid> </Page> このまま実行しても Person クラスの ToString の結果のクラス名が表示されます。ここに DataTemplate を適用してみます。
  • 105. 105 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ContentControl> <local:Person Name="Tanaka" Age="35" /> <ContentControl.ContentTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Age}" /> </StackPanel> </DataTemplate> </ContentControl.ContentTemplate> </ContentControl> </Grid> </Page> DataTemplate では、DataContext に ContentControl の Content に設定されたオブジェクトが 設定されているので、実行時データバインディングでプロパティの値をコントロールと紐づけ ることが出来ます。上記のコードを実行すると以下のような結果になります。
  • 106. 106 コンパイル時データバインディングを使う場合は x:DataType 属性で、コンパイル時データバ インディングで使用される型を指定する必要があります。コードを以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ContentControl> <local:Person Name="Tanaka" Age="35" /> <ContentControl.ContentTemplate> <DataTemplate x:DataType="local:Person"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock Text="{x:Bind Age}" /> </StackPanel> </DataTemplate> </ContentControl.ContentTemplate> </ContentControl> </Grid> </Page> 実行結果は、実行時データバインディングと同じため省略します。 DataTemplate を再利用する場合は以下のように App クラスや Page クラスのリソースに DataTemplate を定義して StaticResource マークアップ拡張で参照することができます。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 107. 107 x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="PersonTemplate" x:DataType="local:Person"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock Text="{x:Bind Age}" /> </StackPanel> </DataTemplate> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ContentControl ContentTemplate="{StaticResource PersonTemplate}"> <local:Person Name="Tanaka" Age="35" /> </ContentControl> </Grid> </Page> DataTemplate には、状況に応じて選択する DataTemplate をプログラムで決定するための DataTemplateSelector という機能があります。DataTemplateSelector クラスを継承して、 SelectTemplateCore メソッドをオーバーライドして DataTemplate の選択ロジックを記述しま す。SelectTemplateCore メソッドは object item を受け取るものと object item, DependencyObject container の 2 つの引数を受け取るものがあります。これらは、 ItemsControl.ItemsPanel が ItemsStackPanel または ItemsWrapGrid の場合、前者のメソッ ドを使用します。ItemsPanel が VirtualizingStackPanel や WrapGrid などの別のパネルであ る場合は、後者のメソッドを使用します。どちらのケースも対応できるように両方実装してお くのが良いでしょう。例えば、Person クラスの Age プロパティの値によって選択する DataTemplate を切り替える DataTemplateSelector の実装は以下のようになります。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace XamlAdvApp
  • 108. 108 { public class PersonDataTemplateSelector : DataTemplateSelector { public DataTemplate LessThan50Template { get; set; } public DataTemplate Greater50Template { get; set; } protected override DataTemplate SelectTemplateCore(object item) { if (item == null) { return null; } var person = (Person)item; if (person.Age <= 50) { return this.LessThan50Template; } else { return this.Greater50Template; } } protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { return this.SelectTemplateCore(item); } } } 上記 DataTemplateSelector を使うには、ContentControl の ContentTemplateSelector プロパテ ィに設定します。以下のような XAML になります。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp"
  • 109. 109 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Page.Resources> <local:PersonDataTemplateSelector x:Key="PersonDataTemplateSelector"> <local:PersonDataTemplateSelector.LessThan50Template> <DataTemplate x:DataType="local:Person"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock Text="50 歳以下" /> </StackPanel> </DataTemplate> </local:PersonDataTemplateSelector.LessThan50Template> <local:PersonDataTemplateSelector.Greater50Template> <DataTemplate x:DataType="local:Person"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock Text="50 歳より上" /> </StackPanel> </DataTemplate> </local:PersonDataTemplateSelector.Greater50Template> </local:PersonDataTemplateSelector> </Page.Resources> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ContentControl ContentTemplateSelector="{StaticResource PersonDataTemplateSelector}"> <local:Person Name="Tanaka" Age="35" /> </ContentControl> <ContentControl ContentTemplateSelector="{StaticResource PersonDataTemplateSelector}"> <local:Person Name="Kimura" Age="55" /> </ContentControl>
  • 110. 110 </StackPanel> </Page> 実行すると、Age のプロパティの値に応じて以下のように表示が切り替わります。 8.5.2. ItemsControl 系 で の 使 用 ItemsControl では、ItemTemplate が ContentControl の ContentTemplate に概要し、 ItemTemplateSelector が ContentControl の ContentTemplateSelector に該当します。 ItemsControl の ItemsSource プロパティに設定された要素に対して、ItemTemplate が適用さ れて表示されます。 例えば、以下のような XAML があるとします。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ItemsControl x:Name="ItemsControlPeople"> <ItemsControl.ItemTemplate> <DataTemplate x:DataType="local:Person"> <Border BorderThickness="1" BorderBrush="Red"> <StackPanel> <TextBlock Text="{x:Bind Name}" />
  • 111. 111 <TextBlock Text="{x:Bind Age}" /> </StackPanel> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Page> この XAML のコードビハインドで以下のように ItemsSource に値を設定します。 using System.Linq; using Windows.UI.Xaml.Controls; namespace XamlAdvApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.ItemsControlPeople.ItemsSource = Enumerable.Range(1, 5) .Select(x => new Person { Name = $"Tanaka {x}", Age = 48 + x, }); } } } 実行すると、以下のように ItemsControl に ItemTemplate が適用されて Border(枠をあらわす コントロール)で囲まれた要素が 5 つ表示されます。
  • 112. 112 これを、DataTemplateSelector を使うようにするには ItemsControl の ItemTemplateSelector を設定します。XAML の例を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ItemsControl x:Name="ItemsControlPeople"> <ItemsControl.ItemTemplateSelector> <local:PersonDataTemplateSelector> <local:PersonDataTemplateSelector.LessThan50Template> <DataTemplate x:DataType="local:Person"> <Border BorderThickness="1"
  • 113. 113 BorderBrush="Gray"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock Text="{x:Bind Age}" /> </StackPanel> </Border> </DataTemplate> </local:PersonDataTemplateSelector.LessThan50Template> <local:PersonDataTemplateSelector.Greater50Template> <DataTemplate x:DataType="local:Person"> <Border BorderThickness="1" BorderBrush="Blue"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock Text="{x:Bind Age}" /> </StackPanel> </Border> </DataTemplate> </local:PersonDataTemplateSelector.Greater50Template> </local:PersonDataTemplateSelector> </ItemsControl.ItemTemplateSelector> </ItemsControl> </Grid> </Page> 実行すると、以下のようにデータに応じて枠の色が変わります。
  • 114. 114 8.6. ControlTemplate UWP のコントロールは、ControlTemplate という機能を使うことでコントロールの振る舞い はそのままに、見た目を 100%カスタマイズすることが出来ます。コントロールの Template プロパティにコントロールの見た目が定義されていて、その値を差し替えることでコントロー ルの見た目を任意のものに変えることが出来ます。 8.6.1. ク リ ック 可 能 な テ キス ト の 作成 例として、クリックが可能なテキストを考えてみます。UWP でテキストを表すには TextBlock というコントロールを使用しますが、TextBlock には Click イベントがありません。近い動作を する PointerPressed などのイベントを使ってエミュレートする必要があります。Button の Click と同じ動作をさせようとすると多少のコーディングが必要になります。Button のように クリック可能な TextBlock を作りたい場合は、Button の見た目を TextBlock に差し替えるとい う考えが UWP では簡単です。コード例を以下にしめします。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  • 115. 115 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Hello world"> <Button.Template> <!-- Button の見た目を差し替える --> <ControlTemplate TargetType="Button"> <!-- 見た目は TextBlock で Text に Content の内容をバインドする --> <TextBlock Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> </ControlTemplate> </Button.Template> </Button> </Grid> </Page> 実行すると以下のような見た目になります。見た目上は TextBlock そのものですが、Click イベ ントなど Button の動作がそのまま残っています。
  • 116. 116 8.6.2. コ ン トロ ー ル の Visual State コントロールには、状態に応じて様々な Visual State が定義されています。例えば Button では マウスオーバーの状態やマウスが乗っていない状態などです。この状態を確認するには、デフ ォルトのスタイルをコピーする Visual Studio の機能を使うか、generic.xaml の定義を確認する 方法があります。 Visual Studio の機能を使うには、テンプレートを確認したいコントロールをデザイナ上で右ク リックして「テンプレートの編集」→「コピーして編集」を選択します。
  • 117. 117 そうすると、展開された定義を Page や App.xaml の何処に展開するのかという選択肢が出てき ます。そして展開先を選択するとコントロールのテンプレートが Style として展開されます。 例えば、Button にはマウスカーソルなどが上におかれたときの PointerOver という名前の VisualState が定義されています。そして、通常時をあらわす Normal という名前の VisualState が CommonStates という VisualStateGroup に定義されています。この VisualState を定義する ことで、マウスなどのカーソルがコントロール上に来たときの見た目を変えることが出来ま す。以下に PointerOver のときに文字の色が赤色になるように定義した XAML を示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  • 118. 118 xmlns:local="using:XamlAdvApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="XamlAdvApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Hello world"> <Button.Template> <!-- Button の見た目を差し替える --> <ControlTemplate TargetType="Button"> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <VisualState.Setters> <Setter Target="TextBlock.Foreground" Value="Red" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <!-- 見た目は TextBlock で Text に Content の内容をバインドする - -> <TextBlock x:Name="TextBlock" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> </Grid> </ControlTemplate> </Button.Template> </Button> </Grid> </Page> 実行すると以下のようになります。
  • 120. 120 9. 代表的なコントロール ここでは、UWP で使用できる代表的なコントロールについて紹介します。 9.1. レ イ アウ ト パ ネ ル UWP では Panel を継承したコントロールを使って要素の並びを制御します。ここでは、代表 的な Panel を継承したコントロールについて紹介します。 9.1.1. StackPanel StackPanel は、要素を縦や横に並べていくシンプルなコントロールです。デフォルト状態で は、縦に並べていきます。以下の XAML を示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  • 121. 121 xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel> <Button Content="Hello" /> <Button Content="Hello" /> <Button Content="Hello" /> <Button Content="Hello" /> <Button Content="Hello" /> </StackPanel> </Grid> </Page> 以下のように縦並びに表示されます。
  • 122. 122 StackPanel の Orientation プロパティに「Horizontal」を設定することで(デフォルトは Vertical)横並びになります。 <StackPanel Orientation="Horizontal"> 9.1.2. Grid Grid は、画面を格子状に区切って、そこにコントロールを配置していきます。格子は、Grid の RowDefinitions で行方向の区切りを指定して、ColumnDefinitions で列方向の区切りを指定 します。RowDefinitions の中では RowDefinition を使って行を定義していきます。 ColumnDefinitions の中では ColumnDefinition を使って列を定義していきます。定義された行 と列には Grid.Row 添付プロパティで行の位置を、Grid.Column で列の位置を、Grid.RowSpan で何行占有するか、Grid.ColumnSpan で何列占有するかを指定します。格子内での配置につい ては水平方向の配置をあらわす HorizontalAlignment で Center(中央寄せ)、Left(左寄 せ)、Right(右寄せ)、Stretch(全体占有)と、垂直方向の配置をあらわす
  • 123. 123 VerticalAlignment で Center(中央寄せ)、Bottom(下寄せ)、Top(上寄せ)、Stretch(全 体占有)を指定できます。 例として、2×2 の格子状に区切って、そこにボタンを配置してみます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button Content="0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> <Button Content="0,1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Column="1" /> <Button Content="1,0" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" /> <Button Content="1,1" Grid.Row="1"
  • 124. 124 Grid.Column="1"/> </Grid> </Page> 実行すると以下のように 2×2 の格子の中にボタンが配置されます。 デフォルトでは、RowDefinition や ColumnDefinition で定義した格子の幅は等間隔になりま す。幅の指定方法には*と数字と Auto という 3 つがあります。  *による指定(デフォルト) 余白を比率によって分割します。3*と 2*が指定された RowDefinition がそれぞれある場合 は、3:2 の比率で配置されます。  数字による指定 ピクセル単位での幅の指定になります。  Auto による指定 中に配置されたコントロールの幅によってサイズが決まります。
  • 125. 125 RowDefinition が Height プロパティで指定して ColumnDefinition が Width プロパティで、そ れぞれ幅が指定できます。例として、画面左側にリストがあり、トップにアプリタイトルがあ る画面の XAML を以下にしめします。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Text="Sample application" Style="{StaticResource TitleTextBlockStyle}" Grid.ColumnSpan="2" /> <ListView Grid.Row="1" Width="250"> <ListViewItem Content="Item1" /> </ListView> <Border Grid.Row="1" Grid.Column="1" Background="Azure"> </Border> </Grid>
  • 126. 126 </Page> 実行すると、以下のようになります。 Grid は、使いこなすと柔軟なレイアウトが組める非常に強力なコントロールです。是非使い方 をマスターしてください。 9.1.3. Canvas Canvas は、Left、Top と ZIndex の絶対座標で位置を指定します。Left で左端からのピクセル 数、Top で上からのピクセル数、ZIndex は重なりあったときにどちらが上にくるかを指定しま す。XAML の例を以下にしめします。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
  • 127. 127 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Canvas> <Rectangle Fill="Red" Width="100" Height="100" Canvas.Top="100" Canvas.Left="100" Canvas.ZIndex="1" /> <Button Content="Hello" Canvas.Top="10" Width="150" Height="150" Canvas.Left="10" /> </Canvas> </Grid> </Page> 実行すると以下のようになります。Rectangle に ZIndex=”1”を指定しているので、Button より も Rectangle が上に来ている点が特徴です。
  • 128. 128 Canvas はシンプルなぶん高速に動作する点が特徴です。 9.1.4. RelativePanel RelativePanel は、UWP で追加された新しいレイアウトパネルになります。XAML のネストを 深くしなくても柔軟にレイアウトを指定可能という点が特徴です。RelativePanel は、名前の通 り別のコントロールからの相対位置でコントロールを配置します。相対的な位置の指定は以下 のプロパティで行います。  RelatviePanel.Above=”対象のコントロール名” 対象のコントロール名で指定したコントロールの上に配置されます。  RelativePanel.Below=”対象のコントロール名” 対象のコントロール名で指定したコントロールの下に配置されます。  RelativePanel.RightOf=”対象のコントロール名” 対象のコントロール名で指定したコントロールの右に配置されます。
  • 129. 129  RelativePanel.LeftOf=”対象のコントロール名” 対象のコントロール名で指定したコントロールの左に配置されます。  RelativePanel.AlignBottomWith=”対象のコントロール名” 対象のコントロール名で指定したコントロールと下の位置を合わせます。  RelativePanel.AlignHorizontalCenterWith=”対象のコントロール名” 対象のコントロール名で指定したコントロールと水平方向の中央の位置を合わせます。  RelativePanel.AlignLeftWith=”対象のコントロール名” 対象のコントロール名で指定したコントロールと左の位置を合わせます。  RelativePanel.AlignRightWith=”対象のコントロール名” 対象のコントロール名で指定したコントロールと右の位置を合わせます。  RelativePanel.AlignTopWith=”対象のコントロール名” 対象のコントロール名で指定したコントロールと上の位置を合わせます。  RelativePanel.AlignVerticalCenterWith=”対象のコントロール名” 対象のコントロール名で指定したコントロールと垂直方向の中央の位置を合わせます。 このほかに、親になる Panel の位置に対して相対的に設定するプロパティがあります。これら のプロパティは True を設定することで有効になります。  RelativePanel.AlignBottomWithPanel パネルの下端にコントロールの下を合わせるか指定します。  RelativePanel.AlignHorizontalCenterWithPanel パネルの水平方向の中心にコントロールの中心を合わせるか指定します。  RelativePanel.AlignLeftWithPanel パネルの左にコントロールの左を合わせるか指定します。  RelativePanel.AlignRightWithPanel パネルの右にコントロールの右を合わせるか指定します。
  • 130. 130  RelativePanel.AlignTopWithPanel パネルの上にコントロールの上を合わせるか指定します。  RelativePanel.AlignVerticalCenterWithPanel パネルの垂直方向の中央にコントロールの中心を合わせるか指定します。 RelativePanel のプロパティをいくつかつかってコントロールを配置した XAML を以下に示し ます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button x:Name="Origin" Content="Origin" RelativePanel.AlignHorizontalCenterWithPanel="True" RelativePanel.AlignVerticalCenterWithPanel="True"/> <Button Content="None" /> <Button Content="LeftOf" RelativePanel.LeftOf="Origin" /> <Button Content="RightOf" RelativePanel.RightOf="Origin" /> <Button Content="Above" RelativePanel.Above="Origin" /> <Button Content="Below" RelativePanel.Below="Origin" /> <Button Content="Bottom/RightWithPanel" RelativePanel.AlignBottomWith="Origin" RelativePanel.AlignRightWithPanel="True"/> <Button Content="Center/BottomWithPanel"
  • 131. 131 RelativePanel.AlignHorizontalCenterWith="Origin" RelativePanel.AlignBottomWithPanel="True"/> </RelativePanel> </Page> 実行すると以下のように表示されます。Origin を中心にして、相対的にコントロールが配置さ れていることが確認できます。 9.2. Border Border は、枠を提供するコントロールです。BorderThickness で枠の太さを指定して、 BorderBrush で枠線の太さを指定できます。また、Padding プロパティで内側の余白を指定し たり、Background プロパティで背景色の塗りつぶしを指定できます。Border コントロールを 使うと簡単にコントロールをグルーピングできます。Border コントロールの使用例を以下に示 します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  • 133. 133 Border の BorderThickness は Thickness 型で「左&右,上&下」や「左,上,右,下」の順番でそれ ぞれの枠の太さを指定できます。以下のような XAML になります。 <Border BorderThickness="5,1,10,3" BorderBrush="Blue" Padding="10" Margin="10" Background="Azure"> <Rectangle Fill="Red" /> </Border> 実行すると以下のように枠線の太さが変わります。 9.3. TextBlock シンプルなテキストを表示するためのコントロールです。Text プロパティに設定した文字列 か、Inlines プロパティに設定されたインラインテキスト要素を表示します。まず、簡単な Text プロパティの設定例を示します。
  • 134. 134 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock Text="Hello world" /> </Grid> </Page> 実行すると Hello world という文字列が表示されます。 Inlines プロパティを使うと Run オブジェクトや LineBreak オブジェクトを使って複数行の書 式付きテキストを表示できます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock> <Run Text="Hello" /> <Underline>world</Underline> <LineBreak />
  • 135. 135 <Bold>XAML</Bold> <Italic>programming</Italic> <Hyperlink NavigateUri="http://msdn.microsoft.com/">world</Hyperlink> </TextBlock> </Grid> </Page> 実行すると以下のような表示になります。 9.4. Button ボタンは、ユーザーがマウスやタッチで選択してコマンドを実行するための UI を提供しま す。Button は Click イベントがあり、そのイベントを購読することで選択時の処理を記述でき ます。Button は ContentControl を継承しているため、ContentControl で使用した ControlTemplate などの機能を使うことが出来ます。単純な Button を置いて Click に応答する コードは以下のようになります。 <Page x:Class="ControlApp.MainPage"
  • 136. 136 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Alert" Click="Button_Click" /> </Grid> </Page> Click イベント発生時にアラートダイアログを出すには以下のようなコードビハインドを記述し ます。 using System; using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { var dialog = new MessageDialog("Hello world"); await dialog.ShowAsync(); }
  • 137. 137 } } Click イベントのほかに、Command プロパティという ICommand インターフェースを実装し たクラスを受け取るプロパティがあります。このプロパティに ICommand をセットすると、 ICommand の CanExecute が false を返す間は自動で押せない状態になり、CanExecute が true を返すときには押せる状態になるということが出来ます。Button が押せる状態になったときに 選択されると ICommand の Execute メソッドが呼び出されます。以下のような ICommand を 実装したクラスを定義しておいて public class AlertCommand : ICommand { public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return true; } public async void Execute(object parameter) { var dialog = new MessageDialog("Hello world"); await dialog.ShowAsync(); } } Button の Command プロパティに設定することで Button を選択したときにダイアログが表示 されます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 138. 138 mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button Content="Alert"> <Button.Command> <local:AlertCommand /> </Button.Command> </Button> </Grid> </Page> 9.5. TextBox TextBox は、文字列を入力するための UI を提供します。TextChanged イベントで入力テキス トの変更を購読することができます。TextBox の Text プロパティを通して入力された文字列を 取得したり設定することができます。 Text プロパティとデータバインディングを行うときは Mode を TwoWay にすることで TextBox からの入力をソースに反映できます。UpdateSourceTrigger を PropertyChanged に設 定することで、リアルタイムに変更をプロパティに反映できます。コンパイル時データバイン ディングには UpdateSourceTrigger が無いため、フォーカスが外れたタイミングのみでの値の 同期が基本になります。 以下のような XAML で、コードビハインドのテキストを通じて TextBox の入力と TextBlock の Text を同期させることができます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 139. 139 <TextBox Text="{x:Bind InputText, Mode=TwoWay}"/> <TextBlock Text="{x:Bind InputText, Mode=OneWay}" Style="{StaticResource BodyTextBlockStyle}"/> </StackPanel> </Page> コードビハインドは以下のようになります。 using System.ComponentModel; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page, INotifyPropertyChanged { public MainPage() { this.InitializeComponent(); } public event PropertyChangedEventHandler PropertyChanged; private string inputText; public string InputText { get { return this.inputText; } set { this.inputText = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InputText))); } } } }
  • 140. 140 実行すると以下のように TextBox のフォーカスが外れたタイミングでテキストが TextBlock に 同期されます。 9.6. CheckBox CheckBox は、ユーザーに 2 択の選択肢を提供することが出来ます。IsChecked プロパティで true か false でチェックがされているかいないかを取得できます。厳密にいうと IsChecked プ ロパティは bool?型なので、true でも false でもない、不確定という null の選択肢もあります。 Checked イベントでチェックされたときのイベントを購読できます。Uncheked イベントでチ ェックが外されたときのイベントを購読することが出来ます。 以下のように XAML で定義します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp"
  • 141. 141 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <CheckBox Content="Check me" IsChecked="True" /> </Grid> </Page> 以下のような見た目になります。 9.7. RadioButton RadioButton は、複数の選択肢の中から 1 つを選ばせる UI を提供します。IsChecked プロパテ ィでチェック状態を取得できる点は CheckBox と同じですが、同じ Panel 内に置かれたコント ロールは、同時に 1 つしかチェックできないという特徴があります。以下に XAML を示しま す。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Border BorderThickness="1" BorderBrush="Black" Padding="10"> <StackPanel> <RadioButton Content="100 人以下" />
  • 142. 142 <RadioButton Content="500 人以下" /> <RadioButton Content="それ以上" /> </StackPanel> </Border> <Border BorderThickness="1" BorderBrush="Black" Padding="10"> <StackPanel> <RadioButton Content="青" /> <RadioButton Content="黄" /> <RadioButton Content="赤" /> </StackPanel> </Border> </StackPanel> </Page> 以下のような見た目になります。100 人以下、500 人以下、それ以上の中で 1 つ、青、黄、赤 の中で 1 つのみがチェック可能です。
  • 143. 143 同じ Panel 内にある CheckBox をグルーピングしたい場合は GroupName プロパティで明示的 に名前を付けることで同じ Panel 内や Panel 外の RadioButton をグルーピングできます。例え ば、先ほどの XAML の RadioButton 全てに同じ GroupName を指定すると、6 個の RadioButton の内、同時に選択できるのが 1 つに制限できます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Border BorderThickness="1" BorderBrush="Black" Padding="10">
  • 144. 144 <StackPanel> <RadioButton Content="100 人以下" GroupName="AGroup" /> <RadioButton Content="500 人以下" GroupName="AGroup"/> <RadioButton Content="それ以上" GroupName="AGroup"/> </StackPanel> </Border> <Border BorderThickness="1" BorderBrush="Black" Padding="10"> <StackPanel> <RadioButton Content="青" GroupName="AGroup" /> <RadioButton Content="黄" GroupName="AGroup" /> <RadioButton Content="赤" GroupName="AGroup" /> </StackPanel> </Border> </StackPanel> </Page> 実行結果を以下に示します。
  • 145. 145 9.8. RepeatButton RepeatButton は、押しっぱなしになったときに Click イベントを連続で発行する機能を持った Button です。NumericUpDown のように数字を入力するようなコントロールで使われることが 多いコントロールになります。例えば、ボタンを押し続けるとカウントが上がっていくコード は以下のようになります。 XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
  • 146. 146 <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <RepeatButton Content="OK" Click="{x:Bind Click}" /> <TextBlock Text="{x:Bind Count, Mode=OneWay}" Style="{StaticResource BodyTextBlockStyle}"/> </StackPanel> </Page> コードビハインドは以下のようになります。 using System.ComponentModel; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page, INotifyPropertyChanged { public MainPage() { this.InitializeComponent(); } public event PropertyChangedEventHandler PropertyChanged; private int count; public int Count { get { return this.count; } set { this.count = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); } }
  • 147. 147 public void Click() { this.Count++; } } } 実行してボタンを押しっぱなしにするとカウントがインクリメントされていくことが確認でき ます。 9.9. ToggleSwitch ToggleSwitch は CheckBox と同じようにユーザーに ON/OFF の選択を提供するコントロール です。Header プロパティで見出しを設定して、OnContent プロパティと OffContent プロパテ ィで ON のときと OFF の時に表示されるコンテンツを指定できます。IsOn プロパティで
  • 148. 148 ON/OFF の状態を bool 型で取得できます。true のときが ON で false のときが OFF になりま す。 XAML の例を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ToggleSwitch Header="Option1" OnContent="ON" OffContent="OFF" IsOn="True" /> <ToggleSwitch Header="Option1" OnContent="ON" OffContent="OFF" IsOn="False" /> </StackPanel> </Page> 実行すると以下のようになります。
  • 149. 149 9.10. ToggleButton ToggleButton は、クリックをすると ON/OFF を切り替えることが出来るボタンです。Button と同様に Content プロパティで表示コンテンツを指定して、IsChecked プロパティで選択状態 を取得または設定できあmす。IsChecked プロパティは、bool?型なので true(選択)、false(未 選択)、null(どちらでもない)状態を表すことが出来ます。XAML での使用例を以下に示し ます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ToggleButton Content="Button1" />
  • 150. 150 <ToggleButton Content="Button2" IsChecked="True" /> </StackPanel> </Page> ToggleButton を 2 つ表示して、上が OFF の状態で下が ON の状態です。実行すると以下のよ うに表示されます。 9.11. ComboBox ComboBox は、ユーザーに対して複数の選択肢から 1 つの要素を選択する機能を提供するコン トロールです。継承関係をたどっていくと ItemsControl を継承しているので、ItemsSource を 使ってコレクションを設定して、ItemTemplate で見た目を定義できます。選択された要素は、 SelectedItem で ItemsSource に設定された要素の中で選択されたオブジェクトのインスタンス が取得できます。SelectedIndex で選択された要素のコレクション内でのインデックスが取得で きます。使用例を以下に示します。
  • 151. 151 XAML は以下のようになります。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ComboBox x:Name="ComboBox" Header="Header" ItemsSource="{x:Bind People}"> <ComboBox.ItemTemplate> <DataTemplate x:DataType="local:Person"> <StackPanel> <TextBlock Text="{x:Bind Name}" /> <TextBlock> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button Content="Alert" Click="{x:Bind Click}" /> </StackPanel> </Page> コードビハインドは以下のようになります。 using System; using System.Linq; using Windows.UI.Popups;
  • 152. 152 using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { // ComboBox にバインドするデータ public Person[] People { get; } public MainPage() { this.InitializeComponent(); // 適当なデータを作っておく this.People = Enumerable.Range(1, 10) .Select(x => new Person { Name = $"tanaka {x}", Age = 30 + x, }) .ToArray(); } public async void Click() { // 選択されてないときは何もしない if (this.ComboBox.SelectedIndex == -1) { return; } // 選択されてるときは、その情報を出す var selectedPerson = (Person)this.ComboBox.SelectedItem; var dialog = new MessageDialog( $"{this.ComboBox.SelectedIndex}: {selectedPerson.Name}, {selectedPerson.Age}歳 "); await dialog.ShowAsync(); } }
  • 153. 153 public class Person { public string Name { get; set; } public int Age { get; set; } } } 実行すると以下のようになります。要素を選択して、ボタンを選択するとダイアログが表示さ れます。 9.12. CalendarDatePicker CalendarDatePicker は、ドロップダウンでカレンダーを使ってユーザーに日付を選択する機能 を提供します。Date プロパティ(DateTimeOffset?型)で選択日を取得または設定でき、 DateFormat プロパティで日付のフォーマットを指定できます。PlaceholderText プロパティ で、日付を選択してないときに表示するテキストを設定できます。使用例を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  • 154. 154 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <CalendarDatePicker x:Name="CalendarDatePicker" PlaceholderText="日付を選んでね" DateFormat="{}{year.full}-{month.integer}-{day.integer}" /> <Button Content="Alert" Click="{x:Bind Click}" /> </StackPanel> </Page> コードビハインドは以下のようになります。 using System; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } public async void Click() { var dialog = new MessageDialog( this.CalendarDatePicker.Date?.ToString("yyyy-MM-dd") ?? "未選択"); await dialog.ShowAsync(); } }
  • 156. 156 日付を選択してボタンを選択すると、選択した日付が以下のようにアラートで表示されます。 9.13. CalendarView CalendarView は、カレンダーを使って日付を選択する機能をユーザーに提供します。 SelectedDates プロパティで選択された日付を取得できます。SelectedDates プロパティは IList<DateTimeOffset>型で複数選択に対応しています。SelectionMode プロパティに Single を設定した場合は単一の日付の選択ですが、Multiple を設定した場合は複数の日付を選択可能 になります。 以下に使用例を示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 157. 157 <CalendarView x:Name="CalendarView" SelectionMode="Multiple"/> <Button Content="Alert" Click="{x:Bind Click}" /> </StackPanel> </Page> コードビハインドは以下のようになります。 using System; using System.Linq; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } public async void Click() { var lines = string.Join(Environment.NewLine, this.CalendarView .SelectedDates .Select(x => x.ToString("yyyy-MM-dd")) .ToArray()); var dialog = new MessageDialog(lines); await dialog.ShowAsync(); } }
  • 158. 158 } 実行すると以下のようになります。 複数選択した日付が表示されていることが確認できます。 9.14. DatePicker DatePicker は、ユーザーに対して日付をドロップダウンのような形式で選択する機能を提供し ます。Date プロパティで DateTimeOffset 型として選択している値を取得または設定できま す。また、MaxYear プロパティと MinYear プロパティを DateTimeOffset 型で指定すること で、指定可能な年数の範囲を設定できます。
  • 159. 159 使用例の XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <DatePicker x:Name="DatePicker" /> <TextBlock Text="{x:Bind DatePicker.Date, Mode=OneWay}" /> </StackPanel> </Page> DatePicker で選択した日付を TextBlock にバインドしています。実行すると以下のようになり ます。
  • 160. 160 日付の選択は、DatePicker を選択すると以下のようにドロップダウンが表示されて年と月と日 を、それぞれ選択可能になります。 9.15. TimePicker TimePicker は、ドロップダウンの形式で時間を選択するための機能をユーザーに提供します。 TimeSpan 型の Time プロパティで選択された日付を取得したり設定できます。ClockIdentifier プロパティに 12HourClock を指定すると午前・午後を指定する形のインターフェースになりま す。デフォルトは 24 時間で時間を選択する 24HourClock が設定されています。 使用例の XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  • 161. 161 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TimePicker x:Name="_24HourTimePicker" /> <TextBlock Text="{x:Bind _24HourTimePicker.Time, Mode=OneWay}" /> <TimePicker x:Name="_12HourTimePicker" ClockIdentifier="12HourClock" /> <TextBlock Text="{x:Bind _12HourTimePicker.Time, Mode=OneWay}" /> </StackPanel> </Page> 実行すると以下のようになります。 TimePicker を選択すると、以下のようにドロップダウンが展開されて ClockIdentifier に応じ た選択肢が表示されます。以下の図は 12HourClock を設定した TimePicker の表示になりま す。
  • 162. 162 9.16. FlipView FlipView は左右にコンテンツを切り替えることが出来る ItemsControl になります。少数のデ ータをユーザーに対して提供するのに向いています。ItemsControl のように ItemsSource にコ レクションを設定して ItemTemplate を使って表示をカスタマイズできます。静的なコンテン ツでよければ、以下のように FlipView の直下に FlipViewItem を設定することでも作成できま す。コード例を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 163. 163 <FlipView> <FlipViewItem> <TextBlock Text="Item1" /> </FlipViewItem> <FlipViewItem> <TextBlock Text="Item2" /> </FlipViewItem> <FlipViewItem> <TextBlock Text="Item3" /> </FlipViewItem> </FlipView> </Grid> </Page> 実行すると以下のような表示になります。2 番目の Item2 を表示している状態になります。
  • 164. 164 左右の「<」や「>」のボタンを選択することで表示を切り替えることができます。あまりあり ませんが、縦に表示を切り替えたいときは ItemsPanel に Orientation を Vertical に設定した VirtualizingStackPanel にすることで実現できます。XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <FlipView> <FlipView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Vertical" /> </ItemsPanelTemplate> </FlipView.ItemsPanel> <FlipViewItem> <TextBlock Text="Item1" /> </FlipViewItem> <FlipViewItem> <TextBlock Text="Item2" /> </FlipViewItem> <FlipViewItem> <TextBlock Text="Item3" /> </FlipViewItem> </FlipView> </Grid> </Page> 以下のような表示になります。
  • 165. 165 切り替えボタンが上下になっているところで FlipView が横切り替えではなく縦切り替えになっ ていることが確認できます。 ItemsSource にデータを設定する使用方法を以下に示します。まず、コードビハインドに表示 するためのデータを準備します。 using System.Collections.Generic; using System.Linq; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { private IEnumerable<Person> People { get; } public MainPage()
  • 166. 166 { this.InitializeComponent(); this.People = Enumerable.Range(1, 5) .Select(x => new Person { Name = $"tanaka {x}", Age = 30 + x, }); } } public class Person { public string Name { get; set; } public int Age { get; set; } } } この People プロパティを FlipView の ItemsSource にバインドして ItemTemplate を設定しま す。XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <FlipView ItemsSource="{x:Bind People}"> <FlipView.ItemTemplate> <DataTemplate x:DataType="local:Person"> <Grid> <TextBlock>
  • 167. 167 <Run Text="{x:Bind Name}" /> <Run Text="さん" /> <LineBreak /> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </Grid> </DataTemplate> </FlipView.ItemTemplate> </FlipView> </Grid> </Page> 実行すると、以下のように表示されます。 ItemsSource に設定したデータに ItemTemplate が適用されて表示されていることが確認できま す。
  • 168. 168 9.17. Frame Frame はページ遷移をするための領域を提供します。実際のアプリケーション開発で Frame を 自分で作ることは、あまり多くありませんが、必ず意識しなければならないコントロールにな ります。Frame は、App.xaml.cs を見ればわかるように Window.Current.Content というコント ロールのルート要素として作成されています。 // App.xaml.cs から抜粋 Frame rootFrame = Window.Current.Content as Frame; // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返 さずに、 // ウィンドウがアクティブであることだけを確認してください if (rootFrame == null) { // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動 します rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: 以前中断したアプリケーションから状態を読み込みます } // フレームを現在のウィンドウに配置します Window.Current.Content = rootFrame; } この後に MainPage へ遷移が行われています。 rootFrame.Navigate(typeof(MainPage), e.Arguments); Frame の Navigate メソッドを使って、ページの型とページに渡す引数を指定して画面遷移がで きます。この第 2 引数で渡した画面遷移の引数は、Page の OnNavigatedTo メソッドの引数の
  • 169. 169 NavigationEventArgs の Parameter プロパティで参照することが出来ます。コメントにある通 り OnNavigatedTo メソッドは他の画面から遷移してきたときに呼ばれる Page のメソッドで す。それと対となる他ページへ移動したときに呼ばれる OnNavigatedFrom というメソッドも あります。OnNavigatedTo メソッドで渡された引数を取得するコードを以下に示します。 using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { // 他ページから画面遷移してきたときに呼ばれる base.OnNavigatedTo(e); var param = e.Parameter; } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 他ページへ画面遷移したときに呼ばれる base.OnNavigatedFrom(e); } } } この画面遷移時に渡せるパラメータは、int や string 型などの基本型や Guid 型などに限られて います。自作クラスなどを渡すことはできないので注意してください。
  • 170. 170 Frame 内の Page では、Frame プロパティを通じて Page をホストしている Frame を取得でき ます。これを使ってページ内のメソッドで Navigate メソッドを呼び出して別ページへ画面遷移 できます。以下のコードは OtherPage へ画面遷移する場合の例になります。 public void Click() { this.Frame.Navigate(typeof(OtherPage)); } 9.18. CommandBar CommandBar は、ボタンや任意のコントロールを配置できるバーになります。特に Page クラ スの TopAppBar プロパティと BottomAppBar プロパティに設定することで画面上部と下部に 固定されたコマンド領域を指定できます。CommandBar には PrimaryCommands プロパティ (コンテンツプロパティとして設定されています)と SecondaryCommands プロパティがあ り、その中に AppBarButton と AppBarSeparator と AppBarToggleButton を設定して使いま す。AppBarButton がボタンで AppBarSeparator が区切りで AppBarToggleButton がチェック ボックスのような役割になります。PrimaryCommands に設定した AppBarButton などはデフ ォルトで表示され、SecondaryCommands に設定された AppBarButton などは、CommandBar の右端に表示される「…」ボタンをクリックすることで、メニューが展開され表示されます。 AppBarButton と AppBarToggleButton は、Label プロパティと Icon プロパティを指定して使 います。Label プロパティは文字列情報で Icon プロパティはボタンに表示されるアイコンにな ります。Icon プロパティは、Visual Studio のプロパティウィンドウで簡単に様々なアイコンが 指定可能です。SymbolIcon を使って定義済みの様々なアイコンを使用できます。FontIcon を 使うことで Segoe MDL2 Assets などのアイコンが定義されたフォントを使って様々なアイコン を設定可能です。Icon プロパティのプロパティウィンドウでの編集の様子を以下に示します。
  • 171. 171
  • 172. 172 BottomAppBar に CommandBar を設定した場合の XAML の例を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Label="WiFi"> <AppBarButton.Icon> <FontIcon Glyph="&#xE701;" /> </AppBarButton.Icon> </AppBarButton> <AppBarSeparator /> <AppBarButton Icon="Accept" Label="Accept"/> <AppBarToggleButton Icon="AlignCenter" Label="Center"/> <CommandBar.SecondaryCommands> <AppBarButton Label="SubMenu1" /> <AppBarButton Label="SubMenu2" /> </CommandBar.SecondaryCommands> </CommandBar> </Page.BottomAppBar> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> </Grid> </Page> 初期状態で以下のように表示されます。
  • 173. 173 AppBarToggleButton は ToggleButton と同じように ON/OFF を切り開けることが出来ます。 ON にした状態を以下に示します。
  • 174. 174 画面右端の「…」ボタンを選択すると以下のように CommandBar が展開されて SecondaryCommands に定義したメニューや AppBarButton の Label などが表示されます。
  • 175. 175 AppBarButton が押されたときの操作は Button と同じように Click イベントや Command プロ パティに ICommand を設定することで可能です。 9.19. ListView と GridView ListView/GridView は UWP アプリで、よくつかうコントロールです。ListView がデフォルト で要素を縦に並べるのに対して GridView は要素を横に並べる点が特徴です。 以下に ListView と GridView の表示を比較するための簡単な XAML の例を示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
  • 176. 176 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <ListView> <ListViewItem Content="Item1" /> <ListViewItem Content="Item2" /> <ListViewItem Content="Item3" /> </ListView> <GridView Grid.Row="1"> <GridViewItem Content="Item1" /> <GridViewItem Content="Item2" /> <GridViewItem Content="Item3" /> </GridView> </Grid> </Page> 実行すると以下のようになります。ListView が縦で GridView が横に要素が並んでいることが 確認できます。
  • 177. 177 ListView は、要素が画面下部に到達するとスクロールバーでスクロールします。GridView は、要素が画面の右端に到達すると折り返します。折り返して画面下部に要素が到達すると縦 スクロールが表示されます。これがデフォルトの挙動になります。 実際に、コレクションをバインドして動作を確認します。以下のように 1000 項目の Person ク ラスを作成して ListView と GridView の ItemsSource にバインドします。 using System.Collections.Generic; using System.Linq; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public IEnumerable<Person> People { get; }
  • 178. 178 public MainPage() { this.InitializeComponent(); this.People = Enumerable.Range(1, 1000) .Select(x => new Person { Name = $"tanaka {x}", Age = 30 + x % 20, }); } } public class Person { public string Name { get; set; } public int Age { get; set; } } } XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="PersonTemplate" x:DataType="local:Person"> <Grid> <TextBlock> <Run Text="{x:Bind Name}" /> <Run Text="さん" />
  • 179. 179 <LineBreak /> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </Grid> </DataTemplate> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <ListView ItemsSource="{x:Bind People}" ItemTemplate="{StaticResource PersonTemplate}"> </ListView> <GridView Grid.Row="1" ItemsSource="{x:Bind People}" ItemTemplate="{StaticResource PersonTemplate}"> </GridView> </Grid> </Page> 実行すると以下のような表示になります。
  • 180. 180 ListView も GridView もデータの表示の並びが違うだけで、基本的に同じように使えます。こ こからの説明では ListView を主体に説明していきますが、同じ方法が基本的にも GridView に 適用可能です。 9.19.1.選 択 項目 の 操 作 選択項目の操作には SelectedValue プロパティか SelectedIndex プロパティを使用します。 SelectedValue が選択項目のインスタンスそのものを取得または設定するのに対して、 SelectedIndex は、ListView に設定されているコレクションの中の何番目の項目が選択されて いるかを取得または設定します。先ほどのプログラムの XAML を変更して以下のようにして SelectedIndex プロパティと SelectedItem プロパティの動作を確認してみます。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  • 181. 181 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="PersonTemplate" x:DataType="local:Person"> <Grid> <TextBlock> <Run Text="{x:Bind Name}" /> <Run Text="さん" /> <LineBreak /> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </Grid> </DataTemplate> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ListView x:Name="ListView" ItemsSource="{x:Bind People}" ItemTemplate="{StaticResource PersonTemplate}"> </ListView> <Grid Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <TextBlock Text="{x:Bind ListView.SelectedIndex, Mode=OneWay}" Style="{StaticResource HeaderTextBlockStyle}"/> <ContentControl Grid.Row="1" Content="{x:Bind ListView.SelectedItem, Mode=OneWay}" ContentTemplate="{StaticResource PersonTemplate}" />
  • 182. 182 </Grid> </Grid> </Page> ListView を画面左側に配置して、画面右側に ListView の SelectedIndex プロパティと SelectedItem プロパティをバインドしています。実行すると以下のようになります。初期状態 では、未選択状態のため SelectedIndex プロパティは-1 で、SelectedItem プロパティは null (プロパティが取得できていない)になっています。 項目を選択すると、SelectedIndex プロパティと SelectedItem プロパティに値が連動して入っ ていることが確認できます。
  • 183. 183 ListView は、SelectionMode プロパティで None、Single(規定値)、Multiple、Extended を指 定して選択時の動作をカスタマイズできます。Single のときは、今まで示した通り 1 つのみ項 目を選択できて、そのときの値は SelectedIndex プロパティと SelectedItem プロパティを使っ てアクセスできます。Multiple と Extended を指定した場合複数項目の選択ができるようにな ります。この場合は SelectedItems プロパティで選択された項目のコレクションが取得できま す。このコレクションにプログラムから値を追加することで選択項目を制御することもできま す。SelectionMode プロパティに Multiple を設定した場合は以下のように CheckBox が表示さ れ複数項目が選択可能になります。
  • 184. 184 Extended を設定した場合は、Ctrl キーや Shift キーを押した状態でクリックすることで複数選 択が可能になります(電話では不向きな設定な気がします)
  • 185. 185 選択項目を表示するコード例を以下に示します。SelectionMode プロパティに Multiple を設定 して CommandBar の AppBarButton を Click したときに SelectedItems プロパティを使って選 択項目を取得しています。 XAML は以下のようになります。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Label="Alert" Icon="Accept"
  • 186. 186 Click="{x:Bind Click}" /> </CommandBar> </Page.BottomAppBar> <Page.Resources> <DataTemplate x:Key="PersonTemplate" x:DataType="local:Person"> <Grid> <TextBlock> <Run Text="{x:Bind Name}" /> <Run Text="さん" /> <LineBreak /> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </Grid> </DataTemplate> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ListView x:Name="ListView" ItemsSource="{x:Bind People}" ItemTemplate="{StaticResource PersonTemplate}" SelectionMode="Multiple"> </ListView> </Grid> </Page> コードビハインドは以下のようになります。 using System; using System.Collections.Generic; using System.Linq; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace ControlApp
  • 187. 187 { public sealed partial class MainPage : Page { public IEnumerable<Person> People { get; } public MainPage() { this.InitializeComponent(); this.People = Enumerable.Range(1, 1000) .Select(x => new Person { Name = $"tanaka {x}", Age = 30 + x % 20, }); } public async void Click() { var message = string.Join(Environment.NewLine, this.ListView.SelectedItems .OfType<Person>() .Select(x => x.Name) .ToArray()); var dialog = new MessageDialog(message); await dialog.ShowAsync(); } } public class Person { public string Name { get; set; } public int Age { get; set; } } }
  • 188. 188 実行して、複数項目を選択して AppBarButton を押すと以下のように表示されます。 また、アイテムがタップされたときに、そのアイテムに対して操作をすることが可能です。 IsItemClickEnabled を True にすることで有効になります。SelectionMode を None にすると直 観的な動作になるので合わせて設定することをお勧めします。XAML の例を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 189. 189 <ListView ItemsSource="{x:Bind People}" SelectionMode="None" IsItemClickEnabled="True" ItemClick="{x:Bind ItemClick}"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:Person"> <Grid> <TextBlock> <Run Text="{x:Bind Name}" /> <Run Text="さん" /> <LineBreak /> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> </Page> コードビハインドでは、ItemClick イベントハンドラでイベント引数の ClickedItem プロパティ を使ってクリックされた項目の要素を取得できます。クリックされた項目の情報をメッセージ ダイアログで表示するコードを以下に示します。 using System; using System.Collections.Generic; using System.Linq; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page {
  • 190. 190 public IEnumerable<Person> People { get; } public MainPage() { this.InitializeComponent(); this.People = Enumerable.Range(1, 10) .Select(x => new Person { Name = $"tanaka {x}", Age = 30 + x, }); } public async void ItemClick(object sender, ItemClickEventArgs e) { var target = (Person)e.ClickedItem; var dialog = new MessageDialog($"{target.Name}さん {target.Age}歳"); await dialog.ShowAsync(); } } public class Person { public string Name { get; set; } public int Age { get; set; } } } 実行して、項目をタップすると以下のようになります。
  • 191. 191 9.20. Image Image コントロールは画像を表示する機能を提供します。Image コントロールの Source プロパ ティに ImageSource 型を指定することで画像を表示できます。XAML の場合は URL を指定す ることで、画像の表示ができます。 9.20.1.XAML 内 での URL XAML で指定できる URL は http や https で始まる Web 上のリソースを指し示すものの他に以 下のようなものがあります。  ms-appx:///Images/Sample.png アプリケーション内のリソースを表示します。上記 URL はアプリケーションのルートか ら Images フォルダ内にある Sample.png を指し示します。  ms-appdata:///local/Images/Sample.png アプリケーションのローカルフォルダを指し示します。上記 URL はアプリケーションの ローカルフォルダの Images フォルダ内にある Sample.png を指し示します。
  • 192. 192  ms-appdata:///roaming/Images/Sample.png アプリケーションのローミングフォルダを指し示します。上記 URL はアプリケーション のローミングフォルダの Images フォルダ内にある Sample.png を指し示します。  ms-appdata:///temp/Images/Sample.png アプリケーションの一時保存フォルダを指し示します。上記 URL はアプリケーションの 一時保存フォルダの Images フォルダ内にある Sample.png を指し示します。 9.20.2.画 像 の表 示 例 例として、デフォルトのプロジェクトに必ず入っている Assets フォルダの下にある SplashScreen.png を表示する XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Image Source="ms-appx:///Assets/SplashScreen.png" /> </Grid> </Page> 実行すると、以下のようになります。
  • 193. 193 C#から設定する場合は、BitmapImage クラスを使用して以下のように書きます。まず、XAML を修正して x:Name 属性をつけて C#からアクセスできるようにします。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Image x:Name="Image" /> </Grid> </Page> そして、コードビハインドで Uri クラスを BitmapImage のコンストラクタに渡して作成しま す。
  • 194. 194 using System; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.Image.Source = new BitmapImage(new Uri("ms-appx:///Assets/ SplashScreen.png")); } } } 実行結果は XAML で URL を指定したときと同じため省略します。 9.20.3.ユ ー ザー の 指 定 し たフ ァ イ ルの 表 示 ユーザーの指定したファイルを表示することも出来ます。ユーザーの指定したファイルは、 FileOpenPicker を使って取得できます。FileOpenPicker の使い方は以下のような流れになりま す。  FileOpenPicker のインスタンスを作成する  FileTypeFilter に開きたいファイルの拡張子を登録する  PickSingleFileAsync でファイルを開くダイアログを出す  戻り値の StorageFile が null だったらキャンセルなので何もしない  StorageFile の OpenReadAsync で IRandomAccessStream を取得する  BitmapImage の SetSourceAsync で BitmapImage に読み込む
  • 195. 195 コード例を以下に示します。まず、XAML でコードビハインドから触るための x:Name を指定 した Image コントロールと、ファイルを開く操作のきっかけになる AppBarButton を置いた画 面になります。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="Accept" Label="Open" Click="{x:Bind Click}" /> </CommandBar> </Page.BottomAppBar> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Image x:Name="Image" /> </Grid> </Page> コードビハインドでは、先ほどの FileOpenPicker の使い方の手順で示した通りのコードを書い ています。 using System; using Windows.Storage.Pickers; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; namespace ControlApp { public sealed partial class MainPage : Page {
  • 196. 196 public MainPage() { this.InitializeComponent(); } public async void Click() { var picker = new FileOpenPicker(); picker.FileTypeFilter.Add(".png"); picker.FileTypeFilter.Add(".jpg"); var file = await picker.PickSingleFileAsync(); if (file == null) { return; } using (var reader = await file.OpenReadAsync()) { var bitmap = new BitmapImage(); await bitmap.SetSourceAsync(reader); this.Image.Source = bitmap; } } } } 実行すると以下のようになります。実行して、AppBarButton を選択するとファイルを開くダ イアログが表示されます。
  • 198. 198 9.20.4.画 像 の表 示 時 の 拡 大方 法 方 法 Image コントロールは、明示的に幅と高さが指定されなかったときに画像をどのように拡大・ 縮小して表示するかということが設定可能です。Stretch プロパティが設定項目で、None、 Fill、Uniform、UniformToFill の 4 項目が設定可能です。  None オリジナルのサイズを保持する。  Fill Image コントロールのサイズいっぱいに表示する。画像の縦横比は維持されない。  Uniform(デフォルト値) 縦横比を維持しながら、Image コントロールに収まるように拡大・縮小する。  UniformToFill 縦横比を維持しながら、Image コントロールいっぱいに表示されるように変更する。
  • 199. 199 動作を確認するために、Assets フォルダの下にある小さな正方形の StoreLogo.png を表示して Stretch プロパティに上記値を適用して表示を比べてみます。 XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Image Grid.Row="0" Grid.Column="0" Source="ms-appx:///Assets/StoreLogo.png" Stretch="None" /> <Image Grid.Row="1" Grid.Column="0" Source="ms-appx:///Assets/StoreLogo.png" Stretch="Fill" /> <Image Grid.Row="0" Grid.Column="1" Source="ms-appx:///Assets/StoreLogo.png" Stretch="Uniform" /> <Image Grid.Row="1" Grid.Column="1"
  • 200. 200 Source="ms-appx:///Assets/StoreLogo.png" Stretch="UniformToFill" /> </Grid> </Page> 左上が None で、左下が Fill で、右上が Uniform で、右下が UniformToFill になります。実行 すると以下のようになります。 9.21. InkCanvas InkCanvas コントロールは、ペンなどで線を引くことが出来る機能を提供します。InkCanvas コントロールを画面に配置すると、それだけでペンによる入力を受け付けて線を引くことがで きます。XAML を以下に示します。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp"
  • 201. 201 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <InkCanvas /> </Grid> </Page> 実行すると以下のようにペンによる入力で線が引けます。 9.21.1.ペ ン 以外 で の 入 力 の受 け 付 け IncCanvas はデフォルトではペンでの入力のみ受け付けます。InkCanvas の InkPresenter プロ パティの InputDeviceTypes に CoreInputDeviceTypes 列挙体の Pen、Mouse、Touch を設定す ることでペン以外のデバイスからの入力を受け付けることが出来ます。XAML でコードビハイ ンドから触れるように名前を付けます。 <Page x:Class="ControlApp.MainPage"
  • 202. 202 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <InkCanvas x:Name="InkCanvas" /> </Grid> </Page> そして、コンストラクタで設定をします。 using Windows.UI.Core; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.InkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch; } } } これで実行するとペン以外のマウスやタッチでも入力を受け付けるようになります。 9.21.2.ペ ン の色 ・ 太 さ を 変え る
  • 203. 203 ペンの色と太さを変えるには InkDrawingAttributes を使用します。InkDrawingAttributes に各 種設定をして、InkPresenter の UpdateDefaultDrawingAttributes で更新を行います。例えば以 下のようなコードを、コードビハインドに記述します。 using Windows.Foundation; using Windows.UI; using Windows.UI.Core; using Windows.UI.Input.Inking; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.InkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch; // ペンの色や太さを変更する var attr = new InkDrawingAttributes { Color = Colors.Red, Size = new Size(10, 10), }; this.InkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attr); } } } 実行すると以下のようになります。
  • 204. 204 9.21.3.文 字 認識 文字認識には InkRecognizerContainer を使います。RecognizeAsync で InkCanvas の InkPresenter の StrokeContainer と、認識対象を示す引数を渡してやれば結果が返ってきま す。結果は、InkRecognitionResult の List なのでループで回して GetTextCandidates から認識 候補を取り出して処理をします。例として、第一候補のみをつなげた場合のコード例を示しま す。 XAML は以下のようになります。 <Page x:Class="ControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
  • 205. 205 <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="Accept" Label="Recognize" Click="{x:Bind Click}" /> </CommandBar> </Page.BottomAppBar> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <InkCanvas x:Name="InkCanvas" /> </Grid> </Page> コードビハインドは以下のようになります。 using System; using System.Linq; using Windows.UI.Input.Inking; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } public async void Click() { var irc = new InkRecognizerContainer(); var results = await irc.RecognizeAsync(this.InkCanvas.InkPresenter.StrokeContainer, InkRecognitionTarget.All);
  • 206. 206 var dlg = new MessageDialog(results.Select(x => x.GetTextCandidates().First()).Aggregate((x, y) => x + y)); await dlg.ShowAsync(); } } } 実行して文字を書いて AppBarButton を選択すると以下のように文字認識結果が表示されま す。 9.22. MapControl MapControl は地図を表示する機能を提供します。MapControl を使うには Bing Map デベロッ パーセンターというところでトークンを作る必要があります。Bing Map デベロッパーセンタ ーは以下の URL からアクセスできます。 https://www.bingmapsportal.com/
  • 207. 207 My account の Create or view keys の Click here to create a new key の here の所のリンクをクリ ックして必要情報を入力するとキーが作れます。 取得したキーを MapServiceToken に設定することで使えます。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps" x:Class="ControlApp.MainPage" mc:Ignorable="d">
  • 208. 208 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Maps:MapControl x:Name="MapControl" MapServiceToken="…Key…"/> </Grid> </Page> 実行すると、以下のように地図が表示されます。 地図はピンチイン・ピンチアウトやホイールスクロールなどで拡大縮小が出来ます。プログラ ムから拡大縮小を行うには、ZoomLevel プロパティを指定します。20 で最大まで拡大で、1 で 一番縮小した状態になります。最大まで拡大した例は以下のようになります。 this.MapControl.ZoomLevel = 20; マップを任意の場所に移動させるには、Center プロパティに、Geopoint クラスを指定しま す。Geopoint クラスのコンストラクタには、BasicGeoposition クラスを指定して、Latitude プ ロパティと Longitude プロパティで緯度経度を指定します。広島県庁を表示するコードは以下 のようになります。
  • 209. 209 using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.MapControl.ZoomLevel = 20; this.MapControl.Center = new Windows.Devices.Geolocation.Geopoint( new Windows.Devices.Geolocation.BasicGeoposition { Latitude = 34.396560, Longitude = 132.459622 }); } } } 実行すると以下のように広島県庁が最大拡大率で表示されます。
  • 210. 210 マップにアイコンを置くには MapIcon クラスを MapElements に追加することで実現できま す。MapIcon クラスは、Location プロパティに場所を指定して、Title プロパティにアイコン のタイトル、Image プロパティに表示するアイコンのイメージの IRandomAccessStreamReference を指定します。広島県庁にアイコンを置くコードを以下に示 します。 using System; using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Maps; namespace ControlApp { public sealed partial class MainPage : Page {
  • 211. 211 public MainPage() { this.InitializeComponent(); var ignore = SetupMapControlAsync(); } private async Task SetupMapControlAsync() { this.MapControl.ZoomLevel = 20; this.MapControl.Center = new Windows.Devices.Geolocation.Geopoint( new Windows.Devices.Geolocation.BasicGeoposition { Latitude = 34.396560, Longitude = 132.459622 }); // MapIcon var imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/StoreLogo.png")); var reference = RandomAccessStreamReference.CreateFromFile(imageFile); var mapIcon = new MapIcon { Location = new Windows.Devices.Geolocation.Geopoint( new Windows.Devices.Geolocation.BasicGeoposition { Latitude = 34.396560, Longitude = 132.459622 }), Title = "ひろしま!", Image = reference }; this.MapControl.MapElements.Add(mapIcon); } } }
  • 212. 212 非同期処理を内部で使うため、コンストラクタに直接書くのではなく、コンストラクタから非 同期のメソッドを 1 段挟んで初期化処理をしています。上記コードを実行すると以下のような 表示になります。 マップには、任意の XAML コントロールを置くことも出来ます。コントロールを作成して Location 添付プロパティを設定して Map コントロールの Children に追加することで表示でき ます。コードを以下に示します。 using System; using System.Threading.Tasks; using Windows.Storage; using Windows.Storage.Streams; using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Maps; using Windows.UI.Xaml.Media;
  • 213. 213 namespace ControlApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); var ignore = SetupMapControlAsync(); } private async Task SetupMapControlAsync() { this.MapControl.ZoomLevel = 20; this.MapControl.Center = new Windows.Devices.Geolocation.Geopoint( new Windows.Devices.Geolocation.BasicGeoposition { Latitude = 34.396560, Longitude = 132.459622 }); // MapIcon var imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/StoreLogo.png")); var reference = RandomAccessStreamReference.CreateFromFile(imageFile); var mapIcon = new MapIcon { Location = new Windows.Devices.Geolocation.Geopoint( new Windows.Devices.Geolocation.BasicGeoposition { Latitude = 34.396560, Longitude = 132.459622 }), Title = "ひろしま!", Image = reference };
  • 214. 214 this.MapControl.MapElements.Add(mapIcon); // XAML var border = new Border { BorderBrush = new SolidColorBrush(Colors.Red), BorderThickness = new Thickness(5), Width = 100, Height = 100 }; MapControl.SetLocation(border, new Windows.Devices.Geolocation.Geopoint( new Windows.Devices.Geolocation.BasicGeoposition { Latitude = 34.396560, Longitude = 132.459622 })); this.MapControl.Children.Add(border); } } } 実行すると、以下のようにアイコンに重なるように矩形が表示されます。
  • 215. 215 9.23. MediaElement MediaElement コントロールは音楽や動画の再生機能を提供します。MediaElement でサポート されるファイルの形式は以下のサイトを参照してください。 サポートされるオーディオとビデオの形式 (Windows ランタイム アプリ) https://msdn.microsoft.com/ja-jp/library/hh986969.aspx Assets フォルダの下に movie.wmv という動画ファイルを置いた場合、Source プロパティに以 下のように URL を指定することでファイルを再生することが出来ます。デフォルトでは自動 で再生が始まります。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 216. 216 x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <MediaElement Source="ms-appx:///Assets/movie.wmv" /> </Grid> </Page> 実行すると以下のように表示されます。 自動再生をオフにするには、AutoPlay を Flase に設定することで可能です。また、 AreTransportControlsEnabled を True にすることで組み込みの UI を表示することが出来ま す。XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 217. 217 x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <MediaElement Source="ms-appx:///Assets/movie.wmv" AutoPlay="False" AreTransportControlsEnabled="True"/> </Grid> </Page> 実行すると以下のように標準的な動画再生の UI が表示されるようになります。 また、IsMute プロパティでミュートの制御、IsFullScreen プロパティで全画面の制御、Volume プロパティで 0~1 の範囲で音量の調整ができます。 9.24. Povot Pivot コントロールは、FlipView のように複数のコンテンツの中から 1 つだけを表示する機能 を提供します。FlipView との違いは、Pivot コントロールの表示は以下のようにタブ表示に似
  • 218. 218 た感じで表示されるという点と FlipView が最後のコンテンツまで移動したあと最初のコンテン ツに戻れない点に対して、Pivot は端のコンテンツから反対側の端のコンテンツへ移動できる という点があります。 上記の表示を行う XAML は以下のようになります。基本的に、ほかの ItemsControl 系コント ロールと同じになります。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Pivot> <PivotItem Header="Item1">
  • 219. 219 <TextBlock Text="Hello pivot." /> </PivotItem> <PivotItem Header="Item2"> <TextBlock Text="Hello pivot." /> </PivotItem> <PivotItem Header="Item3"> <TextBlock Text="Hello pivot." /> </PivotItem> </Pivot> </Grid> </Page> ItemsSource プロパティと ItemTemplate プロパティを使用した表示も対応しています。Pivot には、ヘッダー項目もあるため HeaderTemplate も指定する必要があります。以下に使用例を 示します。まず、コードビハインドで Pivot に表示するコレクションを作成します。 using System.Collections.Generic; using System.Linq; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { public IEnumerable<Person> People { get; } public MainPage() { this.InitializeComponent(); this.People = Enumerable.Range(1, 10) .Select(x => new Person { Name = $"tanaka {x}", Age = 30 + x, });
  • 220. 220 } } public class Person { public string Name { get; set; } public int Age { get; set; } } } そして、XAML で HeaderTemplate と ItemTemplate を設定した Pivot を作成します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Pivot ItemsSource="{x:Bind People}"> <Pivot.ItemTemplate> <DataTemplate x:DataType="local:Person"> <Grid> <TextBlock Text="{x:Bind Age}" Style="{StaticResource HeaderTextBlockStyle}" /> </Grid> </DataTemplate> </Pivot.ItemTemplate> <Pivot.HeaderTemplate> <DataTemplate x:DataType="local:Person"> <Grid> <TextBlock Text="{x:Bind Name}" Style="{StaticResource TitleTextBlockStyle}" /> </Grid>
  • 221. 221 </DataTemplate> </Pivot.HeaderTemplate> </Pivot> </Grid> </Page> 実行すると以下のようになります。 9.25. PasswordBox PasswordBox コントロールは、認証画面のパスワード入力欄のようにユーザーの入力をマスク して見えないようにする入力欄を提供します。XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 222. 222 x:Class="ControlApp.MainPage" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <PasswordBox x:Name="PasswordBox" /> </StackPanel> </Page> 実行して文字を打ち込むと以下のような表示になります。 PasswordBox は、Password プロパティで入力を取得できます。また、PasswordChar プロパテ ィで入力をマスクしたときに表示される文字を指定できます。XAML での使用例を以下に示し ます。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 223. 223 x:Class="ControlApp.MainPage" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <PasswordBox x:Name="PasswordBox" PasswordChar="*"/> <TextBlock Text="{x:Bind PasswordBox.Password, Mode=OneWay}" /> </StackPanel> </Page> TextBlock に PasswordBox の Password プロパティをバインドして結果を表示しています。ま た PasswordChar プロパティでパスワードをマスクしたときに表示される文字を*に変更して います。実行して p@ssw0rd と入力したときの表示結果を以下に示します。 9.26. ProgressBar ProgressBar は、ユーザーに処理の進捗状況を伝える機能を持ったコントロールです。 Minimum プロパティで、値の最小値を指定して、Maximum プロパティで最大値を指定しま
  • 224. 224 す。Value プロパティで、Minimum と Maximum の間の値を指定することで進捗度合を示すこ とができます。また、進捗を明確に示すことができないが処理が進んでいることを示すための アニメーションを表示するための IsIndeterminate プロパティがあります。このプロパティを True にすることで、アニメーションが有効化されます。XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ProgressBar Minimum="0" Maximum="100" Value="50" Margin="5"/> <ProgressBar Minimum="0" Maximum="1000" Value="400" Margin="5" /> <ProgressBar IsIndeterminate="True" /> </StackPanel> </Page> 実行すると以下のようになります。一番上が 50%の進捗で、真ん中が 40%の進捗で、一番下 が、処理が進んでいることを示すアニメーションになります。
  • 225. 225 9.27. ProgressRing ProgressRing も ProgressBar と同じで処理が実行中であることを示す UI を提供します。 ProgressRing は、進捗度合は示すことはできずに処理が進んでいることを示すアニメーション を表示することができます。IsActive プロパティを False に設定することで非表示にすること ができます。Visibility プロパティとの違いは、IsActive が False の場合は、非表示になります がレイアウト上は残っているため、スペースを占有するという点です。XAML を以下に示しま す。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 226. 226 <ProgressRing IsActive="False" Width="100" Height="100" /> <ProgressRing IsActive="True" Width="100" Height="100"/> </StackPanel> </Page> 実行すると以下のようになります。上の ProgressRing は非表示ですが、領域は占有しているこ とが確認できます。 9.28. ScrollViewer ScrollViewer は、スクロール可能な領域を提供するコントロールです。水平方向と、垂直方向 のスクロールを提供するほかに、コンテンツの拡大・縮小の機能も提供します。ScrollViewer は、HorizontalScrollMode プロパティ、VerticalScrollMode プロパティに、Auto、Enabled、
  • 227. 227 Disabled を設定することで、水平方向・垂直方向のスクロールの自動判定、有効、無効を設定 できます。さらに、HorizontalScrollBarVisibility プロパティ、VerticalScrollBarVisibility プロ パティに Auto、Visible、Hidden、Disabled を設定することでスクロールバーの表示を自動判 定、表示、隠す、無効化を設定できます。 ScrollBarVisibility でスクロールバーを表示していても、ScrollMode を Disabled にすることで スクロールバーが表示されていてもスクロールが出来ないという状態を作ることができます。 (通常はしませんが、こういう設定になっていてはまることがあります)また、 ScrollBarVisibility を Hidden にすることでスクロールバーを非表示にしたままスクロール可能 な領域を提供することができます。 また、ZoomMode プロパティを Enabled、Disabled を設定することで拡大・縮小を有効化、無 効化することができます。 各種プロパティの初期値は以下のようになっています。  HorizontalScrollBarVisibility:Disabled  VerticalScrollBarVisibility:Visible  HorizontalScrollMode:Auto  VerticalScrollMode:Auto  ZoomMode:Disabled 例として、画像を縦横スクロールと拡大縮小が可能にする場合の XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 229. 229 ScrollViewer には、HorizontalScrollBarVisibility のプロパティなどと同名の添付プロパティが あります。GridView や ListView などのような内部に ScrollViewer を持ったコントロールに設 定することで、内部の ScrollViewer の挙動をカスタマイズできます。例えばデフォルトで縦方 向にスクロールする ListView ですが、以下のように ItemsPanel と ScrollViewer の添付プロパ ティを使って横スクロールにすることができます。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <!-- 水平スクロールを有効にする --> <ListView ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Disabled"
  • 230. 230 ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled"> <ListView.ItemsPanel> <ItemsPanelTemplate> <!-- 水平方向にコンテンツを並べる --> <ItemsStackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ListView.ItemsPanel> <Border Width="300" Height="300" Background="Red" /> <Border Width="300" Height="300" Background="Blue" /> <Border Width="300" Height="300" Background="Cyan" /> <Border Width="300" Height="300" Background="Gray" /> </ListView> </Grid> </Page> 実行結果を以下に示します。
  • 231. 231 9.29. Slider Slider は、一定の範囲の値を入力するための UI を提供します。ProgressBar と同じように Minimum プロパティと Maximum プロパティで値の範囲を設定し、Value プロパティで現在の 値を取得または設定できます。XAML の例を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Slider x:Name="Slider" Minimum="50" Maximum="150" />
  • 232. 232 <TextBlock Text="{x:Bind Slider.Value, Mode=OneWay}" Style="{StaticResource HeaderTextBlockStyle}"/> </StackPanel> </Page> 実行すると以下のようになります。 9.30. WebView WebView は、Web ブラウザの機能を提供します。Source プロパティに Uri を設定すること で、そのページを表示することができます。例えば Bing を表示するような XAML は以下のよ うになります。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 233. 233 x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <WebView Source="http://bing.co.jp" /> </Grid> </Page> 実行すると以下のようになります。 NavigationStarting メソッドや NavigationCompleted イベントを購読することで画面遷移の状 態をハンドリングできます。NavigationStarting が画面遷移開始時に呼ばれるイベントで NavigationCompleted イベントが、画面遷移が完了したときに呼ばれます。その他に NavigationFailed イベントという画面遷移に失敗したときに呼ばれるイベントもあります。こ のイベントを使うことで、画面遷移中に ProgressRing を表示するといったことが出来るように なります。XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  • 234. 234 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="Accept" Label="Google" Click="AppBarButton_Click" /> </CommandBar> </Page.BottomAppBar> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <WebView x:Name="WebView" NavigationStarting="WebView_NavigationStarting" NavigationCompleted="WebView_NavigationCompleted" /> <ProgressRing x:Name="ProgressRing" Width="100" Height="100" Visibility="Collapsed" IsActive="True" /> </Grid> </Page> コードビハインドを以下に示します。WebView は Navigate メソッドに Uri を渡すことで画面 遷移することができるので、それを使って Google を表示しています。 using System; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page {
  • 235. 235 public MainPage() { this.InitializeComponent(); } private void WebView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args) { this.ProgressRing.Visibility = Windows.UI.Xaml.Visibility.Visible; } private void WebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args) { this.ProgressRing.Visibility = Windows.UI.Xaml.Visibility.Collapsed; } private void AppBarButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) { this.WebView.Navigate(new Uri("http://google.co.jp")); } } } 実行すると以下のようになります。読み込み中にプログレスリングが表示されていることが確 認できます。
  • 236. 236 WebView は Uri で示すリソースとしてアプリ内のリソースを指定することが出来ます。ms- appx-web で始まる Uri がそれにあたります。例えばアプリのルートに html というフォルダを 用意して、その中の index.html を表示する場合は以下のようになります。 this.WebView.Navigate(new Uri(“ms-appx-web:///html/index.html”)); また、HTML 形式の文字列を指定することもできます。NavigateToString というメソッドを使 い引数に HTML を渡すことで、その HTML を表示することが出来ます。以下にコード例を示 します。 this.WebView.NavigateToString(@" <html> <head><title>Hello</title></head> <body><b>Hello world</b></body> </html>"); 実行すると以下のようになります。
  • 237. 237 9.31. AugoSuggestBox AutoSuggestBox は、検索用テキストボックスの UI を提供します。検索ワードに応じて検索候 補を表示したりといった一般的な検索用テキストボックスの機能を提供します。 AutoSuggestBox のデフォルトの見た目は TextBox と同じになります。以下のような XAML を 記述したとします。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <AutoSuggestBox /> </Grid>
  • 239. 239 AutoSuggestBox で一番よく使うと思うのは虫眼鏡のアイコンになると思うので、QueryIcon プ ロパティに Find を指定することが多いと思います。 <AutoSuggestBox QueryIcon="Find" /> 実行すると以下のような見た目になります。
  • 240. 240 検索候補の表示は、TextChanged イベントで検索広報を ItemsSource に設定することで作成で きます。以下のようなコードになります。まず XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <AutoSuggestBox QueryIcon="Find" TextChanged="AutoSuggestBox_TextChanged"/> </Grid> </Page>
  • 241. 241 コードビハインドを以下に示します。入力を見て ItemsSource に適当な値を設定しています。 本番では、入力に応じて適切な値を設定すると良いと思います。イベント引数の Reason プロ パティは、どういう経緯で入力が変化したのかを検出できます。今回はユーザーの入力により 変化したということを条件にしています。 using System; using System.Linq; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { private Random Random { get; } = new Random(); public MainPage() { this.InitializeComponent(); } private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) { if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) { sender.ItemsSource = string.IsNullOrWhiteSpace(sender.Text) ? null : Enumerable.Range(1, 5 + this.Random.Next(5)) .Select(x => $"{sender.Text}の検索候補 {x}"); } } } } 実行してテキストを入力すると、入力に応じて以下のような結果が表示されます。
  • 242. 242 検索の確定は QuerySubmitted イベントで拾うことが出来ます。QuerySubmitted イベントのイ ベント引数の QueryText で入力文字列を取得できます。XAML を以下に示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <AutoSuggestBox QueryIcon="Find" TextChanged="AutoSuggestBox_TextChanged" QuerySubmitted="AutoSuggestBox_QuerySubmitted"/> </Grid> </Page> コードビハインドを以下に示します。検索文字列をダイアログで表示しています。
  • 243. 243 using System; using System.Linq; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace ControlApp { public sealed partial class MainPage : Page { private Random Random { get; } = new Random(); public MainPage() { this.InitializeComponent(); } private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) { if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) { sender.ItemsSource = string.IsNullOrWhiteSpace(sender.Text) ? null : Enumerable.Range(1, 5 + this.Random.Next(5)) .Select(x => $"{sender.Text}の検索候補 {x}"); } } private async void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) { await new MessageDialog(args.QueryText).ShowAsync(); } }
  • 244. 244 } 実行して選択候補を選ぶか、アイコンを選択することで以下のように表示されます。 AutoSuggestBox は、ItemTemplate を設定出来るため、ItemsSource に文字列ではないクラス を要素とした IEnumerable<T>を設定して ItemTemplate を適用することで、検索候補を文字 列よりも情報量を豊富に表示することが出来ます。例えば、以下のようなコードになります。 XAML を以下に示します。Name と Age を持った Person クラスの存在を前提にしています。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <AutoSuggestBox QueryIcon="Find" TextChanged="AutoSuggestBox_TextChanged" QuerySubmitted="AutoSuggestBox_QuerySubmitted" SuggestionChosen="AutoSuggestBox_SuggestionChosen"> <AutoSuggestBox.ItemTemplate>
  • 245. 245 <DataTemplate x:DataType="local:Person"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Image Source="ms-appx:///Assets/StoreLogo.png" Grid.RowSpan="2" /> <TextBlock Text="{x:Bind Name}" Style="{StaticResource BodyTextBlockStyle}" Grid.Column="1"/> <TextBlock Style="{StaticResource CaptionTextBlockStyle}" Grid.Column="1" Grid.Row="1"> <Run Text="{x:Bind Age}" /> <Run Text="歳" /> </TextBlock> </Grid> </DataTemplate> </AutoSuggestBox.ItemTemplate> </AutoSuggestBox> </Grid> </Page> コードビハインドを以下に示します。 using System; using System.Linq; using Windows.UI.Popups; using Windows.UI.Xaml.Controls;
  • 246. 246 namespace ControlApp { public sealed partial class MainPage : Page { private Random Random { get; } = new Random(); public MainPage() { this.InitializeComponent(); } private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) { if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) { sender.ItemsSource = string.IsNullOrWhiteSpace(sender.Text) ? null : Enumerable.Range(1, 5 + this.Random.Next(5)) .Select(x => new Person { Name = $"{sender.Text} {x}", Age = 30 + x, }); } } private async void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) { if (args.ChosenSuggestion == null) { await new MessageDialog(args.QueryText).ShowAsync(); } else
  • 247. 247 { await new MessageDialog(((Person)args.ChosenSuggestion).Name).ShowAsync(); } } private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args) { sender.Text = ((Person)args.SelectedItem).Name; } } public class Person { public string Name { get; set; } public int Age { get; set; } } } SuggestionChosen イベントで選択されたオブジェクトを実際のテキストにマッピングを行いま す。QuerySubmitted イベントで、ChosenSuggestion に値が入っているか入っていないか(入 っている場合は選択肢が選ばれたという意味)を確認して処理を分岐しています。実行すると 以下のようになります。 実行して適当にテキストを入力した状態。
  • 249. 249 SplitView は左側にメニュー、右側にコンテンツを表示するコントロールです。SplitView の Pane プロパティにメニュー部の UI を定義して、Content プロパティにコンテンツの UI を定 義します。Pane プロパティに定義したメニューは IsPaneOpen プロパティで True、False で表 示、非表示を切り替えることが出来ます。 Pane プロパティに定義した部分の表示方法は、DisplayMode プロパティで変更することが出 来ます。CompactOverlay、CompactInline、Overlay、Inline の 4 種類が設定できます。 Compact と名前がついているものは、IsPaneOpen プロパティが False の場合でも CompactPaneLength プロパティで指定したサイズだけ表示されます。デフォルトは 48 なので 48 ピクセル表示されます。Overlay と名前がついているものは、Content プロパティを覆い隠 すように表示されます。Inline と名前がついているものは、Content プロパティの左側にイン ラインで表示されます。つまり、Pane が表示されると、そのぶん Content プロパティで定義 した UI は右にずれます。 9.32.1.ハ ン バー ガ ー メ ニ ュー の 実 装 SplitView は、ハンバーガーメニューを実装するのに、よく使われます。ToggleButton の IsChecked プロパティと SplitView の IsPaneOpen プロパティを Binding することで、それら しく表示することが出来ます。以下に Pane プロパティ部分に ListView を使用した XAML の 例を示します。 <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="ControlApp.MainPage" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <SplitView IsPaneOpen="{Binding IsChecked, ElementName=ToggleButtonSplitView, Mode=TwoWay}" DisplayMode="CompactOverlay"> <SplitView.Pane> <Grid>
  • 250. 250 <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <ToggleButton x:Name="ToggleButtonSplitView" FontFamily="{ThemeResource SymbolThemeFontFamily}" Content="&#xE700;" Width="48" Height="40" /> <TextBlock Text="Select menu" Style="{StaticResource TitleTextBlockStyle}" Margin="5,0" /> </StackPanel> <ListView x:Name="ListView" SelectionMode="Single" SelectedIndex="0" Grid.Row="1"> <ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <Setter Property="Padding" Value="0" /> <Setter Property="Margin" Value="0" /> </Style> </ListView.ItemContainerStyle> <ListViewItem> <StackPanel Orientation="Horizontal"> <SymbolIcon Symbol="Accept" Width="48" /> <TextBlock Text="Menu 1" Style="{StaticResource BodyTextBlockStyle}" /> </StackPanel> </ListViewItem>
  • 251. 251 <ListViewItem> <StackPanel Orientation="Horizontal"> <SymbolIcon Symbol="Find" Width="48" /> <TextBlock Text="Menu 2" Style="{StaticResource BodyTextBlockStyle}" /> </StackPanel> </ListViewItem> <ListViewItem> <StackPanel Orientation="Horizontal"> <SymbolIcon Symbol="Edit" Width="48" /> <TextBlock Text="Menu 3" Style="{StaticResource BodyTextBlockStyle}" /> </StackPanel> </ListViewItem> </ListView> </Grid> </SplitView.Pane> <ContentControl Content="{x:Bind ListView.SelectedIndex, Mode=OneWay}" FontSize="48" /> </SplitView> </Grid> </Page> ハンバーガーメニューのアイコンは SymbolThemeFontFamily で定義されたフォントの &#xE700;で表示することが出来ます。実行すると以下のようになります。
  • 253. 253 10. 共有 UWP では、アプリ間でデータを共有するための仕組みが提供されています。この仕組みを使 うことで、簡単にアプリ間でテキストや画像などといったファイルをやりとりして動作するこ とが出来ます。 10.1.1.共 有 の送 信 側 の 作 成 共有の送信側を作成するには、DataTransferManager の DataRequested イベントを実装しま す。DataTransferManager クラスは、GetForCurrentView 静的メソッドで取得することが出来 ます。アプリケーション全体で同じものを共有するなら App クラスの OnLaunch メソッド で、ページ単位で共有を有効化、無効化したい場合は Page の OnNavigatedTo でイベントを購 読して、OnNavigatedFrom でイベントの購読解除を行うと良いでしょう。ページで購読する場 合は、以下のようなコードイメージになります。 protected override void OnNavigatedTo(NavigationEventArgs e)
  • 254. 254 { base.OnNavigatedTo(e); var dtm = DataTransferManager.GetForCurrentView(); dtm.DataRequested += this.DataTransferManager_DataRequested; } protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); var dtm = DataTransferManager.GetForCurrentView(); dtm.DataRequested -= this.DataTransferManager_DataRequested; } private void DataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { // TODO : ここに共有コンテンツを提供する処理を書く } DataRequested イベント引数の Request プロパティで DataRequest クラスのインスタンスが取 得できます。このクラスの Data プロパティで DataPackage クラスのインスタンスが取得でき ます。この DataPackage クラスにデータを設定したりすることで共有するコンテンツを指定で きます。DataPackage クラスには様々なデータを渡すためのメソッドがあります。以下のよう なデータがサポートされています。  テキスト(SetText メソッド)  URI(SetUri メソッド)  HTML(SetHtmlFormat メソッド)  RTF(SetRtf メソッド)  ビットマップ(SetBitmap メソッド)  ファイル(SetStorageItems メソッド)  カスタムデータ(SetData メソッド)
  • 255. 255 それぞれ、専用の設定用メソッドが提供されています。データを設定する他に、DataPackage クラスの Properties クラスの Title を設定する必要があります。これは共有を説明するための タイトルのテキストになります。より詳細な説明を設定するための Description プロパティも 提供されています。特に理由の無い限りは Description プロパティも設定するのが良いでしょ う。 共有のための UI を表示するメソッドがあります。DataTransferManager クラスの ShowShareUI 静的メソッドがそれになります。このメソッドを呼び出すことで、共有のための UI が表示され、結果として DataTransferManager クラスの DataRequested メソッドが呼び出 されます。 簡単な入力テキストを共有するコード例を以下に示します。まず、XAML に共有用のテキスト を設定する TextBox を置いて、共有を行うためのアプリバーボタンを置いているだけのシンプ ルな画面を構築します。 <Page x:Class="ShareApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ShareApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="ReShare" Label="Share" Click="{x:Bind ShareClick}"/> </CommandBar> </Page.BottomAppBar> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Text="{x:Bind InputText, Mode=TwoWay}"/> </StackPanel> </Page>
  • 256. 256 コードビハインドで、データの共有のためのイベントのセットアップや共有処理を行います。 using Windows.ApplicationModel.DataTransfer; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace ShareApp { public sealed partial class MainPage : Page { public string InputText { get; set; } public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); var dtm = DataTransferManager.GetForCurrentView(); dtm.DataRequested += this.DataTransferManager_DataRequested; } protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); var dtm = DataTransferManager.GetForCurrentView(); dtm.DataRequested -= this.DataTransferManager_DataRequested; } private void DataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { if (string.IsNullOrWhiteSpace(this.InputText)) {
  • 257. 257 return; } args.Request.Data.Properties.Title = "入力テキストの共有"; args.Request.Data.Properties.Description = "入力したテキストを共有します。"; args.Request.Data.SetText(this.InputText); } public void ShareClick() { DataTransferManager.ShowShareUI(); } } } 実行してテキストを入力して共有ボタンを選択すると以下のように共有の UI が表示されま す。
  • 258. 258 共有の UI の上部に設定した Title と Description が表示されていることが確認できます。ここ から例えば Twitter アプリを選択することで以下のようにツイート画面に共有したテキストが 表示されます。 共有するためのデータを用意するときに IO が発生するなど、非同期のメソッドを呼び出す場 合のために、DataRequest メソッドでは GetDeferral メソッドが提供されています。この戻り 値の Complete メソッドを呼び出すことで共有に必要な処理が完了したことを外部に通知でき ます。例として、DataRequested イベントでアプリ内のリソースを共有する場合の例を以下に 示します。 private async void DataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { var d = args.Request.GetDeferral(); var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/StoreLogo.png")); args.Request.Data.Properties.Title = $"{file.Name}の共有"; args.Request.Data.SetStorageItems(new[] { file }); d.Complete(); } このコードを実行すると、以下のようになります。
  • 259. 259 共有は、共有の UI でアプリが選択されないと共有のために用意したデータは無駄になってし まいます。そのため、毎回共有のたびに巨大なデータを作るようなケースは操作が重たくなっ てしまい、ユーザーにとって悪い体験を提供してしまうことになります。このような時のため に、データが必要になったタイミングで呼び出されるデリゲートを渡すことができます。使用 するメソッドは SetDataProvider メソッドになります。コード例を以下に示します。 private void DataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { args.Request.Data.Properties.Title = "アプリ内リソースの共有"; args.Request.Data.SetDataProvider(StandardDataFormats.Bitmap, async (e) => { var d = e.GetDeferral(); var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/StoreLogo.png")); var reference = RandomAccessStreamReference.CreateFromFile(file); e.SetData(reference); d.Complete(); }); }
  • 260. 260 10.1.2.共 有 の受 信 側 の 作 成 共有の受信側になるには、Package.appxmanifest ファイルを開いて「宣言」のタブの使用可能 な宣言で「共有ターゲット」を追加します。共有で受信可能なファイルの形式かデータ形式を 設定します。データ形式は「新規追加」ボタンを選択して Text などのデータ形式を入力する ことで設定します。URI、Bitmap、StorageItems などを指定したり、独自のデータ形式を指定 可能です。独自のデータ形式を指定する場合は、送信側もそれに対応していなければなりませ ん。以下は、Text を指定した場合の画面になります。 サポートされるファイルの種類も同様に「新規作成」ボタンを押して.txt などの拡張を入力し ます。以下は、.txt を指定した場合の画面になります。 ここで設定した内容に応じて、共有の UI の選択肢にアプリが表示されます。アプリが選択さ れると、App クラスの OnShareTargetActivated メソッドがコールバックされます。ここで共 有時の処理を行います。例えば Frame を作成して、共有用の UI ページへ遷移します。このと き、イベント引数の ShareOperation プロパティは、共有の完了を通知したりするのに必要なの
  • 261. 261 で、画面遷移の引数に渡してページに引き渡します。通常画面遷移では、画面遷移の引数にこ ういったオブジェクト型は渡しませんが、画面遷移履歴を保持しない共有の UI の場合に限っ て言えば選択肢としてありだと考えています。(というか後述する公式サンプルプログラムが そういう実装になっている) 例として ShareTargetPage というページを作ります。XAML を以下に示します。 <Page x:Class="ShareApp.ShareTargetPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ShareApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Header="共有されたテキスト" Text="{x:Bind SharedText, Mode=TwoWay}" /> <Button Content="Execute" Click="Button_Click" /> </StackPanel> </Page> コードビハインドを以下に示します。 using System; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.ShareTarget; using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation;
  • 262. 262 namespace ShareApp { public sealed partial class ShareTargetPage : Page, INotifyPropertyChanged { public ShareTargetPage() { this.InitializeComponent(); } public event PropertyChangedEventHandler PropertyChanged; private ShareOperation ShareOperation { get; set; } private string sharedText; public string SharedText { get { return this.sharedText; } set { this.sharedText = value; this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SharedText))); } } protected override async void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.ShareOperation = (ShareOperation)e.Parameter; if (this.ShareOperation.Data.Contains(StandardDataFormats.Text)) { this.SharedText = await this.ShareOperation.Data.GetTextAsync(); } else if (this.ShareOperation.Data.Contains(StandardDataFormats.StorageItems)) {
  • 263. 263 var items = await this.ShareOperation.Data.GetStorageItemsAsync(); var file = items.OfType<IStorageFile>().FirstOrDefault(); if (file != null) { this.SharedText = await FileIO.ReadTextAsync(file); } } } private async void Button_Click(object sender, RoutedEventArgs e) { await Task.Delay(3000); // 受信したデータを使って何かやる this.ShareOperation.ReportCompleted(); } } } OnNavigatedTo メソッドで受信したデータが Text か StorageItems の場合に内容を SharedText に格納しています。Button の選択時に 3 秒待って共有を完了しています。通常、 ここで受信したデータを使って何かやるのが一般的です。(Twitter クライアントならツイー トするなど)最後に、App クラスの OnShareTargetActivated で画面遷移処理を行います。コ ードを以下に示します。 protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args) { var f = new Frame(); Window.Current.Content = f; f.Navigate(typeof(ShareTargetPage), args.ShareOperation); Window.Current.Activate(); } 実行して、テキストファイルや文字列を共有すると以下のように共有の一覧にアプリが表示さ れます。
  • 265. 265 Execute ボタンを選択すると、3 秒まって共有アプリが終了することが確認できます。 11. バックグラウンドタスク バックグラウンドタスクは、アプリケーションが起動していないときや中断時にも OS によっ てバックグラウンドで実行されるプログラムです。バックグラウンドタスクは Windows ラン タイムプロジェクトで作成された IBackgroundTask インターフェースを実装したクラスで Package.appxmanifest と BackgroundTaskBuilder クラスによって設定された条件で起動しま す。 バックグラウンドタスクは、一般的に軽量なタスクをこなすために使用されます。常時実行し て高負荷な処理をするためのものではないので注意してください。バックグランドタスクは、 ウォールクロック時間で 30 秒間起動することが許されています。(ウォールクロック時間は 実際の時間だと思います) 11.1.1.バ ッ クグ ラ ウ ン ド タス ク の 作成 と 登 録 一番簡単なバックグラウンドタスクであるタイマーのバックグラウンドの作成を通してバック グラウンドタスクの作成から登録、実行までの流れを見ていきます。まず、バックグラウンド タスクを作成するために、以下のプロジェクトを作成します。  BackgroundTaskSampleApp(空白のアプリ(ユニバーサル Windows))  BackgroundTaskSampleApp.Tasks(Windows ランタイムコンポーネント(ユニバーサル Windows)) プロジェクトを作成したら、BackgroundTaskSampleApp から BackgroundTaskSampleApp.Tasks プロジェクトへの参照を追加します。 BackgroundTaskSampleApp.Tasks プロジェクトの Class1 クラスを TimerBackgroundTask にリ ネームして以下のコードを記述します。 using System.Diagnostics; using Windows.ApplicationModel.Background;
  • 266. 266 namespace BackgroundTaskSampleApp.Tasks { public sealed class TimerBackgroundTask : IBackgroundTask { public void Run(IBackgroundTaskInstance taskInstance) { Debug.WriteLine(“Hello world background task.”); } } } 次に、BackgroundTaskSampleApp プロジェクトの Package.appxmanifest を開いて「宣言」タ ブを開きます。使用可能な宣言から「バックグラウンド タスク」を選択して追加します。プロ パティが表示されるので「サポートされるタスクの種類」でタスクの種類を設定します。今回 はタイマーなので「タイマー」にチェックを入れます。次に「エントリポイント」でタスクの クラス名を指定します。今回は「BackgroundTaskSampleApp.Tasks.TimerBackgroundTask」 を指定します。
  • 267. 267 最後に、コードでバックグラウンドタスクの登録を行います。App クラスの OnLaunched メソ ッドの先頭で以下のコードを記述します。 // ステータスチェック var status = await BackgroundExecutionManager.RequestAccessAsync(); if (status != BackgroundAccessStatus.Denied) { // 存在チェック if (!BackgroundTaskRegistration.AllTasks.Values.Any(x => x.Name == "Sample timer task")) { // 登録
  • 268. 268 var b = new BackgroundTaskBuilder { Name = "Sample timer task", TaskEntryPoint = "BackgroundTaskSampleApp.Tasks.TimerBackgroundTask", }; // 15 分のタイマーでワンショットではない b.SetTrigger(new TimeTrigger(15, false)); // 条件としてユーザーがいるときのみ実行する(オプション) b.AddCondition(new SystemCondition(SystemConditionType.UserPresent)); b.Register(); } } まず、バックグラウンドタスクを登録する前に、RequestAccessAsync メソッドを呼び出す必要 があります。ユーザーが明示的にデバイス設定でバックグラウンドタスクに対するアプリのア クセス許可を拒否した場合は、BackgroundAccessStatus.Denied を返します。次に、これから 登録するバックグラウンドタスクが既に登録されているかどうかをチェックします。このチェ ックで、まだバックグラウンドタスクが登録されていない場合は BackgroundTaskBuilder でバ ックグラウンドタスクを登録します。BackgroundTaskBuilder では、Name(任意の名前。こ の名前でタスクの存在チェック等を行います)と TaskEntryPoint(タスクのクラス名)と Trigger(バックグラウンドタスクを起動するトリガーの種類)と、バックグラウンドタスクが 起動する条件(オプション)を設定したうえで Register メソッドを呼び出します。今回の場合 は、Sample timer task が名前で、BackgroundTaskSampleApp.Tasks.TimerBackgroundTask が エントリーポイントで、TimeTrigger がトリガーで、new SystemCondigion(SystemConditionType.UserPresent))が条件になります。TimeTrigger は、 第一引数で実行間隔を指定して、第二引数でワンショットかどうかを指定します。今回の例で は 15 分間隔でワンショットではないという指定をしています。TimeTrigger は 15 分が指定で きる最小の値で、それ以下の値を指定した場合は例外がスローされます。 条件は任意に設定でき、SystemConditionType で指定できます。今回は、UserPresent という ユーザーが存在する間だけバックグラウンドタスクが実行できるという条件を指定しました。 その他に InternetAvailable などネットワーク接続が有効な場合などが指定できます。
  • 269. 269 SystemConditionType については以下のドキュメントを参照してください。 https://msdn.microsoft.com/library/windows/apps/br224835 プログラムを実行すると画面が起動します。15 分待つと最低でも 1 回出力ウィンドウにメッセ ージが出力されます。現実問題としてデバッグしづらいので、バックグラウンドタスクを任意 に起動させる手段も提供されています。「ライフサイクルイベント」の右側の▼を選択する と、バックグラウンドタスクの名前が表示されます。これを選択することで、任意のタイミン グでバックグラウンドタスクを実行することが出来ます。 これを選択すると出力ウィンドウに以下のメッセージが表示されます。 Hello world background task. 11.1.2.バ ッ クグ ラ ウ ン ド タス ク と フォ ア グ ラ ン ドの 連 携 バックグラウンドタスクとフォアグランドの処理の連携方法について説明します。バックグラ ウンドタスクとフォアグランドの処理は、static な変数などを利用して連携を行うことはでき ません。BackgroundTaskRegistration(Register メソッドの戻り値か AllTasks から取得する) の Progress イベントで途中経過を得ることと、Completed イベントでバックグラウンドタスク の完了を知ることが出来ます。データのやり取りは Progress イベントではイベント引数の Progress プロパティで進捗を確認するための数値が確認できます。それ以外のデータについて は ApplicationData.Current.LocalSettings を使用します。
  • 270. 270 例として、先ほど作成した TimerBackgroundTask を以下のように変更します。 using System; using Windows.ApplicationModel.Background; using Windows.Storage; using Windows.System.Threading; namespace BackgroundTaskSampleApp.Tasks { public sealed class TimerBackgroundTask : IBackgroundTask { private IBackgroundTaskInstance TaskInstance { get; set; } private BackgroundTaskDeferral Deferral { get; set; } public void Run(IBackgroundTaskInstance taskInstance) { this.TaskInstance = taskInstance; this.Deferral = this.TaskInstance.GetDeferral(); ThreadPoolTimer.CreatePeriodicTimer(this.Tick, TimeSpan.FromSeconds(1)); } private void Tick(ThreadPoolTimer timer) { if (this.TaskInstance.Progress >= 100) { // タイマーを停止して完了時刻のタイムスタンプを LocalSettings に設定し て終了する timer.Cancel(); ApplicationData.Current.LocalSettings.Values["TimerBackgroundTask"] = DateTimeOffset.Now.ToString("yyyy/MM/dd HH:mm:sszzz"); this.Deferral.Complete(); } else {
  • 271. 271 // Progress を更新する this.TaskInstance.Progress += 10; } } } } 1 秒間隔のタイマーを作成して、その中で Progress を 10 ずつインクリメントしています。今 回のように、Run メソッドの終了よりも遅延してバックグラウンドタスクの終了を通知したい 場合は GetDeferral メソッドで BackgroundTaskDeferral を取得します。 BackgroundTaskDeferral は、Complete を呼ぶことでタスクの終了を通知できます。タイマー の中で 10 回処理が実行されたら、現在の時間を LocalSettings に書き込んで終了しています。 MainPage.xaml を以下のように編集します。状態を表示するための TextBlock を置いていま す。 <Page x:Class="BackgroundTaskSampleApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:BackgroundTaskSampleApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock x:Name="TextBlock" Style="{StaticResource HeaderTextBlockStyle}" /> </StackPanel> </Page> コードビハインドを以下のように編集します。 using System; using System.Collections.Generic; using System.IO; using System.Linq;
  • 272. 272 using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel.Background; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.Storage; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace BackgroundTaskSampleApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); // タスクの状態を監視する var r = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(x => x.Name == "Sample timer task"); if (r != null) { r.Progress += this.BackgroundTask_Progress; r.Completed += this.BackgroundTask_Completed; } }
  • 273. 273 protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); // タスクの監視を終了する var r = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(x => x.Name == "Sample timer task"); if (r != null) { r.Progress -= this.BackgroundTask_Progress; r.Completed -= this.BackgroundTask_Completed; } } private async void BackgroundTask_Progress(BackgroundTaskRegistration sender, BackgroundTaskProgressEventArgs args) { await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { this.TextBlock.Text = $"Progress: {args.Progress}"; }); } private async void BackgroundTask_Completed(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args) { await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { this.TextBlock.Text = $"Completed time: {ApplicationData.Current.LocalSettings.Values["TimerBackgroundTask"]}"; }); } } }
  • 274. 274 BackgroundTaskRegistration から Sample timer task を取り出し、Progress イベントと Completed イベントを購読しています。イベントの中では Progress を表示するのと、 Completed イベントでは、LocalSettings の TimerBackgroundTask に設定された値を表示して います。Progress イベントと Completed イベントは UI スレッド外で実行されるため、 Dispatcher を使って UI スレッド上で処理しています。このプログラムを実行して、バックグ ラウンドタスクを実行すると以下のようになります。 Progress の状態が表示されます。 Progress が 100 を超えると、完了時刻が表示されます。
  • 275. 275 11.1.3.バ ッ クグ ラ ウ ン ド タス ク の 追加 情 報 バックグラウンドタスクのトリガーの種類によっては、バックグラウンドタスク内でタスクが 起動されたときの追加の情報を取得することが出来ます。詳細情報は、BackgroundTask の Run メソッドの引数に渡される IBackgroundTaskInstance の TriggerDetails から取得できま す。この TriggerDetails をトリガーの種類に応じて対応する型にキャストすることで詳細情報 にアクセスできます。この処理の詳細については後述する「12.1.4 トースト」で、トースト通 知をバックグラウンドで受け取る処理でコード例を示します。 11.1.4.シ ス テム イ ベ ン ト への 応 答 ここでは、SystemTrigger を紹介します。SystemTrigger は名前の通りシステムの様々なイベン トに応答してバックグラウンドタスクを起動できるトリガーになります。バックグラウンドタ スクを登録する処理でトリガーを設定するところで、SystemTrgger を設定することで使用可能 です。前述した SystemTriggerType とワンショットかどうかの指定をコンストラクタで指定し ます。SystemTriggerType では、ユーザーが在籍しているとき、ネットワークのステータスが
  • 276. 276 変わったときなどの指定が可能です。指定可能なすべてのオプションについては以下のドキュ メントを参照してください。 https://msdn.microsoft.com/ja- jp/library/windows/desktop/windows.applicationmodel.background.systemtriggertype 12. タイルとトースト タイルは、Windows 10 においてスタート画面を通じてユーザーに様々な追加情報を提供する ための有用な場として使用することが出来ます。Windows 10 のデスクトップのタブレットモ ードや Windows 10 Mobile などのようにスタート画面がデフォルト画面となる環境では、その 傾向が顕著です。タイルを有効に使うアプリケーションを作ることで、ユーザーに対して自分 のアプリケーションをスタート画面にピンどめしてもらうための 1 つの強い動機づけになりま す。 トーストは、ユーザーに対してアプリから気付いてほしいものをユーザーに伝えるための手段 として使えます。適度なトーストは、ユーザーがアプリに戻ってくるためのきっかけを提供す るための手段として使えます。(トーストを表示しすぎるとアンインストールされると思うの でほどほどに) 12.1.1.タ イ ルや ト ー ス ト を作 成 す るた め の 補 助 ツー ル UWP では、XML 形式でタイルや通知を定義します。この XML を作成するための補助ツール として UWP アプリの Notifications Visualizer がおすすめです。ストアで検索してみてくださ い。以下のように XML を定義しながらタイルや通知の状況を確認できます。
  • 277. 277 また、プログラムでこれらの XML を直接組み立てることも可能ですが、これを補助するライ ブラリもリリースされています。NotificationsExtensions.Win10 で NuGet から入手可能です。 このライブラリを使うことでタイプセーフに XML を組み立てることが出来ます。 タイルの XML のスキーマについては以下のドキュメントを参照してください。 https://msdn.microsoft.com/ja-jp/windows/uwp/controls-and-patterns/tiles-and-notifications- adaptive-tiles-schema
  • 278. 278 トーストの XML のスキーマについては以下のドキュメントを参照してください。 https://msdn.microsoft.com/ja-jp/windows/uwp/controls-and-patterns/tiles-and-notifications- adaptive-interactive-toasts 12.1.2.ラ イ ブタ イ ル ライブタイルは、アプリの起動のためのタイルに情報を表示する機能を追加するものです。タ イルを作成するには、TileUpdateManager クラスの GetTileUpdaterForApplication 静的メソッ ドで TileUpdater クラスのインスタンスを取得して、Update メソッドに TileNotification を渡 して呼び出します。TileNotification は引数にタイルの見た目を定義した XmlDocument を渡し ます。AddToSchedule メソッドに ScheduledTileNotification を渡すことで、時間指定でタイ ルを更新することが出来ます。ScheduledTileNotification クラスのコンストラクタにタイルの 見た目を定義した XmlDocument と表示時間の DateTimeOffset を渡します。Clear メソッドを 呼び出すと、ライブタイルに追加された更新やスケジュールなどがリセットされます。タイル による通知は 5 つまで有効です。 例として、以下のような XML を通知として表示する場合の手順を示します。 <tile> <visual> <binding template="TileSmall" hint-textStacking="center"> <text hint-align="center">Mon</text> <text hint-align="center" hint-style="body">22</text> </binding> <binding template="TileMedium" branding="name" displayName="Monday 22"> <text hint-wrap="true" hint-maxLines="2">Snowboarding with Mark</text> <text hint-style="captionSubtle">Fri: 9:00 AM</text> </binding> <binding template="TileWide" branding="nameAndLogo" displayName="Monday 22"> <text>Snowboarding with Mark</text> <text hint-style="captionSubtle">Mt. Baker</text>
  • 279. 279 <text hint-style="captionSubtle">Tomorrow: 9:00 AM – 5:00 PM</text> </binding> <binding template="TileLarge" branding="nameAndLogo" displayName="Monday 22"> <group> <subgroup> <text hint-wrap="true">Snowboarding with Mark</text> <text hint-style="captionSubtle">Mt. Baker</text> <text hint-style="captionSubtle">Tomorrow: 9:00 AM – 5:00 PM</text> </subgroup> </group> <text /> <group> <subgroup> <text hint-wrap="true">Casper Baby Pants</text> <text hint-style="captionSubtle">Hollywood Bowl</text> <text hint-style="captionSubtle">Tomorrow: 8:00 PM – 11:00 PM</text> </subgroup> </group> </binding> </visual> </tile> この XML は、Notifications Visualizer の Calendar.xml の内容になります。これを Notifications Extensions を使って組み立てます。コードを以下に示します。(コードが長くな るので TileSmall のみの組み立てを示していますが他の部分も同じ要領で組み立てることが出 来ます。 public void Click() { var tile = new TileContent {
  • 280. 280 Visual = new TileVisual { TileSmall = new TileBinding { Content = new TileBindingContentAdaptive { Children = { new AdaptiveText { HintAlign = AdaptiveTextAlign.Center, Text = "Mon", }, new AdaptiveText { HintAlign = AdaptiveTextAlign.Center, Text = "22", }, } } }, TileMedium = new TileBinding { // ... }, TileLarge = new TileBinding { // ... }, TileWide = new TileBinding { // ... } } };
  • 281. 281 var tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication(); // クリアして追加する tileUpdater.Clear(); tileUpdater.Update(new TileNotification(tile.GetXml())); } この Click メソッドが呼び出されるとタイルが以下のように表示されます。 12.1.3.セ カ ンダ リ タ イ ル UWP では 1 つのアプリに複数のタイルを表示することが出来ます。セカンダリタイルを使う と、アプリを特定画面から開いたり、アプリ起動時に特別な引数を渡すといったことが出来ま す。セカンダリタイルを作るには、SecondaryTile クラスを作成して、 RequestCreateForSelectionAsync を呼び出します。SecondaryTile クラスにはコンストラクタで 以下の引数を指定します。  tileId:タイルの識別 ID。削除時などに使用する。  displayName:タイルに表示されるアプリの長い名前  arguments:アプリ起動時に渡される引数  square150x150Logo:タイルの正方形のロゴ  desiredSize:タイルのサイズ コンストラクタで基本設定を指定したあと、VisualElements プロパティを使ってワイドロゴな どを指定できます。ボタンを押したときにセカンダリタイルを表示するには以下のようなコー ドになります。
  • 282. 282 public async void Click() { // セカンダリタイルクラスの作成 var tile = new SecondaryTile( "sampleTileId", "表示名", "argument", new Uri("ms-appx:///Assets/Square150x150Logo.png"), TileSize.Square150x150); // ワイドタイルの設定 tile.VisualElements.Wide310x150Logo = new Uri("ms-appx:///Assets/Wide310x150Logo.png"); // 名前の表示・非表示を設定 tile.VisualElements.ShowNameOnSquare150x150Logo = true; tile.VisualElements.ShowNameOnWide310x150Logo = true; // ローミングするか設定 tile.RoamingEnabled = false; // 前景色を設定 tile.VisualElements.ForegroundText = ForegroundText.Dark; // タイルの表示をユーザーに要求 await tile.RequestCreateAsync(); } このタイルを削除するコードは以下のようになります。存在確認をしてから削除コードを実行 している点がポイントです。 public async void UnpinClick() { // 存在を確認してから削除する if (SecondaryTile.Exists("sampleTileId")) { var tile = new SecondaryTile("sampleTileId"); await tile.RequestDeleteAsync();
  • 283. 283 } } 実行すると以下のようになります。ボタンを押すとタイルを表示するか尋ねられます。 「はい」を選択すると、タイルがスタート画面に作成されます。 タイルが存在する状態で Unpin のボタンを選択すると、タイルが削除されます。セカンダリタ イルから起動したときは、OnLaunched メソッドの LaunchActivatedEventArgs の Arguments プロパティに SecondaryTile 作成時に指定した引数がわたってきます。(今回のプログラム例 だと argument)この値を使って画面の遷移先や、ページの表示データを変えることでセカンダ リタイルをショートカット的に使うことが出来ます。
  • 284. 284 12.1.4.ト ー スト トーストは、タイルと同様に XML によって定義されます。visual でテキストや画像などを定義 して、actions でボタンや入力を定義して、audio で通知が表示されるときに再生される音楽を 指定できます。 トースト通知は、ToastNotificationManager クラスの CreateToastNotifier 静的メソッドで ToastNotifier クラスを作成して、その Show メソッドに ToastNotification を渡すことで表示す ることが出来ます。ToastNotification クラスは、コンストラクタにタイルと同様に XmlDocument を渡します。トーストの XML も Notifications Visualizer で確認し Notifications Extensions で組み立てることが出来ます。例として、以下のようなシンプルなトースト通知の XML を Notifications Extensions で組み立てます。 <toast> <visual> <binding template="ToastGeneric"> <text>Hello World</text> <text>This is a simple toast message</text> </binding> </visual> </toast> 以下のようなコードになります。 using NotificationsExtensions; using NotificationsExtensions.Toasts; using Windows.UI.Notifications; using Windows.UI.Xaml.Controls; namespace ToastApp { public sealed partial class MainPage : Page {
  • 285. 285 public MainPage() { this.InitializeComponent(); } public void Click() { var toast = new ToastContent { Visual = new ToastVisual { BindingGeneric = new ToastBindingGeneric { Children = { new AdaptiveText { Text = "Hello world" }, new AdaptiveText { Text = "This is a simple toast message" }, } } } }; var toastNotifier = ToastNotificationManager.CreateToastNotifier(); toastNotifier.Show(new ToastNotification(toast.GetXml())); } } } 実行して Click メソッドを呼び出すと以下のようにトーストが表示されます。
  • 286. 286 トーストから入力を受け取ってユーザーがボタンを押したときの操作に応答する例を以下に示 します。トーストを通知するコードは以下のようになります。 using NotificationsExtensions; using NotificationsExtensions.Toasts; using Windows.UI.Notifications; using Windows.UI.Xaml.Controls; namespace ToastApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } public void Click() { var toast = new ToastContent { ActivationType = ToastActivationType.Foreground, Visual = new ToastVisual {
  • 287. 287 BindingGeneric = new ToastBindingGeneric { Children = { new AdaptiveText { Text = "Hello world" }, } } }, Actions = new ToastActionsCustom { Inputs = { new ToastTextBox("textBox") { PlaceholderContent = "Please input message." } }, Buttons = { new ToastButton("Send", "toast"), }, } }; var toastNotifier = ToastNotificationManager.CreateToastNotifier(); toastNotifier.Show(new ToastNotification(toast.GetXml())); } } } このコードで表示されるトーストは以下のようになります。
  • 288. 288 このとき Send ボタンを押すと App クラスの OnActivated メソッドが呼び出されます。トース トから呼び出されたときは ToastNotificationActivatedEventArgs になっているため、型をチェ ックして Argument を確認して UserInput から textBox への入力を取得してメッセージボック スで表示しています。 protected override async void OnActivated(IActivatedEventArgs args) { base.OnActivated(args); var toast = args as ToastNotificationActivatedEventArgs; if (toast != null) { if (toast.Argument == "toast") { var message = toast.UserInput["textBox"] as string; await new MessageDialog($"{message} が入力されました").ShowAsync(); } } } 実行してトーストにテキストを入れて Send ボタンを押すと以下のようにアプリケーションで メッセージボックスが表示されます。
  • 289. 289 バックグラウンドタスクでも、トーストでボタンが押されたときの処理を行うことが出来ま す。Windows ランタイムコンポーネントとしてバックグラウンドタスクを作成します。 using NotificationsExtensions; using NotificationsExtensions.Tiles; using Windows.ApplicationModel.Background; using Windows.UI.Notifications; namespace ToastSampleApp.Background { public sealed class ToastBackgroundTask : IBackgroundTask { public void Run(IBackgroundTaskInstance taskInstance) { var toast = taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail; if (toast.Argument == "toast") { // 受け取ったメッセージでタイルをアップデート var message = toast.UserInput["textBox"] as string; var tileContent = new TileContent {
  • 290. 290 Visual = new TileVisual { TileMedium = new TileBinding { Content = new TileBindingContentAdaptive { Children = { new AdaptiveText { Text = $"{message} が入力されました", HintWrap = true, } } } } } }; TileUpdateManager.CreateTileUpdaterForApplication().Update( new TileNotification(tileContent.GetXml())); } } } } ToastNotificationActionTriggerDetail が渡されてきているので、そこから Argument と UsreInput が取得できます。あとは、OnActivated のときと同じように処理が出来ます。ここ ではタイルをトーストで受け取った内容をもとに更新しています。後は、OnLaunched の最初 でバックグラウンドタスクを登録します。 foreach (var task in BackgroundTaskRegistration.AllTasks) { task.Value.Unregister(true); } var state = await BackgroundExecutionManager.RequestAccessAsync();
  • 291. 291 if (state != BackgroundAccessStatus.Denied) { var b = new BackgroundTaskBuilder(); b.Name = "ToastBackgroundTask"; b.TaskEntryPoint = "ToastSampleApp.Background.ToastBackgroundTask"; b.SetTrigger(new ToastNotificationActionTrigger()); b.Register(); } 最後に、Package.appxmanifest でバックグラウンドタスクを宣言しておきます。ポイントはシ ステムイベントとして登録することです。
  • 292. 292 トースト通知のほうは、ToastButton に対して ActivationType に ToastActivationType.Background を設定することで、ボタンが押されたときにバックグラウン ドタスクが実行されるようになります。 public void Click() { var toast = new ToastContent { Visual = new ToastVisual { BindingGeneric = new ToastBindingGeneric { Children = { new AdaptiveText { Text = "Hello world" }, } } }, Actions = new ToastActionsCustom { Inputs = { new ToastTextBox("textBox") { PlaceholderContent = "Please input message." } }, Buttons = { new ToastButton("Send", "toast") { ActivationType = ToastActivationType.Background, }, }, }
  • 293. 293 }; var toastNotifier = ToastNotificationManager.CreateToastNotifier(); toastNotifier.Show(new ToastNotification(toast.GetXml())); } 実行して Click メソッドを呼び出すと以下のようにトースト通知が表示されます。 テキストを入力して Send ボタンを選択すると、以下のようにタイルが更新されることが確認 できます。 13. プッシュ通知
  • 294. 294 UWP アプリでは、Windows プッシュ通知サービス(WNS)を使用することで簡単にプッシュ 通知を実装することが出来ます。ストアに紐づけ(公開する必要はありません)られたアプリ に対して発行される URI を使用して、任意のサービスから WNS を経由して UWP アプリにプ ッシュ通知を発行することが出来ます。 PushNotificationApp という名前のアプリを作ったものとしてプッシュ通知をアプリに組み込 む方法を説明します。まず、プロジェクトの右クリックメニューから「ストア」→「アプリケ ーションをストアと関連付ける」を選択します。(Windows デベロッパーセンターで開発者 登録をしてない人は、後述する URL から登録しておきましょう。 https://developer.microsoft.com/ja-jp/windows) 「次へ」を選択します。
  • 297. 297 次に Windows デベロッパーセンターにアクセスします。以下の URL へアクセスしてくださ い。 https://developer.microsoft.com/ja-jp/windows Microsoft アカウントでサインインしたら、画面右上にある「ダッシュボード」を選択します。 画面左のリストに先ほど予約した名前のアプリが追加されているので選択します。
  • 300. 300 画面中ほどにある「Live Services site」を選択します。 Microsoft アカウントでサインインするとアプリケーションシークレットと、パッケージ SID とアプリケーション ID が取得できます。
  • 301. 301 アプリケーション ID を Package.appxmanifest に記述します。Package.appxmanifest を右クリ ックして、「ファイルを開くアプリケーションの選択」を選んで XML エディターで開いてく ださい。Package タグ直下に、アプリケーション ID に記載してある Identity タグと同じ物があ ることを確認します。 次にプッシュ通知を送信するサーバーアプリケーションを作成します。通常はクラウド上など に構築された Web アプリケーションなどのサーバーアプリケーションになりますが今回は簡 単にするためにコンソールアプリケーションとして作成します。 「PushNotificationApp.Server」という名前でコンソールアプリケーションを作成します。認証 のコードを以下に示します。 using System; using System.Collections.Generic;
  • 302. 302 using System.Net.Http; namespace PushNotificationApp.Server { class Program { static void Main(string[] args) { // 認証処理 var client = new HttpClient(); var content = new FormUrlEncodedContent( new[] { new KeyValuePair<string, string>("grant_type", "client_credentials"), new KeyValuePair<string, string>("client_id", "ms-app://s-1-15-2- 3517753967-4196436483-1856480010-4052086479-2225032242-2789219409-18995796"), new KeyValuePair<string, string>("client_secret", "Mgg1XLMrsVjvxmaWcE/IJNxeTApuIHEq"), new KeyValuePair<string, string>("scope", "notify.windows.com") }); var res = client.PostAsync("https://login.live.com/accesstoken.srf", content).Result; // 結果のダンプ Console.WriteLine(res.Content.ReadAsStringAsync().Result); } } } client_id と client_secret が先ほどのサイトで取得したパッケージ SID とアプリケーションシー クレットになります。結果は以下のようになります。 {"token_type":"bearer","access_token":"EgCdAQMAAAAEgAAADAAB1wy2M2asJ6I+qaCv7B/t7NzHg 63qvGsr4wBBFNcDGl4XI1tW+XzRPPy1q58m0IilekLr1f6IBuNgYHcjSKRjrsj3uXty691mqe5aZlya6mlbCm RtoXECsxNJs66fZwN2aBv1p76u6dXI1TCR72ZiA8X+AZ6nb92khg2u35nlsrYjs3lQqFmC1HlJF229iZO0S QGCmaQc+i1D6j6q99B/jPuYI6k7n0PE9lmWl5Atvdfds6snIGWmCd9gjkoa2kwht4ZfbVVKl0W7SQ4H1k5y nzupQY3s423Iyy4dL9Kc8j1E5486d9g2Wq94iE8J57gUJmas8AdOqTa0aGWOSvjlxIwAWgCMAAAAAAAO
  • 303. 303 ERpA25B0V9uQdFfrSAQADwAxMjYuMjM3LjExNC4xMQAAAAAAXQBtcy1hcHA6Ly9zLTEtMTUtMi0zN TE3NzUzOTY3LTQxOTY0MzY0ODMtMTg1NjQ4MDAxMC00MDUyMDg2NDc5LTIyMjUwMzIyNDItMjc4 OTIxOTQwOS0xODk5NTc5NgA=","expires_in":86400} ここで取得した access_token がプッシュ通知のサーバーにアクセスするために必要なキーにな ります。JSON.NET などでパースして取得します。ここではコピペで保管します。expires_in で有効期限の時間(秒)が指定されています。上記レスポンスでは 24 時間と指定されていま す。有効期限が切れるまでは、このアクセストークンを利用し、有効期限が切れた場合には再 度アクセストークンを取得します。 次に、UWP アプリでプッシュ通知を受け取るための URL を生成します。 PushNotificationApp の MainPage.xaml を以下のように編集します。URL を表示するための TextBox と URL を生成するための Button を置いています。 <Page x:Class="PushNotificationApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PushNotificationApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="TextBox" /> <Button Content="Request" Click="{x:Bind Click}" /> </StackPanel> </Page> コードビハインドで URL を作成します。PushNotificationChannelManager クラスの CreatePushNotificationChannelForApplicationAsync 静的メソッドで PushNotificationChannel クラスのインスタンスが取得できます。このクラスの Uri プロパティでプッシュ通知送信先の URL が取得できます。コードを以下に示します。 using System;
  • 304. 304 using Windows.Networking.PushNotifications; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace PushNotificationApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } public async void Click() { try { var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync(); this.TextBox.Text = channel.Uri.ToString(); await new MessageDialog($"{channel.Uri}, 期限: {channel.ExpirationTime}").ShowAsync(); } catch(Exception ex) { await new MessageDialog(ex.Message).ShowAsync(); } } } } 通常は、この URL をユーザーID などと紐づけてサーバーに送信してサーバー側で管理を行い ます。CreatePushNotificationChannelForApplicationAsync メソッドの結果の URL が変わらな い場合は、サーバーに再送信する必要はありません。変わっている場合は再送信する必要があ
  • 305. 305 ります。上記プログラムを実行して Request ボタンを選択すると以下のようにメッセージボッ クスが表示されます。 この URL をメモしておきます。 プッシュ通知を送信するには、WNS のプロトコルに沿ったリクエストを送信します。ヘッダ ーに指定する HTTP ヘッダー値の詳細は以下を参照してください。 https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/hh868245 今回は例としてトースト通知をしてみたいと思います。以下のコードを PushNotificationApp.Server プロジェクトの Program.cs に記述します。 using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; namespace PushNotificationApp.Server { class Program {
  • 306. 306 static void Main(string[] args) { // 取得したアクセストークン var token = "EgCdAQMAAAAEgAAADAAB1wy2M2asJ6I+qaCv7B/t7NzHg63qvGsr4wBBFNcDGl4XI1tW+XzRPPy1 q58m0IilekLr1f6IBuNgYHcjSKRjrsj3uXty691mqe5aZlya6mlbCmRtoXECsxNJs66fZwN2aBv1p76u6dXI1T CR72ZiA8X+AZ6nb92khg2u35nlsrYjs3lQqFmC1HlJF229iZO0SQGCmaQc+i1D6j6q99B/jPuYI6k7n0PE9l mWl5Atvdfds6snIGWmCd9gjkoa2kwht4ZfbVVKl0W7SQ4H1k5ynzupQY3s423Iyy4dL9Kc8j1E5486d9g2W q94iE8J57gUJmas8AdOqTa0aGWOSvjlxIwAWgCMAAAAAAAOERpA25B0V9uQdFfrSAQADwAxMjYuMj M3LjExNC4xMQAAAAAAXQBtcy1hcHA6Ly9zLTEtMTUtMi0zNTE3NzUzOTY3LTQxOTY0MzY0ODMtM Tg1NjQ4MDAxMC00MDUyMDg2NDc5LTIyMjUwMzIyNDItMjc4OTIxOTQwOS0xODk5NTc5NgA="; var client = new HttpClient(); // トーストの内容を表す XML を送信する var content = new StringContent(@" <toast> <visual> <binding template=""ToastGeneric""> <text>サンプルトースト通知</text> </binding> </visual> </toast>"); // 認証ヘッダーを指定する client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // wns/toast でトースト通知であることを示す content.Headers.Add("X-WNS-Type", "wns/toast"); content.Headers.ContentType = new MediaTypeHeaderValue("text/xml"); // UWP アプリで生成した URL に対して POST を投げる var response = client.PostAsync("https://hk2.notify.windows.com/?token=AwYAAAAqAPItmTeuRwSuosiz%2bP%2bnv 0lC4RyOwD5%2b9r9lyv73doSaehsleQNnUdnEpFyagREYOjxiFiBA4qEh%2beoes2uTe%2fDq3FwLk3%2bN Wkpz4Px8PqYXwMQA%2bcYNTLZNLE9IlBE%3d", content).Result; Console.WriteLine(response.Content.ReadAsStringAsync().Result); } }
  • 307. 307 } このプログラムを実行すると、プッシュ通知サーバーを経由してトーストが表示されます。 X-WNS-Type ヘッダーを wns/tile にしてタイルの XML を送信することで、プッシュ通知でタ イルの更新が可能です。 X-WNS-Type を wns/raw にして ContentType を application/octet-stream にすることで 5KB までの任意のデータをプッシュ通知することが出来ます。送信側のコード例を以下に示しま す。 using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; namespace PushNotificationApp.Server { class Program { static void Main(string[] args) { // 取得したアクセストークン var token = "EgCdAQMAAAAEgAAADAAB1wy2M2asJ6I+qaCv7B/t7NzHg63qvGsr4wBBFNcDGl4XI1tW+XzRPPy1 q58m0IilekLr1f6IBuNgYHcjSKRjrsj3uXty691mqe5aZlya6mlbCmRtoXECsxNJs66fZwN2aBv1p76u6dXI1T CR72ZiA8X+AZ6nb92khg2u35nlsrYjs3lQqFmC1HlJF229iZO0SQGCmaQc+i1D6j6q99B/jPuYI6k7n0PE9l
  • 308. 308 mWl5Atvdfds6snIGWmCd9gjkoa2kwht4ZfbVVKl0W7SQ4H1k5ynzupQY3s423Iyy4dL9Kc8j1E5486d9g2W q94iE8J57gUJmas8AdOqTa0aGWOSvjlxIwAWgCMAAAAAAAOERpA25B0V9uQdFfrSAQADwAxMjYuMj M3LjExNC4xMQAAAAAAXQBtcy1hcHA6Ly9zLTEtMTUtMi0zNTE3NzUzOTY3LTQxOTY0MzY0ODMtM Tg1NjQ4MDAxMC00MDUyMDg2NDc5LTIyMjUwMzIyNDItMjc4OTIxOTQwOS0xODk5NTc5NgA="; var client = new HttpClient(); // 通知データを設定する var content = new StringContent($"Send custom push notification {DateTimeOffset.Now.ToString()}"); // 認証ヘッダーを指定する client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); // wns/raw で直接配信であることを示す content.Headers.Add("X-WNS-Type", "wns/raw"); content.Headers.ContentType = new MediaTypeHeaderValue("application/octet- stream"); // UWP アプリで生成した URL に対して POST を投げる var response = client.PostAsync("https://hk2.notify.windows.com/?token=AwYAAAAqAPItmTeuRwSuosiz%2bP%2bnv 0lC4RyOwD5%2b9r9lyv73doSaehsleQNnUdnEpFyagREYOjxiFiBA4qEh%2beoes2uTe%2fDq3FwLk3%2bN Wkpz4Px8PqYXwMQA%2bcYNTLZNLE9IlBE%3d", content).Result; Console.WriteLine(response.StatusCode); } } } 受信側は、PushNotificationChannel クラスの PushNotificationReceived イベントを購読するこ とで、フォアグラウンドで受信できます。コード例を以下に示します。 using System; using Windows.Networking.PushNotifications; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; namespace PushNotificationApp {
  • 309. 309 public sealed partial class MainPage : Page { private PushNotificationChannel Channel { get; set; } public MainPage() { this.InitializeComponent(); } public async void Click() { try { this.Channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync(); this.Channel.PushNotificationReceived += this.Channel_PushNotificationReceived; this.TextBox.Text = this.Channel.Uri.ToString(); await new MessageDialog($"{this.Channel.Uri}, 期限: {this.Channel.ExpirationTime}").ShowAsync(); } catch(Exception ex) { await new MessageDialog(ex.Message).ShowAsync(); } } private async void Channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args) { await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await new MessageDialog(args.RawNotification.Content).ShowAsync(); }); }
  • 310. 310 } } UWP アプリ側を実行してボタンを押してイベントハンドラを登録したあとにコンソールアプ リケーションを実行すると以下のようにプッシュ通知をフォアグラウンドで受け取りメッセー ジボックスが表示されます。 wns/raw の通知はバックグラウンドタスクで受信することも出来ます。以下のように Windows ランタイムコンポーネントのプロジェクトにバックグラウンドタスクを作成します。 using Windows.ApplicationModel.Background; using Windows.Networking.PushNotifications; using Windows.Storage; namespace PushNotificationApp.Tasks { public sealed class PushNotificationTask : IBackgroundTask { public void Run(IBackgroundTaskInstance taskInstance) { var notification = taskInstance.TriggerDetails as RawNotification;
  • 311. 311 ApplicationData.Current.LocalSettings.Values["Notification"] = $"{notification.Content} を受信しました"; } } } TriggerDetails は RawNotification が格納されています。 このバックグラウンドタスクを Package.appxmanifest に登録します。 そして、App クラスの OnLaunched メソッドでバックグラウンドタスクとして登録します。ト リガーは PushNotificationTrigger クラスです。コードを以下に示します。
  • 312. 312 var status = await BackgroundExecutionManager.RequestAccessAsync(); if (status != BackgroundAccessStatus.Denied) { if (!BackgroundTaskRegistration.AllTasks.Values.Any(x => x.Name == "PushTask")) { var b = new BackgroundTaskBuilder { Name = "PushTask", TaskEntryPoint = "PushNotificationApp.Tasks.PushNotificationTask" }; b.SetTrigger(new PushNotificationTrigger()); b.Register(); } } 最後にバックグラウンドタスクの処理が完了したときの処理に応答する処理を MainPage に書 きます。OnNavigatedTo で Completed イベントを購読してメッセージボックスを表示してい ます。 protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); var r = BackgroundTaskRegistration.AllTasks.Values.First(x => x.Name == "PushTask"); r.Completed += async (_, __) => { await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await new MessageDialog((string)ApplicationData.Current.LocalSettings.Values["Notification"]).ShowAsync(); }); }; } UWP アプリとコンソールアプリケーションを実行すると以下のようにバックグラウンドタス ク経由のメッセージボックスが表示されます。
  • 313. 313 14. コルタナ連携 UWP アプリでは、Windows 10 の音声によるパーソナルアシスタントであるコルタナとアプリ を連携させることが出来ます。ここでは、コルタナを使ってできることを紹介したいと思いま す。 14.1.1.フ ォ アグ ラ ウ ン ド アプ リ の 起動 コルタナを使用してアプリを起動することが出来ます。コルタナと連携するアプリを作成する 流れは以下のようになります。  VCD と呼ばれる XML 形式のファイルを作成します。このファイルにコルタナで、どのキ ーワードに対して応答するかといったことが定義可能です。VCD ファイルのスキーマに ついては以下を参照してください。 https://msdn.microsoft.com/library/windows/apps/dn706593  アプリ起動時に上記 VCD ファイルをシステムに登録します。  App クラスの OnActivated メソッドでコルタナからのアクティブ化を処理します。
  • 314. 314 まず、UWP アプリを作成(ここでは CortanaApp という名前で作成した前提で説明します) します。そして、XML ファイルをプロジェクトに追加します。ファイル名は VoiceCommands.xml にしました。このファイル名は任意でかまいません。これが VCD ファイ ルになります。VCD ファイルを以下のように記述します。 <?xml version="1.0" encoding="utf-8" ?> <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2"> </VoiceCommands> これ以降は Visual Studio がスキーマを読み込んで、ある程度インテリセンスが効くようになり ます。VoiceCommands タグの下には CommandSet タグを定義します。CommandSet には xml:lang で言語を、Name で名前を定義します。CommandSet タグの下には AppName と Example と Command などを定義します。AppName にはアプリの名前を Example には例を Command は音声認識のコマンドの詳細を定義していきます。Command の中には Example で 例を、ListenFor で認識する音声を、Feedback で音声認識の結果の応答と、そして Navigate で 画面遷移を行うという定義を行います。ListenFor の中では{}でくくることで任意のワードを定 義することが出来ます。ここで定義するワードは PhraseTopic か PhraseList で Command の下 に定義します。例として、以下のように VoiceCommands.xml を書き換えます。 <?xml version="1.0" encoding="utf-8" ?> <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2"> <!-- --> <CommandSet xml:lang="ja-jp" Name="LaunchAppSample_ja-jp"> <AppName>サンプル</AppName> <Example>サンプル起動</Example> <Command Name="LaunchAppCommand"> <Example>サンプル起動 ハローワールド</Example> <ListenFor RequireAppName="BeforePhrase">起動 {keyword}</ListenFor> <Feedback>{keyword}で、アプリを起動しています。</Feedback> <Navigate /> </Command> <Command Name="SearchAppCommand"> <Example>サンプル検索 ハローワールド</Example>
  • 315. 315 <ListenFor RequireAppName="BeforePhrase">検索 {keyword}</ListenFor> <Feedback>{keyword}で、検索しています。</Feedback> <Navigate /> </Command> <PhraseTopic Label="keyword"> </PhraseTopic> </CommandSet> </VoiceCommands> ListenFor で RequireAppName=”BeforePhrase”を定義すると、アプリ名を音声コマンドの前に 入力するという意味になります。上記 VoiceCommands.xml では、「サンプル起動 任意の言 葉」と「サンプル検索 任意の言葉」という 2 つの単語に反応するようにしています。任意の 言葉は keyword という単語で PhraseTopic として定義しています。 VoiceCommands ファイルが出来たら、このファイルをシステムに登録します。登録にはアプ リ起動時などの任意のタイミングで以下のコードを実行します。今回は起動時に読み込ませた いと思うので OnLaunched メソッドの先頭に追加しました。 await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync( await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///VoiceCommands.xml"))); コルタナからアプリが起動されると App クラスの OnActivated メソッドが呼び出されます。イ ベント引数には VoiceCommandActivatedEventArgs がわたってきます。 VoiceCommandActivatedEventArgs の Result の Text で認識した文字列が確認できます。 Result の RulePath[0]で認識した Command の Name が取得できます。Result の SemanticInterpretion の Properties[“keyword”]で{keyword}として認識した文字列のリストを取 得できます。例えば以下のようなコードで、認識したコマンドの名前と keyword の中身を CortanaPage というページに渡すことが出来ます。 protected override void OnActivated(IActivatedEventArgs args) { base.OnActivated(args); if (args is VoiceCommandActivatedEventArgs) {
  • 316. 316 var e = (VoiceCommandActivatedEventArgs)args; var frame = Window.Current.Content as Frame; if (frame == null) { frame = new Frame(); Window.Current.Content = frame; } frame.Navigate(typeof(CortanaPage), $"{e.Result.RulePath[0]} {e.Result.SemanticInterpretation.Properties["keyword"].First()}"); } Window.Current.Activate(); } CortanaPage は以下のような XAML と <Page x:Class="CortanaApp.CortanaPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CortanaApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock x:Name="TextBlock" Style="{StaticResource HeaderTextBlockStyle}" /> </Grid> </Page> 以下のようなコードビハインドを持つシンプルなページです。 using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace CortanaApp
  • 317. 317 { public sealed partial class CortanaPage : Page { public CortanaPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); this.TextBlock.Text = (string)e.Parameter; } } } 一度アプリを実行して、VoiceCommands.xml のインストール処理を走らせます。そして、 「サンプル起動こんにちは」と話しかけると以下のようにコルタナがアプリを認識してアプリ が起動されます。 起動したアプリでは、どのコマンドで、どんなテキストが認識されたのか確認できます。
  • 318. 318 今回は PhraseTopic を使って任意の単語を入力として受け付けましたが、この単語をリストか らの選択式にすることも出来ます。そのときは PhraseList を使います。例として「こんにち は」「こんばんは」「さようなら」の 3 つの単語に応答するようにした VoiceCommands.xml を以下に示します。 <?xml version="1.0" encoding="utf-8" ?> <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2"> <!-- --> <CommandSet xml:lang="ja-jp" Name="LaunchAppSample_ja-jp"> <AppName>サンプル</AppName> <Example>サンプル起動</Example> <Command Name="LaunchAppCommand"> <Example>サンプル起動 ハローワールド</Example> <ListenFor RequireAppName="BeforePhrase">起動 {keyword}</ListenFor> <Feedback>{keyword}で、アプリを起動しています。</Feedback> <Navigate /> </Command>
  • 319. 319 <Command Name="SearchAppCommand"> <Example>サンプル検索 ハローワールド</Example> <ListenFor RequireAppName="BeforePhrase">検索 {keyword}</ListenFor> <Feedback>{keyword}で、検索しています。</Feedback> <Navigate /> </Command> <PhraseList Label="keyword"> <Item>こんにちは</Item> <Item>こんばんは</Item> <Item>さようなら</Item> </PhraseList> </CommandSet> </VoiceCommands> これでアプリを再度実行して VoiceCommands.xml をインストールさせてコルタナさんに「サ ンプル検索さようなら」と話しかけると以下のようにアプリが起動します。
  • 320. 320 この PhraseList はアプリから更新することも出来ます。例として MainPage.xaml で入力したテ キストに PhraseList の内容を置き換える例を示します。まず、MainPage.xaml を以下のように して、複数行受け付ける TextBox と AppBarButton を置きます。 <Page x:Class="CortanaApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CortanaApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="Accept" Label="Regist" Click="{x:Bind Click}" /> </CommandBar> </Page.BottomAppBar> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="TextBox" AcceptsReturn="True" /> </Grid> </Page> そして、Click メソッドでリストの内容を更新します。更新方法は VoiceCommandDefinitionManager の InstalledCommandDefinitions の TryGetValue メソッド で CommandSet に定義した Name 属性の値で VoiceCommandDefinition が取得できます。 VoiceCommandDefinition の SetPhraseListAsync メソッドで PhraseList の名前と、リストの中 身を渡すことでリストの内容を更新できます。コードを以下に示します。 using System; using System.Linq; using Windows.ApplicationModel.VoiceCommands; using Windows.UI.Xaml.Controls;
  • 321. 321 namespace CortanaApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } public async void Click() { if (string.IsNullOrWhiteSpace(this.TextBox.Text)) { return; } var items = this.TextBox.Text.Split('n').Select(x => x.Trim()); VoiceCommandDefinition def; if (VoiceCommandDefinitionManager.InstalledCommandDefinitions .TryGetValue("LaunchAppSample_ja-jp", out def)) { await def.SetPhraseListAsync("keyword", items); } } } } アプリを実行して、以下のように入力して AppBarButton を選択します。
  • 323. 323 14.1.2.バ ッ クグ ラ ウ ン ド アプ リ の 起動 コルタナを使うと、バックグラウンドタスクを使って処理をすることも出来ます。コルタナが 表示されたときのコルタナのマークの下の部分の余白(コルタナキャンバス)を使って一覧情 報などを出したりといったことが可能です。 コルタナに対応したバックグラウンドタスクを作成するために Windows ランタイムコンポー ネントのプロジェクトを作成します。(ここでは CortanaApp.Tasks という名前で作成した前 提で話を進めます)CortanaApp プロジェクトに忘れないうちに CortanaApp.Tasks プロジェク トへの参照を追加しておきます。 コルタナキャンバスにはアイコンを表示することが可能なため、そのアイコンのリソースも準 備しておきます。幅 x 高さが 68x68 と、68x92 と 280x140 の 3 種類が使用できます。ここで は、logo68x68.png のような名前で Assets フォルダに追加しました。 バックグラウンドタスクのクラスを VoiceCommandTask という名前で作成します。そして、 Package.appxmanifest に登録をします。コルタナに関する設定は GUI が提供されていないた
  • 324. 324 め、Package.appxmanifest を XML エディターで開いて Extensions 属性の箇所を以下のように 編集します。 <Extensions> <uap:Extension Category="windows.appService" EntryPoint="CortanaApp.Tasks.VoiceCommandTask"> <uap:AppService Name="VoiceCommandTask"/> </uap:Extension> <uap:Extension Category="windows.personalAssistantLaunch" /> </Extensions> EntryPoint が、先ほど作成したバックグラウンドタスクのクラス名である点と Category の名 前を間違えなければ問題ないと思います。次に、VoiceCommand 定義ファイルを作成します。 フォアグラウンドと異なる点は、Navigate タグが VoiceCommandService タグに変わったとこ ろです。VoiceCommandService タグの Target で先ほど Package.appxmanifest で定義した AppService の Name を指定します。 <?xml version="1.0" encoding="utf-8" ?> <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2"> <CommandSet xml:lang="ja-jp" Name="LaunchAppSample_ja-jp"> <AppName>サンプル</AppName> <Example>サンプル起動</Example> <Command Name="BackgroundSample"> <Example>アイコンを見せて</Example> <ListenFor RequireAppName="BeforePhrase">アイコンを見せて {size}</ListenFor> <Feedback>アイコンを表示します</Feedback> <VoiceCommandService Target="VoiceCommandTask"/> </Command> <PhraseList Label="size"> <Item>小さい</Item> <Item>中ぐらい</Item> <Item>大きい</Item> </PhraseList> </CommandSet> </VoiceCommands>
  • 325. 325 フォアグラウンドと同じように、アプリ起動時などに、このファイルをシステムにインストー ルします。 await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync( await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///VoiceCommands.xml"))); そして、バックグラウンドタスクに処理を書きます。コードを以下に示します。 using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Windows.ApplicationModel.AppService; using Windows.ApplicationModel.Background; using Windows.ApplicationModel.VoiceCommands; using Windows.Storage; namespace CortanaApp.Tasks { public sealed class VoiceCommandTask : IBackgroundTask { private BackgroundTaskDeferral Deferral { get; set; } private VoiceCommandServiceConnection Connection { get; set; } public async void Run(IBackgroundTaskInstance taskInstance) { var details = (AppServiceTriggerDetails)taskInstance.TriggerDetails; if (details.Name != "VoiceCommandTask") { return; } this.Deferral = taskInstance.GetDeferral(); taskInstance.Canceled += TaskInstance_Canceled; this.Connection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(details);
  • 326. 326 this.Connection.VoiceCommandCompleted += Connection_VoiceCommandCompleted; var voiceCommand = await this.Connection.GetVoiceCommandAsync(); // Command の名前に応じて処理を分岐 switch (voiceCommand.CommandName) { case "BackgroundSample": await this.ProcessBackgroundSampleAsync(voiceCommand.Properties["size"].First()); break; default: var userMessage = new VoiceCommandUserMessage { DisplayMessage = "認識失敗", SpokenMessage = "認識に失敗しました", }; await this.Connection.ReportFailureAsync(VoiceCommandResponse.CreateResponse(userMessage)); break; } this.Deferral.Complete(); } private async Task ProcessBackgroundSampleAsync(string size) { var userMessage = new VoiceCommandUserMessage(); // 表示テキスト userMessage.DisplayMessage = "アイコン表示を認識しました"; // コルタナさんがしゃべるテキスト userMessage.SpokenMessage = "アイコンを表示します"; // サイズに応じてタイルを変える var tiles = new Dictionary<string, VoiceCommandContentTile> { {
  • 327. 327 "小さい", // 68x68 new VoiceCommandContentTile { // タイルのサイズ ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText, // タイトルとテキスト Title = "68x68 です", TextLine1 = "一行目のデータです", // アイコン画像 Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/logo68x68.png")), // タイルをクリックしたときにアプリに渡す引数 AppLaunchArgument = "68x68", } }, { "中ぐらい", // 68x92 new VoiceCommandContentTile { ContentTileType = VoiceCommandContentTileType.TitleWith68x92IconAndText, Title = "68x92 です", TextLine1 = "一行目のデータです", Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/logo68x92.png")), AppLaunchArgument = "68x92", } }, { "大きい", // 280x140 new VoiceCommandContentTile
  • 328. 328 { ContentTileType = VoiceCommandContentTileType.TitleWith280x140IconAndText, Title = "280x140 です", TextLine1 = "一行目のデータです", TextLine2 = "二行目のデータです", TextLine3 = "三行目のデータです", Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms- appx:///Assets/logo280x140.png")), AppLaunchArgument = "280x140", } }, }; // userMessage とタイルのリストからレスポンスを生成する var response = VoiceCommandResponse.CreateResponse( userMessage, // ここではタイルは 1 つだけだけど複数表示可能 new[] { tiles[size] }); // 結果を通知する await this.Connection.ReportSuccessAsync(response); } private void Connection_VoiceCommandCompleted(VoiceCommandServiceConnection sender, VoiceCommandCompletedEventArgs args) { this.Deferral?.Complete(); } private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { this.Deferral?.Complete(); } } }
  • 329. 329 長いですがやっていることはシンプルです。taskInstance から GetDeferral を取得して Run メ ソッドが終了してもタスクは実行中であることを示すようにします。次に、 VoiceCommandServiceConnection を TriggerDetails から作成しています。これがコルタナと の接続をつかさどるクラスになります。そこから VoiceCommand を取得して、 CommandName プロパティを参照することで認識した Command の名前がわかります。 あとは、VoiceCommandUserMessage と VoiceCommandContentTile のリストを作成して VoiceCommandResponse を作成して VoiceCommandServiceConnection の ReportSuccessAsync で通知するだけです。VoiceCommandContentTile で指定した AppLaunchArgument がアプリ起動時の引数に渡されます。これに応答するには、App クラス の OnActivated メソッドに以下のように記述します。 protected override void OnActivated(IActivatedEventArgs args) { base.OnActivated(args); if (args.Kind == ActivationKind.Protocol) { var e = (ProtocolActivatedEventArgs)args; var frame = Window.Current.Content as Frame; if (frame == null) { frame = new Frame(); Window.Current.Content = frame; } var decoder = new WwwFormUrlDecoder(e.Uri.Query); frame.Navigate(typeof(CortanaPage), $"{decoder.GetFirstValueByName("LaunchContext")}"); } Window.Current.Activate(); } Uri の LaunchContext というパラメータで飛んでくるので WwwFormUrlDecoder というパラ メータを解析するクラスを利用して、値を取り出しています。
  • 330. 330 アプリケーションを 1 度実行して VoiceCommand 定義ファイルを登録します。登録したらコ ルタナさんに「サンプル 大きいアイコンを見せて」などのように話しかけると、以下のように アイコンがコルタナに表示されます。 アイコンをタップすると、アイコンのサイズが画面に表示されます。
  • 331. 331 15. アプリ間連携 ここでは、UWP のアプリ間連携について説明します。 15.1. AppService 最初に、別アプリのバックグラウンドタスクと通信できる AppService について説明します。 AppServiceSample.Server という名前で、プロジェクトを作成します。そして、 AppServiceSample.Server.Tasks という名前で Windows ランタイムコンポーネントを作成し て、そこの中に以下のような IBackgroundTask の実装クラスを作成します。 using System; using Windows.ApplicationModel.AppService; using Windows.ApplicationModel.Background; using Windows.Foundation.Collections; namespace AppServiceSample.Server.Tasks
  • 332. 332 { public sealed class SampleAppServiceTask : IBackgroundTask { private BackgroundTaskDeferral Deferral { get; set; } public void Run(IBackgroundTaskInstance taskInstance) { this.Deferral = taskInstance.GetDeferral(); taskInstance.Canceled += this.TaskInstance_Canceled; var details = taskInstance.TriggerDetails as AppServiceTriggerDetails; if (details != null && details.Name == "SampleAppServiceTask") { details.AppServiceConnection.RequestReceived += this.AppServiceConnection_RequestReceived; } } private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { this.Deferral?.Complete(); } private async void AppServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { // メッセージの処理開始 var messageDeferral = args.GetDeferral(); // パラメータを取得する var command = (string)args.Request.Message["Command"]; // パラメータに応じてレスポンスを作成する var responseMessage = new ValueSet(); switch (command) { case "Greet":
  • 333. 333 responseMessage.Add("Message", $"{DateTimeOffset.Now}: Hello world"); break; default: responseMessage.Add("Message", $"Unknown command: {command}"); break; } // レスポンスを返す await args.Request.SendResponseAsync(responseMessage); // メッセージの処理完了 messageDeferral.Complete(); } } } まず、Run メソッドで GetDeferral を呼び出し、Run メソッドが終了しても終了しないように しておきます。その後、TriggerDetails を AppServiceTriggerDetails にキャストして Name が 意図したものだったら、AppServiceConnection の RequestReceived イベントを受信するように します。このイベントでアプリ間の通信の受信処理と応答の作成処理を行います。 RequestReceived イベントハンドラでは、イベント引数の GetDeferral を呼び出してメッセー ジの処理の開始をします。イベント引数の Request の Message で渡された値が取得できるの で、そこから Command というキーで値を取得しています。結果は ValueSet で作成して、Add メソッドで値を追加できます。ここでは Greet という Command がわたってきたらメッセージ を返し、そうでない場合はエラーメッセージを返すようにしています。最後に SendResponseAsync でメッセージを送り返して、messageDeferral で処理の完了をしていま す。 AppServiceSample.Server プロジェクトで AppServiceSample.Server.Tasks プロジェクトを参照 に追加して Package.appxmanifest で以下のように AppService を登録します。ここで指定する 名前の項目が、先ほどのプログラム内でチェックしている名前と同じにします。ここでは
  • 334. 334 「SampleAppServiceTask」です。あとは、エントリポイントにクラス名を設定します。 次に、作成した AppService を呼び出すために必要なアプリケーションのファミリ名を取得しま す。これは、MainPage のコンストラクタあたりに以下のコードを書いて取得します。 Debug.WriteLine(Package.Current.Id.FamilyName); アプリを実行すると、デバッグウィンドウに以下のようにファミリ名が表示されるので控えて おきます。 ffe770b3-39b5-4b44-976e-50964885e963_bhx988h6hv66m 次に、この AppService を呼び出すアプリを作成します。AppServiceSample.Client という名前 で UWP アプリを作成します。画面に Button を置いて Click イベントのコードを以下のように します。 private AppServiceConnection Connection { get; set; } public async void Click() { if (this.Connection == null) { this.Connection = new AppServiceConnection { AppServiceName = "SampleAppServiceTask", PackageFamilyName = "ffe770b3-39b5-4b44-976e-50964885e963_bhx988h6hv66m",
  • 335. 335 }; var status = await this.Connection.OpenAsync(); if (status != AppServiceConnectionStatus.Success) { this.Connection = null; await new MessageDialog($"Error: {status}").ShowAsync(); return; } } var message = new ValueSet(); message.Add("Command", "Greet"); var response = await this.Connection.SendMessageAsync(message); await new MessageDialog($"{response.Status}: {response.Message["Message"]}").ShowAsync(); } 基本的に AppServiceConnection を作成して、AppServiceName(Package.appxmanivest で設定 したもの)と PackageFamilyName(Package.Current.Id.FamilyName で取得したもの)を設定 して OpenAsync を呼び出して、ValueSet で必要なパラメータを作成して SendMessageAsync をするという流れです。戻り値は Status 結果の状態を、Message で戻り値の ValueSet が取得 できます。実行して、ボタンを押すと以下のように表示されます。
  • 336. 336 15.2. 別 ア プリ の 起 動 15.2.1.プ ロ トコ ル に よ る アプ リ の 起動 特定のプロトコル(sampleapp:など)で別アプリから起動されるアプリを作成できます。 Package.appxmanifest の「宣言」タブでプロトコルを追加して、ロゴと名前を設定します。名 前がプロトコルになります。小文字の英字にする必要があります。ここでは sampleapp にしま した。 プロトコルによりアプリが起動された場合は、App クラスの OnActivated メソッドが呼び出さ れます。引数の Kind が Protocol か確認して、引数を ProtocolActivatedEventArgs にキャスト して使います。ProtocolActivatedEventArgs の Data プロパティに呼び出し側から渡された ValueSet が入っています。ここから値を取得して使うことが出来ます。OnActivated メソッド のコード例を以下に示します。 protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.Protocol) { var frame = Window.Current.Content as Frame; if (frame == null) { frame = new Frame(); Window.Current.Content = frame; } var protocolArgs = (ProtocolActivatedEventArgs)args;
  • 337. 337 var param = protocolArgs.Data["Param"] as string; frame.Navigate(typeof(MainPage), param); Window.Current.Activate(); } } 次に、以下の 1 行を MainPage のコンストラクタに追加してパッケージファミリ名を取得しま す。 Degug.WriteLine(Package.Current.Id.FamilyName); 呼び出し側では、以下のように LauncherOptions と ValueSet を作ってファミリ名とデータを セットして Launcher クラスの LaunchUriAsync メソッドで呼び出しを行います。 private async void Button_Click(object sender, RoutedEventArgs e) { var options = new LauncherOptions(); options.TargetApplicationPackageFamilyName = "ee4dfeae-b250-4f32-b54d- 92a69b39cd21_bhx988h6hv66m"; var data = new ValueSet(); data.Add("Param", "Hello world"); await Launcher.LaunchUriAsync(new Uri("sampleapp:"), options, data); } 15.2.2.結 果 を受 け 取 る プ ロト コ ル によ る ア プ リ の起 動 プロトコルによるアプリの起動では一方通行の突き放しでのアプリの起動でしたが、別アプリ を起動して、結果を呼び出し元に返すということが出来ます。このときの呼び出し元は、以下 のように LaunchUriForResultsAsync で呼び出します。 var options = new LauncherOptions(); options.TargetApplicationPackageFamilyName = "ee4dfeae-b250-4f32-b54d- 92a69b39cd21_bhx988h6hv66m"; var data = new ValueSet();
  • 338. 338 data.Add("Param", "Hello world"); var result = await Launcher.LaunchUriForResultsAsync(new Uri("sampleapp:"), options, data); if (result.Status == LaunchUriStatus.Success) { await new MessageDialog((string)result.Result["Message"]).ShowAsync(); } 結果が返ってくるので、Status プロパティで成功か失敗か確認して Result プロパティで結果の 入った ValueSet が取得できます。 呼び出される側は、App クラスの OnActivated メソッドの引数の Kind プロパティが ProtocolForResults になっているので、それを確認して ProtocolForResultsActivatedEventArgs に引数をキャストします。パラメータの受け取り方は、プロトコルによるアプリの起動と同じ で Data プロパティから ValueSet が取得できます。この ProtocolForResultsActivatedEventArgs は、あとで完了通知時に必要になるので、App クラスのフィールドに保持しておきます。 public ProtocolForResultsActivatedEventArgs ProtocolForResultsActivatedEventArgs { get; private set; } protected override void OnActivated(IActivatedEventArgs args) { if (args.Kind == ActivationKind.ProtocolForResults) { var frame = Window.Current.Content as Frame; if (frame == null) { frame = new Frame(); Window.Current.Content = frame; } this.ProtocolForResultsActivatedEventArgs = (ProtocolForResultsActivatedEventArgs)args; var param = this.ProtocolForResultsActivatedEventArgs.Data["Param"] as string; frame.Navigate(typeof(MainPage), param); Window.Current.Activate();
  • 339. 339 } } 呼び出し元に結果を返すには、ProtocolForResultsActivatedEventArgs の ProtocolForResultsOperation プロパティに対して ReportCompleted メソッドを呼び出しま す。このとき引数に結果の入った ValueSet を渡します。コード例を以下に示します。 public void Click() { var args = ((App)App.Current).ProtocolForResultsActivatedEventArgs; // 何か処理 var message = new ValueSet(); message.Add("Message", $"{DateTimeOffset.Now}: Hello world"); args.ProtocolForResultsOperation.ReportCompleted(message); } 16. 複数デバイスに対応したアプリの作成 16.1. ア ダ プテ ィ ブ コ ー ド UWP では、すべてのデバイスファミリで使える API だけでも豊富にありますが、個別のデバ イスファミリでしか使えない API も豊富に提供されています。例えば、UWP アプリで参照の 追加をして「Universal Windows」の「拡張」を選択すると下のほうに「Windows **** Extensions for the UWP」というのがあります。2016 年 7 月 2 日現在、著者の環境では Desktop、IoT、Mobile、Team というものがあります。それぞれ、デスクトップ、IoT、モバ イル、Surface Hub で動作するものになります。これらを参照することで、デバイスファミリ 固有の API が使えるようになります。
  • 340. 340 例えば、Mobile の参照を追加したからといって Windows 10 Mobile でしかアプリが動かなく なるということはありません。この Mobile の中で定義されている API を呼び出したところを 実行時に処理が通過すると、実行時エラーになります。この実行時エラーを避けるために、デ バイスファミリ固有の API を呼び出すときは事前に型やメソッドが提供されているか確認して 呼び出す必要があります。こうすることで、モバイルの時にはフル機能で動きつつ、デスクト ップなら、デスクトップ用に動くということを実現できます。この様な実行環境に合わせて柔 軟に動くコードのことをアダプティブコードといいます。 例えば、Mobile の参照を追加して以下のようなコードを書くと、ハードウェアボタンのカメラ ボタンを押されたときのみメッセージボックスが表示されるようになります。 using System; using Windows.Foundation.Metadata; using Windows.Phone.UI.Input; using Windows.UI.Popups; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation;
  • 341. 341 namespace AdaptiveCodeApp { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); // 型の存在をチェックしてから使う if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) { HardwareButtons.CameraPressed += this.HardwareButtons_CameraPressed; } } protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); // 型の存在をチェックしてから使う if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) { HardwareButtons.CameraPressed -= this.HardwareButtons_CameraPressed; } } private async void HardwareButtons_CameraPressed(object sender, CameraEventArgs e) { // モバイルのみ呼び出される await new MessageDialog("カメラボタンが押されました").ShowAsync(); }
  • 343. 343 型の有無を判定する IsTypePresent 以外にも、IsEnumNameValuePresent、IsEventPresent、 IsMethodPresent、IsPropertyPresent、IsReadOnlyPropertyPresent、 IsWritablePropertyPresent など、様々なものの存在有無をチェックするメソッドが提供されて います。詳細については、以下の API ドキュメントを参照してください。 https://msdn.microsoft.com/library/windows/apps/dn949001
  • 344. 344 16.2. ア ダ プテ ィ ブ UI UWP アプリはデスクトップやモバイルや TV など様々な大きさの画面で動作することが求め られます。このような様々な大きさの画面に対して個別に最適なものを作るよりも、以下の画 面サイズをブレークポイントとして、画面を設計することが推奨されています。  320epx  640epx  1024exp  1366epx 詳細については、以下の公式ドキュメントを参照してください。 https://msdn.microsoft.com/ja-jp/windows/uwp/layout/screen-sizes-and-breakpoints-for- responsive-design これらの画面サイズに応じて見た目を変える方法について、ここでは説明したいと思います。 例として 640epx 以下だと縦並びレイアウトで、それ以上だと横並びレイアウトになるような ものを実現したいと思います。 これを実現するためには RelativePanel と VisualStateManager と AdaptiveTrigger を使うのが 簡単です。RelativePanel で相対的な位置関係でレイアウトを行い、AdaptiveTrigger で画面サ イズによって VisualState を切り替え、VisualState によって RelativePanel 内のコンテンツの相 対的な位置関係を調整するというやり方です。 まず、XAML で以下のように 2 つの Rectangle を横に並べます。RelativePanel で並べている点 がポイントです。 <Page x:Class="AdaptiveUIApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AdaptiveUIApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  • 345. 345 mc:Ignorable="d"> <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Rectangle x:Name="RectangleHeader" Width="320" Height="320" Fill="Red" /> <Rectangle x:Name="RectangleContent" Width="320" Height="400" Fill="LightBlue" RelativePanel.RightOf="RectangleHeader"/> </RelativePanel> </Page> 実行すると以下のようになります。
  • 346. 346 これに VisualStateManager を追加していきます。AdaptiveTrigger を使って 641epx までのとき のステート(Wide)とそれ以下(Narrow)のステートの切り替えを定義しています。 RelativePanel のプロパティを書き換えることで、Rectangle を横並びから縦並びに変えていま す。 <Page x:Class="AdaptiveUIApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AdaptiveUIApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="LayoutGroup"> <VisualState x:Name="Wide"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="641" /> </VisualState.StateTriggers> </VisualState> <VisualState x:Name="Narrow"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="0" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="RectangleContent.(RelativePanel.RightOf)" Value="" /> <Setter Target="RectangleContent.(RelativePanel.Below)" Value="RectangleHeader" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
  • 347. 347 <Rectangle x:Name="RectangleHeader" Width="320" Height="320" Fill="Red" /> <Rectangle x:Name="RectangleContent" Width="320" Height="400" Fill="LightBlue" RelativePanel.RightOf="RectangleHeader"/> </RelativePanel> </Page> 実行すると以下のようになります。 横幅が広いと横並びになります。 横幅が 640epx 以下になると縦並びになります。
  • 349. 349 このようにして、画面サイズに応じてレイアウトを動的に切り替えます。 17. UserControl とテンプレートコントロール UWP での再利用可能なコントロールの作成方法である UserControl とテンプレートコントロ ールの作成方法について説明します。 17.1. UserControl UserControl は Page を作成するのと同じ要領で再利用可能なコントロールを作成する機能で す。非常に手軽に作成可能なため、再利用可能なコントロール作成の最初の選択肢としておす すめです。ここでは NumericUpDown コントロールを UserControl で作成してみたいと思いま す。UserControl を作成するには、「新しい項目の追加」で「ユーザー コントロール」を選択 します。ここでは NumericUpDown という名前で新規作成しました。 まず、値を管理するための依存関係プロパティを Value という名前で定義します。
  • 350. 350 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace UserControlApp { public sealed partial class NumericUpDown : UserControl { public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(0)); public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public NumericUpDown() { this.InitializeComponent(); } } } 次に、XAML で見た目を定義します。 <UserControl x:Class="UserControlApp.NumericUpDown" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UserControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
  • 351. 351 <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Text="{x:Bind Value, Mode=OneWay}" VerticalAlignment="Center" Style="{StaticResource BodyTextBlockStyle}" Grid.RowSpan="2"/> <RepeatButton Content="Up" Click="{x:Bind RepeatButtonUp_Click}" Width="50" Grid.Column="1" /> <RepeatButton Content="Down" Click="{x:Bind RepeatButtonDown_Click}" Width="50" Grid.Row="1" Grid.Column="1" /> </Grid> </UserControl> 最後にイベントハンドラを作成します。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace UserControlApp { public sealed partial class NumericUpDown : UserControl { public static readonly DependencyProperty ValueProperty =
  • 352. 352 DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(0)); public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public NumericUpDown() { this.InitializeComponent(); } public void RepeatButtonUp_Click() { this.Value++; } public void RepeatButtonDown_Click() { this.Value--; } } } これで完成です。後は通常のコントロールと同じようにページに置くことが出来ます。例とし て MainPage に置いてみます。 <Page x:Class="UserControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UserControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
  • 353. 353 <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <local:NumericUpDown x:Name="NumericUpDown" /> <TextBlock> <Run Text="入力値は" /> <Run Text="{x:Bind NumericUpDown.Value, Mode=OneWay}" /> <Run Text="です。" /> </TextBlock> </StackPanel> </Page> 実行すると以下のようになります。 非常に簡単に再利用可能なコントロールが作成可能だということが感じ取っていただけたと思 います。 17.2. テ ン プレ ー ト コ ン トロ ー ル
  • 354. 354 次に、もう 1 つの再利用可能なコントロール作成方法であるテンプレートコントロールについ て説明したいと思います。テンプレートコントロールを作成するには、「新しい項目の追加」 で「テンプレート コントロール」を選択します。ここでは、UserControl と同じ NumericUpDown をテンプレートコントロールで作ってみようと思います。まず、 UserControl と同じように Value 依存関係プロパティを作成します。 using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace TemplateControlApp { public sealed class NumericUpDown : Control { public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(0)); public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public NumericUpDown() { this.DefaultStyleKey = typeof(NumericUpDown); } } } 次に見た目を定義していきます。UserControl は Page と同じようにデザイナが提供されていま したが、テンプレートコントロールでは、ぱっと見どこで見た目を定義するのかわからないと 思います。テンプレートコントロールでは、Themes/Generic.xaml で見た目を定義します。 Generic.xaml を開くと NumericUpDown に対する Style が定義されていると思います。ここの
  • 355. 355 Template を書き換えることで見た目を定義していきます。UserControl と同じように Grid を 置いて TextBlock と RepeatButton を置いていきます。違うのは、イベントハンドラとの紐づ けは、ここでは行わないということです。 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TemplateControlApp"> <Style TargetType="local:NumericUpDown" > <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:NumericUpDown"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}}" Style="{StaticResource BodyTextBlockStyle}" VerticalAlignment="Center" Grid.RowSpan="2" /> <RepeatButton x:Name="ButtonUp" Content="Up" Width="50" Grid.Column="1" />
  • 356. 356 <RepeatButton x:Name="ButtonDown" Content="Down" Width="50" Grid.Column="1" Grid.Row="1"/> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> 見慣れない Binding の指定方法は、今回作成しているコントロールテンプレートのコントロー ルのプロパティとバインドするための書き方になります。これで、NumericUpDown に定義し た Value と TextBlock の Text プロパティを紐づけています。次に、ボタンの Click との処理の 紐づけを行います。これは、OnApplyTemplate というメソッドをオーバーライドして行いま す。GetTemplateChild メソッドで、上記の Style 内で定義した x:Name の値でコントロールを 取得することが出来るのでボタンを取得します。取得できたら Click イベントと購読するよう にします。このメソッドは複数回呼ばれる可能性があるので、クリーンアップ処理も入れてお きます。Click イベントでは Value プロパティをインクリメント・デクリメントしています。 コード全体は以下のようになります。 using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; namespace TemplateControlApp { [TemplatePart(Name = nameof(NumericUpDown.ButtonUp), Type = typeof(ButtonBase))] [TemplatePart(Name = nameof(NumericUpDown.ButtonDown), Type = typeof(ButtonBase))] public sealed class NumericUpDown : Control {
  • 357. 357 private ButtonBase ButtonUp { get; set; } private ButtonBase ButtonDown { get; set; } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), new PropertyMetadata(0)); public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public NumericUpDown() { this.DefaultStyleKey = typeof(NumericUpDown); } protected override void OnApplyTemplate() { base.OnApplyTemplate(); if (this.ButtonUp != null) { this.ButtonUp.Click -= this.ButtonUp_Click; } if (this.ButtonDown != null) { this.ButtonDown.Click -= this.ButtonDown_Click; } this.ButtonUp = this.GetTemplateChild("ButtonUp") as ButtonBase; if (this.ButtonUp != null) { this.ButtonUp.Click += this.ButtonUp_Click;
  • 358. 358 } this.ButtonDown = this.GetTemplateChild("ButtonDown") as ButtonBase; if (this.ButtonDown != null) { this.ButtonDown.Click += this.ButtonDown_Click; } } private void ButtonUp_Click(object sender, RoutedEventArgs e) { this.Value++; } private void ButtonDown_Click(object sender, RoutedEventArgs e) { this.Value--; } } } TemplatePart 属性は、この名前でこの型がテンプレート内に存在していることを外部に示すた めの属性になります。なくてもエラーにはなりません。これで、Page に置いて普通のコントロ ールのように使用が可能です。 <Page x:Class="TemplateControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TemplateControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <local:NumericUpDown x:Name="NumericUpDown" /> <TextBlock>
  • 359. 359 <Run Text="入力値は" /> <Run Text="{x:Bind NumericUpDown.Value, Mode=OneWay}" /> <Run Text="です。" /> </TextBlock> </StackPanel> </Page> 実行すると以下のような見た目になります。 UserControl よりもテンプレートコントロールのほうが、作成が難しく煩雑です。では、テン プレートコントロールのメリットはなんでしょうか。それは、テンプレートコントロールは、 通常のコントロールと同じように Template プロパティを使って ControlTemplate を差し替え ることが出来るという点です。UserControl では、これが出来ません。例として先ほどのペー ジの NumericUpDown の見た目を差し替えてみたいと思います。XAML を以下に示します。 <Page x:Class="TemplateControlApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  • 360. 360 xmlns:local="using:TemplateControlApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <local:NumericUpDown x:Name="NumericUpDown"> <local:NumericUpDown.Template> <ControlTemplate TargetType="local:NumericUpDown"> <StackPanel> <RepeatButton x:Name="ButtonUp" Content="▲" /> <TextBlock> <Run Text="[" /> <Run Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> <Run Text="]" /> </TextBlock> <RepeatButton x:Name="ButtonDown" Content="▼" /> </StackPanel> </ControlTemplate> </local:NumericUpDown.Template> </local:NumericUpDown> <TextBlock> <Run Text="入力値は" /> <Run Text="{x:Bind NumericUpDown.Value, Mode=OneWay}" /> <Run Text="です。" /> </TextBlock> </StackPanel> </Page> 実行すると以下のようになります。見た目が差し変わっていることが確認できます。
  • 361. 361 18. UWP でのアプリの設計 UWP アプリを含む XAML をベースとしたアプリケーションの開発では一般的に MVVM パタ ーンという設計手法で開発が行われます。ここでは MVVM の基本的な考えと、UWP アプリ に対応した MVVM フレームワークである Prism for Windows Runtime について簡単に紹介し たいと思います。 18.1. MVVM パ タ ー ン とは MVVM パターンとは、アプリケーションを Model と ViewModel と View に分割して考える設 計パターンです。Wikipedia の MVVM パターンのページを以下に示します。 https://ja.wikipedia.org/wiki/Model_View_ViewModel Wikipedia の図を引用しますが、MVVM パターンは以下のように図示されます。
  • 362. 362 Wikipedia の MVVM パターンのページから引用 簡単に言うと Model は、アプリケーションのコアロジックを持ったレイヤーになります。 ViewModel から操作され、Model からイベント通知などで ViewModel にフィードバックをか えします。ViewModel は、名前の通り View をモデル化したものです。画面に強く紐づいた画 面のための Model であるとも言えます。View は UWP アプリでは XAML とコードビハインド で構成されます。ViewModel と対話しますが、一般的にデータバインディングを使って ViewModel と紐づけます。 具体的には、Model クラスは INotifyPropertyChanged を実装して ViewModel に対してプロパ ティの変更通知で状態変更を伝えます。他には、Model のメソッドの戻り値で結果を ViewModel に通知します。ViewModel も INotifyPropertyChanged を実装して View に対して 変更通知を行います。ユーザーのアクションには ICommand を実装したクラスを公開したり、 View からのメソッド呼び出しによって応答します。 18.2. Prism for Windows Runtime 実際に MVVM パターンで簡単なアプリケーションを作ってみようと思います。MVVM パター ンのアプリケーションを開発するためのフレームワークとして Prism と呼ばれるライブラリが あります。Prism は、もとは WPF のために開発されたライブラリですが現在は、UWP や Xamarin.Forms など XAML 系プラットフォームにひろく対応したライブラリになっていま す。MVVM で開発を行うために必要な各種便利な機能が提供されていたり、オブジェクトの インスタンスの持ち回りといった、めんどくさい問題を簡単に解決してくれる DI コンテナと
  • 363. 363 の連携機能ももっているため使用すると楽に開発が出来ます。Prism の提供する代表的な機能 を以下に示します。  INotifyPropertyChanged を実装するのを楽にするための基本クラス  デリゲートを渡すことで Execute メソッドや CanExecute メソッドに処理を渡すことが出 来る ICommand の実装クラス  ViewModel での画面遷移の仕組み  App クラスの基本クラス提供による App クラスの実装の簡略化  中断時のデータ保存機能  ViewModel での画面遷移のコールバック(OnNavigatedTo, OnNavigatedFrom)の提供  DI コンテナ(Unity)との連携  メッセンジャー機能  入力値の検証機能  View の DataContext に自動で ViewModel を設定する機能 18.2.1.足 し 算ア プ リ の 作 成 Prism の機能を確認するために、簡単な足し算機能をもったアプリを作成してみたいと思いま す。最初の画面で 2 つの数字を入力してボタンを選択すると、画面遷移して足し算の結果が表 示されるというものです。PrismAddApp という名前で UWP アプリを作成します。そして、 NuGet から以下のパッケージをインストールして Prism 関連のライブラリをインストールしま す。  Prism.Unity
  • 364. 364 App.xaml を開いて以下のように書き換えます。 <Prism:PrismUnityApplication x:Class="PrismAddApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PrismAddApp" xmlns:Prism="using:Prism.Unity.Windows" RequestedTheme="Light"> </Prism:PrismUnityApplication> Prism のアプリケーションの基本クラスは PrismUnityApplication になるため、そのように XAML を構成しています。次に App.xaml.cs を開いて PrismUnityApplication を継承するよう に書き換えます。 using Prism.Unity.Windows; using System.Threading.Tasks; using Windows.ApplicationModel.Activation; namespace PrismAddApp { sealed partial class App : PrismUnityApplication { protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args) { this.NavigationService.Navigate("Main", args.Arguments); return Task.CompletedTask;
  • 365. 365 } } } 基本的に、OnLaunchApplicationAsync メソッドをオーバーライドして使います。ここで NavigationService プロパティで取得できる INavigationService インターフェースのインスタン スに対して、Navigate メソッドを呼び出して画面遷移を行います。画面遷移は文字列ベースで 行います。Main という文字列を渡すと、デフォルトの挙動では Views 名前空間の MainPage クラスへ遷移するルールになっています。では、遷移先の View を作成します。既存の MainPage.xaml を削除して、Views 名前空間を作成して、そこに MainPage.xaml を作成しま す。 この MainPage に ViewModel を DataContext に紐づけます。DataContext への ViewModel の 紐づけは、XAML に Prism.Windows.Mvvm 名前空間にある ViewModelLocator というクラス の AutoWireViewModel 添付プロパティを設定します。コードは以下のようになります。 <Page x:Class="PrismAddApp.Views.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PrismAddApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Mvvm="using:Prism.Windows.Mvvm" Mvvm:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> </Grid> </Page> この設定を行うことで ViewModels 名前空間の MainPageViewModel というクラスが自動的に 設定されるようになります。デフォルトでは ViewModels 名前空間のページ名 ViewModel とい うルールですが、このルールはカスタマイズすることも出来ます。ViewModels 名前空間を作 成して、MainPageViewModel クラスを作成して以下のように記述します。ViewModel は基本
  • 366. 366 クラスである ViewModelBase クラスが提供されているため、それを継承します。うまく動い ているか確認するために Message プロパティを定義しています。この Message プロパティのよ うに SetProperty メソッドを使うことで、PropertyChanged が自動で発行されるようになりま す。 using Prism.Windows.Mvvm; namespace PrismAddApp.ViewModels { public class MainPageViewModel : ViewModelBase { private string message = "Hello world"; public string Message { get { return this.message; } set { this.SetProperty(ref this.message, value); } } } } MainPage.xaml に Message プロパティにバインドした TextBlock を置きます。 <Page x:Class="PrismAddApp.Views.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PrismAddApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Mvvm="using:Prism.Windows.Mvvm" Mvvm:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  • 367. 367 <TextBlock Text="{Binding Message}" Style="{StaticResource HeaderTextBlockStyle}" /> </Grid> </Page> 実行すると、Hello world と表示されます。App クラスで画面遷移がされて MainPage に遷移し ます。そして、MainPage の DataContext に MainPageViewModel が設定されて Message プロ パティがバインドされて表示されています。 View と ViewModel のひな型が出来たので Model を作成していきます。Models 名前空間(こ この名前は任意でかまいませんし、別アセンブリにわけてもかまいません。デフォルトで特に ルールなどはありません)に AppModel クラスを作成して以下のように記述します。 using Prism.Windows.Validation; using System.ComponentModel.DataAnnotations; using System.Linq; namespace PrismAddApp.Models
  • 368. 368 { public class AppModel : ValidatableBindableBase { private string lhs = "0"; [Required(ErrorMessage = "Lhs is required")] [RegularExpression("[0-9]+", ErrorMessage = "Lhs is number")] public string Lhs { get { return this.lhs; } set { this.SetProperty(ref this.lhs, value); } } private string rhs = "0"; [Required(ErrorMessage = "Rhs is required")] [RegularExpression("[0-9]+", ErrorMessage = "Rhs is number")] public string Rhs { get { return this.rhs; } set { this.SetProperty(ref this.rhs, value); } } private double answer; public double Answer { get { return this.answer; } set { this.SetProperty(ref this.answer, value); } } public void Add() { if (this.GetAllErrors().Any()) {
  • 369. 369 this.Answer = double.NaN; return; } this.Answer = int.Parse(this.Lhs) + int.Parse(this.Rhs); } } } この AppModel クラスを Prism の管理している DI コンテナ(Unity)の中に登録します。こう することで、ViewModel へ自動的にインスタンスを渡したりといったことが可能になります。 App クラスで ConfigureContainer メソッドをオーバーライドして、DI コンテナのに登録を行 っています。RegisterTypeIfMissing メソッドが DI コンテナへの登録を行うメソッドで第一引 数が登録する実体の型、第二引数がコンテナから取得するときに指定する型(インターフェー スなどを実装しているクラスなどの場合には、ここでインターフェースの型を渡します)、第 三引数がシングルトンで管理するかどうかになります。今回は、AppModel 型をシングルトン で登録しています。 using Prism.Unity.Windows; using System.Threading.Tasks; using Windows.ApplicationModel.Activation; using Microsoft.Practices.Unity; using PrismAddApp.Models; namespace PrismAddApp { sealed partial class App : PrismUnityApplication { protected override void ConfigureContainer() { base.ConfigureContainer(); this.RegisterTypeIfMissing(typeof(AppModel), typeof(AppModel), true); }
  • 370. 370 protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args) { this.NavigationService.Navigate("Main", args.Arguments); return Task.CompletedTask; } } } 次に MainPageViewModel を作成します。AppModel から必要なだけプロパティを公開してい ます。そして、DelegateCommand という ICommand の実装クラスを使って View に対して ViewModel が出来る操作を公開しています。Command は、Lhs プロパティと Rhs プロパティ を監視して CanExecuteChanged イベントを発行するという指定をしています。 using Prism.Commands; using Prism.Windows.Mvvm; using Prism.Windows.Navigation; using PrismAddApp.Models; using System.Linq; namespace PrismAddApp.ViewModels { public class MainPageViewModel : ViewModelBase { private AppModel Model { get; } private INavigationService NavigationService { get; } public string Lhs { get { return this.Model.Lhs; } set { this.Model.Lhs = value; this.OnPropertyChanged(nameof(Lhs)); } } public string Rhs {
  • 371. 371 get { return this.Model.Rhs; } set { this.Model.Rhs = value; this.OnPropertyChanged(nameof(Rhs)); } } public DelegateCommand AddCommand { get; } public MainPageViewModel(AppModel model, INavigationService navigationService) { this.Model = model; this.NavigationService = navigationService; this.AddCommand = new DelegateCommand(() => { this.NavigationService.Navigate("Answer", null); }, () => !this.Model.GetAllErrors().Any()) .ObservesProperty(() => this.Lhs) .ObservesProperty(() => this.Rhs); } } } Model の入力エラーが無い状態で AddCommand が実行されると画面遷移を行っています。画 面遷移は、コンストラクタ引数に INavigationService を受け取ることで自動的に画面遷移に使 うクラスのインスタンスが注入されます。 View 側の実装を行います。View でコンパイル時バインディングを使うために DataContext を 型を指定して公開するプロパティをコードビハインドに定義します。 using PrismAddApp.ViewModels; using Windows.UI.Xaml.Controls; namespace PrismAddApp.Views { public sealed partial class MainPage : Page
  • 372. 372 { public MainPageViewModel ViewModel => this.DataContext as MainPageViewModel; public MainPage() { this.InitializeComponent(); } } } そして、画面で Lhs プロパティと Rhs プロパティをバインドするための TextBox と AddCommand をバインドするための AppBarButton を定義します。 <Page x:Class="PrismAddApp.Views.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PrismAddApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Mvvm="using:Prism.Windows.Mvvm" Mvvm:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> <Page.BottomAppBar> <CommandBar> <AppBarButton Icon="Add" Label="Add" Command="{x:Bind ViewModel.AddCommand}" /> </CommandBar> </Page.BottomAppBar> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Header="Lhs" Text="{Binding Lhs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBox Header="Rhs" Text="{Binding Rhs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> </StackPanel>
  • 373. 373 </Page> 次に、AnswerPage を作成します。Views 名前空間に AnswerPage を作って、ViewModels 名前 空間に AnswerPageViewModel を作成します。AnswerPageViewModel は画面遷移時に AppModel の Add メソッドを呼び出して計算をしています。そして、計算結果を View に公開 するためのプロパティを定義しています。このプロパティの変更があったことを通知するため に、AppModel の PropertyChanged を購読して、必要に応じて自分の PropertyChanged イベン トを発火しています。 using Prism.Windows.Mvvm; using Prism.Windows.Navigation; using PrismAddApp.Models; using System.Collections.Generic; namespace PrismAddApp.ViewModels { public class AnswerPageViewModel : ViewModelBase { private AppModel Model { get; } public double Answer => this.Model.Answer; public AnswerPageViewModel(AppModel model) { this.Model = model; } public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary<string, object> viewModelState) { base.OnNavigatedTo(e, viewModelState); this.Model.PropertyChanged += this.Model_PropertyChanged; this.Model.Add(); }
  • 374. 374 public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string, object> viewModelState, bool suspending) { base.OnNavigatingFrom(e, viewModelState, suspending); if (!suspending) { this.Model.PropertyChanged -= this.Model_PropertyChanged; } } private void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(AppModel.Answer): this.OnPropertyChanged(nameof(Answer)); break; } } } } View は、コードビハインドで ViewModel プロパティの定義と答えを表示するための TextBlock が置いてあるだけのシンプルな画面です。 using PrismAddApp.ViewModels; using Windows.UI.Xaml.Controls; namespace PrismAddApp.Views { public sealed partial class AnswerPage : Page { public AnswerPageViewModel ViewModel => this.DataContext as AnswerPageViewModel; public AnswerPage()
  • 375. 375 { this.InitializeComponent(); } } } XAML は以下のようになります。 <Page x:Class="PrismAddApp.Views.AnswerPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PrismAddApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Mvvm="using:Prism.Windows.Mvvm" Mvvm:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock Text="{x:Bind ViewModel.Answer, Mode=OneWay}" Style="{StaticResource HeaderTextBlockStyle}" /> </Grid> </Page> 実行すると以下のようになります。入力値が不正な時は、AppBarButton が押せなくなってい ます。
  • 378. 378 18.2.2.基 本 的な ク ラ ス の 使い 方 ここでは、基本的な Prism のクラスの使い方を説明します。 18.2.2.1.BindableBase クラス INotifyPropertyChanged インターフェースを実装したクラスになります。以下のようにプロパ ティを定義することで、PropertyChanged を発行してくれるプロパティが定義できます。 private string input; public string Input { get { return this.input; } set { this.SetProperty(ref this.input, value); } } 18.2.2.2.ValidatableBindableBase クラス
  • 379. 379 DataAnnotations による入力値の検証をサポートするクラスです。基本的な使い方は BindableBase と同じですが、プロパティに DataAnnotations の各種検証用属性をつけることで 入力値の検証を行うことが出来ます。Input というプロパティが必須入力のクラスの定義例を 以下に示します。 using Prism.Windows.Validation; using System.ComponentModel.DataAnnotations; namespace ValidatableBindableBaseApp.ViewModels { class InputDataViewModel : ValidatableBindableBase { private string input; [Required(ErrorMessage = "入力してください")] public string Input { get { return this.input; } set { this.SetProperty(ref this.input, value); } } } } デフォルトでは、プロパティに値が設定されたタイミングで値の検証が走ります。 そのほかに 任意のプロパティの値の検証や、全てのプロパティの値の検証を行うメソッドが提供されてい ます。  ValidateProperty:プロパティ名指定での検証  ValidateProperties:全プロパティの検証 メソッドの戻り値は bool 型で、false が返るとエラーがあることを示しています。 エラー結果の取得は Errors プロパティで取得します。このクラスにはインデクサが定義されて いて、そこにプロパティ名を渡すことでエラーメッセージのコレクションが取得できます。こ
  • 380. 380 れを画面で Binding することで、エラーメッセージのコレクションが取得できます。 ViewModel に Input という名前で先ほどのクラスを定義した画面でエラーメッセージの表示を 実現する方法を以下に示します。 <Page x:Class="ValidatableBindableBaseApp.Views.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ValidatableBindableBaseApp.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mvvm="using:Prism.Windows.Mvvm" mvvm:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock Text="入力項目" Style="{StaticResource CaptionTextBlockStyle}" /> <TextBox Text="{x:Bind ViewModel.InputData.Input, Mode=TwoWay}" /> <ItemsControl ItemsSource="{Binding InputData.Errors[Input], Mode=OneWay}" Foreground="Red" /> <Button Content="チェック" Click="{x:Bind ViewModel.Alert}" /> </StackPanel> </Page> 以下のような感じの画面になります。
  • 381. 381 エラーの有無の判定は、GetAllErrors メソッドに要素が含まれているかどうかで判定できま す。 コード例を以下に示します。 public async void Alert() { if (this.InputData.GetAllErrors().Any()) { var dlg = new MessageDialog("HasError"); await dlg.ShowAsync(); } else { var dlg = new MessageDialog("NoError"); await dlg.ShowAsync(); } }
  • 382. 382 通常はプロパティがセットされたタイミングで値の検証がされますが、これを無効化すること ができます。 IsValidationEnabled プロパティを false に設定することで無効化できます。 IsValidationEnabled を false に設定したときは ValidateProperties を呼び出すことで強制的に全 プロパティの検証を行えます。 18.2.2.3.DelegateCommand クラス コンストラクタに Execute 時に実行される処理と CanExecute 時に実行される処理を渡して使 う ICommand の実装クラスです。RaiseCanExecuteChanged メソッドを呼び出すことで CanExecuteChanged イベントを発行します。ObserveProperty メソッドを使うことで、指定し たプロパティを監視して、プロパティに変更があったタイミングで RaiseCanExecuteChanged を実行するように指定が可能です。 public DelegateCommand AddCommand { get; } public コンストラクタ() { // メソッドを渡してもラムダ式を渡しても OK this.AddCommand = new DelegateCommand(this.AddExecute, this.AddCanExecute) // Hoge プロパティを監視する .ObserveProperty(() => this.Hoge); } private void AddExecute() { … } private bool AddCanExecute() { … } コマンド実行時に CommandParameter を受け取る DelegateCommand<T>というクラスもあ ります。Execute と CanExecute の処理が引数を受け取るようになる以外の使い方は同じになり ます。 18.2.2.4.ViewModelBase クラス ViewModel の基本クラスになります。BindableBase に対して画面遷移時のコールバックメソッ ドを追加したものになります。OnNavigatedTo メソッドが画面に来たときの処理で OnNavigatedFrom メソッドが画面から去るときの処理になります。OnNavigatedFrom メソッ
  • 383. 383 ドは、suspending 引数で画面から去る理由が画面遷移のせいなのか、サスペンドに入るためな のかを確認することが出来ます。また、引数の viewModelState を使うことで、サスペンド時に データを一時保存するための領域が提供されています。サスペンド時に viewModelState にデー タを格納して OnNavigatedTo メソッドで値を取り出して使用します。 また、画面遷移時に渡されるパラメータは NavigatedToEventArgs の Parameter を使って取得 できます。 public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary<string, object> viewModelState) { base.OnNavigatedTo(e, viewModelState); var parameter = e.Parameter; // 画面に来た時の処理 } public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string, object> viewModelState, bool suspending) { base.OnNavigatingFrom(e, viewModelState, suspending); if (!suspending) { // 画面から離れる時の処理 } else { // サスペンド時の処理 } } この他に、ViewModelBase クラスを継承したクラスのプロパティに RestorableState 属性をつ けることで、サスペンド時に自動で保存を行って復帰時に自動で値を復元してくれるといった 機能もあります。単純な値の保存などは、viewModelState を使うよりも、そちらのほうが簡単 にできます。
  • 384. 384 18.2.2.5.INavigationService インターフェース Prism の管理している DI コンテナ(Unity)の中で管理されているインターフェースです。 Navigate メソッドで画面名とパラメータを渡して画面遷移を行います。GoBack メソッド、 GoFoward メソッドで画面遷移の履歴を戻ったり進んだりできます。CanGoBack メソッド、 CanGoFoward メソッドで戻ったり進めたりできるかといった判定が出来ます。 INavigationService を使うには、ViewModel クラスなどのコンストラクタで INavigationService を受け取るようにして使用します。 private INavigationService NavigationService { get; } public コンストラクタ(INavigationService navigationService) { this.NavigationService = navigationService; } public void Foo() { // NextPage へ 10 を引数に渡して遷移する this.NavigationService.Navigate(“Next”, “10”); } 18.2.2.6.ISessionStateService インターフェース ISessionStateService インターフェースも Prism の DI コンテナ(Unity)の中で管理されてい るインターフェースです。使用するには ViewModel クラスなどのコンストラクタで受け取る ようにします。SessionState プロパティが Dictionary<string, object>型なので、ここに任意の 値(シリアライズ可能であることと後述する条件を満たすことが必要です)を格納すること で、サスペンド時にも保存されるように値を管理してくれます。あくまで一時保存領域のた め、永続的に保存したい値などは別途 ApplicationData クラスを使って保存してください。 プリミティブ型以外の独自のクラスを格納する場合は、App クラスの OnRegisterKnownTypesForSerialization メソッドで以下のように RegisterKnownType で登録 する必要があります。 protected override void OnRegisterKnownTypesForSerialization()
  • 385. 385 { base.OnRegisterKnownTypesForSerialization(); this.SessionStateService.RegisterKnownType(typeof(Hoge)); this.SessionStateService.RegisterKnownType(typeof(Fuga)); } 18.2.2.7.IDeviceGestureService インターフェース IDeviceGestureService インターフェースも Prism の DI コンテナ(Unity)の中で管理されて いるインターフェースです。使用するには ViewModel クラスなどのコンストラクタで受け取 るようにします。戻るボタンへの対応やデスクトップでのタイトルバーの戻るボタンの表示・ 非表示対応や、電話のハードウェアの戻るボタンカメラボタンへの対応サポート機能がありま す。 IDeviceGestureService には、各種ハードウェアの有無を問い合わせるための以下のプロパティ が定義されています。  IsHardwareCameraButtonPresent  IsHardwareBackButtonPresent  IsKeyboardPresent  IsMousePresent  IsTouchPresent 名前の通り上から順番に、カメラボタン、戻るボタン、キーボード、マウス、タッチが提供さ れているか確認できます。さらに、ハードウェアボタンとマウスに関しては各イベントが提供 されています。  GoBackRequested  CameraButtonPressed  CameraButtonHalfPressed
  • 386. 386  CameraButtonReleased  MouseMoved 順番に戻る処理、カメラボタンが押されたか、半押しされたか、離されたか、マウスが動いた かといったイベントが取れます。 また、このクラスは Prism のデフォルトの挙動のデスクトップで動かすとタイトルバーに戻る ボタンが表示されるといった機能も司っています。これを非表示にするには App クラスで DeviceGestureService の作成機能を以下のようにオーバーライドする必要があります。 // App.xaml.cs protected override IDeviceGestureService OnCreateDeviceGestureService() => new DeviceGestureService { UseTitleBarBackButton = false }; 18.2.2.8.IEventAggregator インターフェース IEventAggregator インターフェースも Prism の DI コンテナ(Unity)の中で管理されているイ ンターフェースです。使用するには ViewModel クラスなどのコンストラクタで受け取るよう にします。IEventAggregator は、イベントの仲介役のクラスです。このクラスを仲介役にして クラス間で疎結合なイベントのやり取りが行えます。IEventAggregator は、T GetEvent<T>() メソッドを持つだけのシンプルなインターフェースです。GetEvent メソッドの型引数には、通 常 PubSubEvent<T>クラスの派生クラスを指定します。以下のようなクラスを定義します。 using Prism.Events; namespace EventAggregatorSample.Infrastructure { public class InputValueEvent : PubSubEvent<InputValue> { } public class InputValue { public int Value { get; set; } }
  • 387. 387 } このクラスをキーにしてイベントのやり取りを行います。IEventAggregator でイベントを発行 するには、以下のようなコードを記述します。 this.EventAggregator .GetEvent<InputValueEvent>() .Publish(new InputValue { Value = this.Random.Next(100) }); イベントの購読側では Subscribe メソッドを使ってイベントの購読を行います。Subscribe メソ ッドの戻り値に対して Dispose メソッドを呼び出すことでイベントの購読解除を行えます。 this.EventAggregator .GetEvent<InputValueEvent>() .Subscribe(this.InputValueSubscribe, ThreadOption.UIThread); // 受信メソッド private void InputValueSubscribe(InputValue x) { … } 19. まとめ 簡単にですが、UWP アプリを開発するうえで必要になるであろうことを書き綴ってきまし た。ここから先、何を見ていけばいいのかを書いて最後のまとめとしたいと思います。 まず、公式ドキュメントです。これを書くにあたっても参考にしました。ここで書いていない ことも載っていますので、あんなことが出来ないか?と思ったら目次を展開してみてくださ い。 https://developer.microsoft.com/ja-jp/windows/develop そして、GitHub にあがっている公式のサンプルプログラムも是非ダウンロードして確認して ください。このサンプルプログラムは正直難解ですが、かなりの機能が網羅されているサンプ ルプログラム集になっています。XAML と名前のつくものからやるのが、見た目に直結してい たわかりやすいと思うのでお勧めします。その他のものも、時間のあるときに、どんなものが あるのか起動だけでもして確認しておくと引き出しが増えると思います。 https://github.com/Microsoft/Windows-universal-samples