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 が実行されます。
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();
}
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)));
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"
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>
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; }
}
}
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; }
}
}
実行して、項目をタップすると以下のようになります。
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">
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);
}
}
}
実行すると、以下のようにアイコンに重なるように矩形が表示されます。
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;
}
}
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 メソッドを呼び出すと以下のようにトーストが表示されます。
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,
},
},
}
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
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;