はじめに
前回 からだいぶ間が空いてしまいましたが、Windowsストア アプリ開発の2回目です。
今回は、設定チャーム から表示できる設定ウィンドウ (図1 )をアプリで使えるようにしてみましょう。設定ウィンドウの実装はアプリの認定(審査)にも関わる重要な機能ですので押さえておきましょう(なぜ重要かは後述します) 。
図1 SkyDriveの設定ウィンドウ
また、前回の内容の発展としてSkyDriveなどにアクセスするためのMicrosoftアカウントのサインイン機能を設定ウィンドウで実装します。
設定ウィンドウと設定ポップアップ
Windowsストア アプリでは、アプリの全般的な設定を、設定チャームからアクセスする設定ウィンドウ というWindowsで用意されている共通のUIから行えます。
設定ウィンドウ
図1 はSkyDriveの設定ウィンドウです。
ウィンドウ上部にアプリの設定項目が表示されています。Windowsストアからインストールしたアプリは、「 アクセス許可」と「評価とレビュー」が既定で用意されています。それ以外の「オプション」 、「 バージョン情報」 、「 ヘルプ」の項目はSkyDriveが用意したものです。
ユーザーが設定ウィンドウを開いた時、アプリの設定を表示するようアプリ側でコーディングします。
設定ウィンドウ下部は、PCに関する設定が表示されます。
設定ポップアップ
設定ウィンドウに表示されるアプリの設定項目は、エントリポイントとして使い、アプリ画面の表示切り替えや設定ポップアップ を表示して、より詳細な設定画面を表示します。
SkyDriveの「オプション」は、図2 のように設定ウィンドウから設定ポップアップが開きます。
図2 SkyDriveのオプションの設定ポップアップ
設定ウィンドウや設定ポップアップに、どのような項目を用意すべきかなど詳しくは、アプリ設定のガイドライン (Windows ストア アプリ) (Windows) を参照してみてください。
設定ウィンドウが重要な理由
冒頭で触れた設定ウィンドウがアプリ認定に重要な理由ですが、ほぼすべてのアプリはプライバシーポリシーを用意する必要があります[1] 。そして、プライバシーポリシーは、アプリの設定からアクセスできるようにする必要があります。
このため、設定ウィンドウでアプリのプライバシーポリシーにアクセスできるようにすることは、アプリ開発でほぼ必須の作業となっています。
ちなみに、プライバシーポリシーを記載したWebページも必要です。アプリ登録時にプライバシーポリシーのURLを指定します。
[1]
Webにアクセスするアプリは必須です。アクセスしないアプリでも認定を通過するためには用意したほうが無難です。個人で作成したアプリでも必要ですが、内容は簡素なものでもOKですのでそれほど難しい作業ではありません。いろいろなアプリを参考にしてみるとよいでしょう。
設定ウィンドウにアプリ設定項目の追加
それでは実際にコーディングしていきましょう。
プロジェクトの作成
Visual Studioでプロジェクトを作成します。今回も前回と同じく、C#の一番シンプルな「新しいアプリケーション(XAML) 」を選択します(図3 ) 。図は、Visual Studio 2012 Express for Windows 8の画面です。前回のプロジェクトを引き続き使用しても構いません。
図3 プロジェクトの作成
アプリの設定項目の追加
さっそく、設定ウィンドウにアプリ独自の項目を追加します。
設定ウィンドウのオブジェクト(SettingsPane オブジェクト)のCommandsRequested イベントで項目を追加します。ここでは、ユーザーがアプリの起動した時にその処理を書きます。App.xaml.csのOnLaunched メソッド内に次のようにコードを追記しましょう。
bool EventRegistered ;
protected override void OnLaunched ( LaunchActivatedEventArgs args )
{
if (! this . EventRegistered )
{
SettingsPane . GetForCurrentView (). CommandsRequested += OnCommandsRequested ;
this . EventRegistered = true ;
}
}
OnCommandsRequested の内容は次のようになります。ここで実際に項目を設定ウィンドウに追加しています。項目となるSettingsCommand オブジェクトを生成してApplicationCommands 配列に追加します。
ここでSettingsCommand オブジェクト生成時に指定している値は、“ policy” というコマンドのID(任意に決めれます)と設定ウィンドウに表示される「プライバシーポリシー」の文字、そして項目を選択したときに呼ばれるハンドラーです。
void OnCommandsRequested ( SettingsPane settingsPane , SettingsPaneCommandsRequestedEventArgs eventArgs )
{
var handler = new UICommandInvokedHandler ( OnSettingsCommand );
var policyCommand = new SettingsCommand ( "policy" , "プライバシーポリシー" , handler );
eventArgs . Request . ApplicationCommands . Add ( policyCommand );
}
続いて、設定項目が選択された時の処理を書きます。次のようにSettingsCommand オブジェクトに指定したIDを使って処理を分岐できます(今はひとつしか項目を追加していませんが) 。
ここではWebページを表示するようにしています。
void OnSettingsCommand ( IUICommand command )
{
var settingsCommand = ( SettingsCommand ) command ;
switch ( settingsCommand . Id . ToString ())
{
case "policy" :
ShowPrivacyPolicy ();
break ;
}
}
async void ShowPrivacyPolicy ()
{
Uri uri = new Uri ( "http://example.jp/privacypolicy" );
await Launcher . LaunchUriAsync ( uri );
}
ここまでを実行してみましょう。設定チャームから設定ウィンドウを開くと追加した項目が表示されたでしょうか(図4 ) 。
図4 設定ウィンドウにプライバシーポリシーの追加
以上で、Webページを表示するような簡単な設定は作れるようになりました。
設定ポップアップの表示
続いて、設定ポップアップを表示してみましょう。設定ポップアップとなるページをプロジェクトに追加します。
メニューの「プロジェクト」ー「新しい項目の追加」から空白のページを選びます。ここでは、SettingsFlyout.xamlという名前にしています。
図5 空白のページの追加
XAMLの内容を変更します。ひとまず動作がわかるよう文字を表示するだけの簡単な内容にしておきます。
SettingsFlyout.xaml
<Grid Background = "{StaticResource ApplicationPageBackgroundThemeBrush}" >
<TextBlock> サンプル </TextBlock>
</Grid>
項目の追加
設定ポップを表示するための項目を設定ウィンドウに追加しましょう。OnCommandsRequested メソッドにコードを追記します[2] 。
void OnCommandsRequested ( SettingsPane settingsPane , SettingsPaneCommandsRequestedEventArgs eventArgs )
{
var handler = new UICommandInvokedHandler ( OnSettingsCommand );
var accountCommand = new SettingsCommand ( "account" , "アカウント" , handler );
eventArgs . Request . ApplicationCommands . Add ( accountCommand );
var policyCommand = new SettingsCommand ( "policy" , "プライバシーポリシー" , handler );
eventArgs . Request . ApplicationCommands . Add ( policyCommand );
}
OnSettingsCommand メソッドは、次のように変更し処理を分岐します。
void OnSettingsCommand ( IUICommand command )
{
var settingsCommand = ( SettingsCommand ) command ;
switch ( settingsCommand . Id . ToString ())
{
case "policy" :
ShowHelp ();
break ;
case "account" :
ShowSettingsFlyout ();
break ;
}
}
設定ポップアップ処理
ShowSettingsFlyout メソッドに設定ポップアップを表示するコードを記述します。
手順は、設定ポップアップとなるPopup オブジェクトを生成し、ChildプロパティにSettingsFlyout オブジェクトを指定します。位置や大きさ、アニメーションの設定など細々と行っていますが、最後にPopup. IsOpen プロパティにtrueを指定すれば設定ポップアップが表示されます。
private Popup popup ;
void ShowSettingsFlyout ()
{
var bounds = Window . Current . Bounds ;
var width = 646 ;
popup = new Popup ();
popup . IsLightDismissEnabled = true ;
popup . Width = width ;
popup . Height = bounds . Height ;
popup . ChildTransitions = new Windows . UI . Xaml . Media . Animation . TransitionCollection ();
popup . ChildTransitions . Add ( new Windows . UI . Xaml . Media . Animation . PaneThemeTransition ()
{
Edge = ( SettingsPane . Edge == SettingsEdgeLocation . Right ) ?
EdgeTransitionLocation . Right :
EdgeTransitionLocation . Left
});
var pane = new SettingsFlyout ();
pane . Width = width ;
pane . Height = bounds . Height ;
popup . Child = pane ;
popup . SetValue ( Canvas . LeftProperty , SettingsPane . Edge == SettingsEdgeLocation . Right ? ( bounds . Width - width ) : 0 );
popup . SetValue ( Canvas . TopProperty , 0 );
popup . IsOpen = true ;
}
以上で、設定ポップアップを表示するだけのところまではできました。ここまで実行して動作を確認してみましょう。
アカウント設定ポップアップの作成
さて、次は図6 のようにもう少しいい感じの見た目に変更します。ポップアップの上部にヘッダーを用意し、戻るボタンを実装します。
図6 アカウント設定ポップアップ
例としてMicrosoftアカウントへサインイン・サインアウトを行うアカウント設定を作ります。Microsoftアカウントでのサインインについては前回 の内容を参照してください。
サインインUXのガイドライン
ここでサインインに関するガイドラインを確認しましょう。
SkyDriveなどMicrosoftアカウントを使ってデータにアクセスするWindowsストア アプリは、ガイドラインに従うと設定ウィンドウに「アカウント」および「プライバシーポリシー」に関する項目を設ける必要があります。「 アカウント」設定では、Microsoftアカウントへのサインイン・サインアウト機能を提供します。
サインインするタイミングは、アプリによって異なります。サインインしなければ機能しないアプリの場合は、起動時が適切です。
サインインしなくても使えるアプリの場合は、必要になった時点でサインインを行います。また、アカウント設定からもサインインを行えるようにします。
詳しくは、Microsoft アカウントのサインイン エクスペリエンスのガイドライン (JavaScript と HTML を使った Windows ストア アプリ) (Windows) を参照してください。
設定ポップアップのデザインと動作
それでは、作っていきましょう。まず設定ウィンドウのXAMLを変更します。ポップアップの左上に戻るボタンから設定ウィンドウに戻れるよう動作も実装します。
見た目の部分は、少し長いコードですがXAMLの<Page>要素内を以下のように変更します。設定ポップアップの上部のヘッダー部分とサインイン用のUIを記述しています。コードのほとんどは戻るボタンの定義です。
SettingsFlyout.xaml
<UserControl.Resources>
<Style x:Key = "SettingsBackButtonStyle" TargetType = "Button" >
< Setter Property = "MinWidth" Value = "0" />
< Setter Property = "FontFamily" Value = "Segoe UI Symbol" />
< Setter Property = "FontWeight" Value = "Normal" />
< Setter Property = "FontSize" Value = "26.66667" />
< Setter Property = "AutomationProperties.AutomationId" Value = "BackButton" />
< Setter Property = "AutomationProperties.Name" Value = "Back" />
< Setter Property = "AutomationProperties.ItemType" Value = "Navigation Button" />
< Setter Property = "Template" >
< Setter . Value >
< ControlTemplate TargetType = "Button" >
< Grid x : Name = "RootGrid" Width = "30" Height = "30" >
< Grid Margin = "-6,-6,0,0" >
< TextBlock x : Name = "BackgroundGlyph" Text = "" Foreground = "Transparent" />
< TextBlock x : Name = "NormalGlyph" Text = "{StaticResource BackButtonSnappedGlyph}" Foreground = "White" />
< TextBlock x : Name = "ArrowGlyph" Text = "" Foreground = "#00b2f0" Opacity = "0" />
</ Grid >
< Rectangle
x : Name = "FocusVisualWhite"
IsHitTestVisible = "False"
Stroke = "{StaticResource FocusVisualWhiteStrokeThemeBrush}"
StrokeEndLineCap = "Square"
StrokeDashArray = "1,1"
Opacity = "0"
StrokeDashOffset = "1.5"
/>
< Rectangle
x : Name = "FocusVisualBlack"
IsHitTestVisible = "False"
Stroke = "{StaticResource FocusVisualBlackStrokeThemeBrush}"
StrokeEndLineCap = "Square"
StrokeDashArray = "1,1"
Opacity = "0"
StrokeDashOffset = "0.5"
/>
< VisualStateManager . VisualStateGroups >
< VisualStateGroup x : Name = "CommonStates" >
< VisualState x : Name = "Normal" />
< VisualState x : Name = "PointerOver" >
< Storyboard >
< ObjectAnimationUsingKeyFrames Storyboard . TargetName = "BackgroundGlyph" Storyboard . TargetProperty = "Foreground" >
< DiscreteObjectKeyFrame KeyTime = "0" Value = "{StaticResource BackButtonPointerOverBackgroundThemeBrush}" />
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
< VisualState x : Name = "Pressed" >
< Storyboard >
< ObjectAnimationUsingKeyFrames Storyboard . TargetName = "BackgroundGlyph" Storyboard . TargetProperty = "Foreground" >
< DiscreteObjectKeyFrame KeyTime = "0" Value = "White" />
</ ObjectAnimationUsingKeyFrames >
< DoubleAnimation
Storyboard . TargetName = "ArrowGlyph"
Storyboard . TargetProperty = "Opacity"
To = "1"
Duration = "0" />
< DoubleAnimation
Storyboard . TargetName = "NormalGlyph"
Storyboard . TargetProperty = "Opacity"
To = "0"
Duration = "0" />
</ Storyboard >
</ VisualState >
< VisualState x : Name = "Disabled" >
< Storyboard >
< ObjectAnimationUsingKeyFrames Storyboard . TargetName = "RootGrid" Storyboard . TargetProperty = "Visibility" >
< DiscreteObjectKeyFrame KeyTime = "0" Value = "Collapsed" />
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
</ VisualStateGroup >
< VisualStateGroup x : Name = "FocusStates" >
< VisualState x : Name = "Focused" >
< Storyboard >
< DoubleAnimation
Storyboard . TargetName = "FocusVisualWhite"
Storyboard . TargetProperty = "Opacity"
To = "1"
Duration = "0" />
< DoubleAnimation
Storyboard . TargetName = "FocusVisualBlack"
Storyboard . TargetProperty = "Opacity"
To = "1"
Duration = "0" />
</ Storyboard >
</ VisualState >
< VisualState x : Name = "Unfocused" />
< VisualState x : Name = "PointerFocused" />
</ VisualStateGroup >
</ VisualStateManager . VisualStateGroups >
</ Grid >
</ ControlTemplate >
</ Setter . Value >
</ Setter >
</Style>
</UserControl.Resources>
<Border BorderBrush = "#00b2f0" BorderThickness = "1,0,0,0" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height = "80" />
<RowDefinition Height = "*" />
</Grid.RowDefinitions>
<Grid Background = "#00b2f0" Grid . Row = "0" >
<StackPanel Orientation = "Horizontal" Margin = "40 32 17 13" >
<Button Click = "BackButton_Click" Style = " { StaticResource SettingsBackButtonStyle } " />
<TextBlock Margin = "7 2 0 0" FontSize = "26" Text = "アカウント" Foreground = "White" />
</StackPanel>
</Grid>
<Grid Margin = "40,33,40,39" VerticalAlignment = "Top" Grid . Row = "1" >
<StackPanel x:Name = "FlyoutContent" >
<TextBlock x:Name = "MessageTextBlock" Margin = "0 24 0 0" Text = "サインインしていません" TextWrapping = "Wrap" Style = " { StaticResource BasicTextStyle } " />
<Button x:Name = "SignInButton" Margin = "0 24 0 0" Content = "サインイン" Click = "SignInButton_Click" IsEnabled = "False" />
<Button x:Name = "SignOutButton" Margin = "0 24 0 0" Content = "サインアウト" Click = "SignOutButton_Click" Visibility = "Collapsed" />
</StackPanel>
</Grid>
</Grid>
</Border>
次にプロジェクトのデフォルトでは、黒い背景に白い文字の濃色のテーマカラーとなっていますが、淡色のテーマカラーに変更しましょう。App.xamlのApplication要素に属性「RequestedTheme="Light" 」を次のように追加します(x:Class属性部分はプロジェクトによって異なります) 。
App.xaml
<Application
x:Class = "GihyoSampleApp.App"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local = "using:App1"
RequestedTheme = "Light" >
加えて、本題とは関係のない注意点です。もし、CommonフォルダーにあるStandardStyles.xamlに下記のコードがある場合は「マウス 」を「Mouse 」に修正します。これは日本語テンプレートのバグの修正です。
StandardStyles.xaml
<ToolTipService.Placement> マウス </ToolTipService.Placement>
次に、戻るボタンの動作を実装しましょう。戻るボタンのClickイベントの処理です。設定ポップアップを非表示にし、設定ウィンドウを表示します。
SettingsFlyout.xaml.cs
private void BackButton_Click ( object sender , RoutedEventArgs e )
{
Popup parent = this . Parent as Popup ;
if ( parent != null )
{
parent . IsOpen = false ;
}
if ( Windows . UI . ViewManagement . ApplicationView . Value != Windows . UI . ViewManagement . ApplicationViewState . Snapped )
{
SettingsPane . Show ();
}
}
以上で、より実用的な設定ポップアップの見た目と動作ができました。
サインイン・サインアウト処理
最後にサインイン・サインアウト処理を実装して完了です。ここからはLive
Connect APIの内容です。実装方法は多様ですが、ここでは次のようにしました。
まず、アプリのメインのコードと設定ポップアップとで認証状態を共有できるようApp.xaml.csにLiveAuthClientのstatic変数を追加します[3] 。
App.xaml.cs
static public LiveAuthClient AuthClient ;
設定ポップアップのコードは次のように編集します。サインインとサインアウト機能を実装し、サインイン状態のときはユーザー名を取得し表示します。
SettingsFlyout.xaml.cs
public SettingsFlyout ()
{
this . InitializeComponent ();
if ( App . AuthClient == null || App . AuthClient . Session == null )
{
ShowSignedOutUI ();
}
else
{
ShowSignedInUI ();
}
}
private async void SignInButton_Click ( object sender , RoutedEventArgs e )
{
if ( App . AuthClient == null )
{
App . AuthClient = new LiveAuthClient ();
}
this . SignInButton . IsEnabled = false ;
try
{
var authResult = await App . AuthClient . LoginAsync ( new string [] { "wl.basic" });
if ( authResult . Status == LiveConnectSessionStatus . Connected )
{
ShowSignedInUI ();
}
else
{
this . SignInButton . IsEnabled = true ;
}
}
catch ( LiveConnectException )
{
ShowSignedOutUI ();
}
}
private void SignOutButton_Click ( object sender , RoutedEventArgs e )
{
if ( App . AuthClient != null )
{
if (! App . AuthClient . CanLogout )
{
return ;
}
App . AuthClient . Logout ();
App . AuthClient = null ;
}
ShowSignedOutUI ();
}
private void ShowSignedOutUI ()
{
this . MessageTextBlock . Text = "サインインしていません" ;
this . SignOutButton . Visibility = Visibility . Collapsed ;
this . SignInButton . Visibility = Visibility . Visible ;
this . SignInButton . IsEnabled = true ;
}
private async void ShowSignedInUI ()
{
try
{
var connectClient = new LiveConnectClient ( App . AuthClient . Session );
LiveOperationResult opMeResult = await connectClient . GetAsync ( "me" );
dynamic meResult = opMeResult . Result ;
this . MessageTextBlock . Text = "こんにちは! " + meResult . name ;
this . SignInButton . Visibility = Visibility . Collapsed ;
if ( App . AuthClient . CanLogout )
{
this . SignOutButton . Visibility = Visibility . Visible ;
this . SignOutButton . IsEnabled = true ;
}
else
{
this . SignOutButton . Visibility = Visibility . Collapsed ;
}
}
catch ( LiveConnectException )
{
ShowSignedOutUI ();
}
}
WindowsにMicrosoftアカウントでサインインしている場合、サインアウトできません。ローカルアカウントの場合にのみサインアウトボタンが表示されます。
おわりに
今回はここまでです。いかがでしたか。Windowsストア アプリで設定チャームと連携方法を紹介しました。
Visual Studioのプロジェクトテンプレートから作成したアプリには設定チャームの実装があらかじめ記述されていませんが、アプリ認定に重要な内容です。ここで、もう少し実践的なサンプルを紹介しておきます。
日本マイクロソフトが提供している「Windows 8アプリ開発体験テンプレート 」は、設定ウィンドウ部分が実装されています。サンプルコードを利用してアプリ開発ができます。
また、設定ウィンドウ・設定ポップアップのみのサンプルコードが、デベロッパーセンター で提供されています。今回の内容はこのサンプルをよりシンプルにした形で紹介しています。
次回もまたWindowsストア アプリの開発について紹介する予定です。