UWPのPopup/Flyout/Dialog系のコントロール
UWPで何らかのポップアップのようなUI要素を表示するコントロールの使い方をまとめてみました。
Tooltip/Popup系
まずは、そんなに使用頻度高くないかもしれないけど、ちょっとしたコントロールから。
ToolTip
これは、WPFなどでもあった要素ですね。カーソル乗ったときにポコっと表示してくれるもの。
<Button Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Button" ToolTipService.ToolTip="ボタンの説明" />
使い方も、WPFのものとほぼ同じ感じ。
Buttonなどの各コントロールに直接ToolTipプロパティは用意されてないので、必ずToolTipServiceクラスの添付プロパティを使って設定するのが違いかな。
Popup
この要素は積極的に使うようなコントロールではないです。
ToolTipやFlyout、MessageDialogなど、ほかのコントロールで事足りるときにはそちらのコントロールを使うべきですね。
てことで省略。
PopupMenu
こんな風に、右クリック時に出てくるメニューを作ったりするのに使ってたコントロール。
Win8系ではフライアウト メニューの表示などでも使ってたみたい。
ただし、このコントロールはXAML上で定義できず、コードビハインドで表示内容を定義しなければいけないので、使い勝手は微妙。。。
Win8.1でFlyoutMenuというコントロールが追加されたので、基本的にはFlyoutMenuを使うことになるかと思います。
private async void Button_Click(object sender, RoutedEventArgs e) { var menu = new PopupMenu(); menu.Commands.Add(new UICommand("menu1")); menu.Commands.Add(new UICommand("menu2")); var selected = await menu.ShowForSelectionAsync(GetElementRect((FrameworkElement)sender)); } public static Rect GetElementRect(FrameworkElement element) { var buttonTransform = element.TransformToVisual(null); var point = buttonTransform.TransformPoint(new Point()); return new Rect(point, new Size(element.ActualWidth, element.ActualHeight)); }
Flyout系
Win8.1からあるFlyout系のコントロール。
表示する内容をXAML上で定義できるので、PopupMenuなどより使い勝手がいいです。
任意のXAML要素をポップアップ表示したい場合はFlyoutコントロール。メニュー的な表示をしたい場合にはMenuFlyoutでだいたいの用途には事足りそうです。
Flyout/MenuFlyout
まとめて使ってみます。
ボタン押下時にFlyoutを表示する。
ButtonなどのFlyoutプロパティを持つコントロールでは、このプロパティにFlyout系コントロールを設定するだけで表示することができます。
ボタンを押したらメニューを出す、というような場合にはこの方法を使います。
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Flyoutの表示"> <Button.Flyout> <Flyout> <StackPanel> <TextBlock Text="hoge" /> <Button Content="ボタン"/> </StackPanel> </Flyout> </Button.Flyout> </Button> <Button Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" Content="FlyoutMenuの表示"> <Button.Flyout> <MenuFlyout> <MenuFlyoutItem Text="メニュー1" /> </MenuFlyout> </Button.Flyout> </Button> </StackPanel>
任意のタイミングでFlyoutを表示する
続いて、ボタン押下のタイミングではなく、その他任意のタイミングでフライアウトを表示したい場合の実装方法です。
FlyoutBase.AttachedFlyoutという添付プロパティがあるので、フライアウトを出したいコントロールにこの添付プロパティを設定し、この中でFlyoutの定義をします。
そして、コードビハインドから以下のように呼び出します。
<Rectangle Width="100" Height="50" Margin="75" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="Gray" DoubleTapped="Rectangle_DoubleTapped"> <FlyoutBase.AttachedFlyout> <MenuFlyout> <MenuFlyoutItem Text="メニュー1" /> <MenuFlyoutItem Text="メニュー2" /> <MenuFlyoutItem Text="メニュー3" /> </MenuFlyout> </FlyoutBase.AttachedFlyout> </Rectangle>
private void Rectangle_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) { Flyout.ShowAttachedFlyout(sender as FrameworkElement); }
↓こんな使い方するメリットはないかと思いますが、ダブルタップするとフライアウトを表示します。
Flyoutコントロールに名前を付けて直接制御
Flyoutコントロールに名前を付けておけば、コードビハインドから直接操作することができます。
以下のように、ShowAt/Hideメソッドで表示/非表示を切り替えられます。
(Flyoutは、どこか別の要素をクリックしてフォーカス外れると自動で消えるので、この例ではわざわざHideメソッド用意する必要ないですが。)
<StackPanel Margin="75" Orientation="Horizontal"> <Rectangle x:Name="rect1" Width="100" Height="50" Fill="Gray"> <FlyoutBase.AttachedFlyout> <MenuFlyout x:Name="flyout1"> <MenuFlyoutItem Text="メニュー1" /> <MenuFlyoutItem Text="メニュー2" /> <MenuFlyoutItem Text="メニュー3" /> </MenuFlyout> </FlyoutBase.AttachedFlyout> </Rectangle> <Button Margin="10" Click="ShowFlyout" Content="Show" /> <Button Margin="10" Click="HideFlyout" Content="Hide" /> </StackPanel>
private void ShowFlyout(object sender, RoutedEventArgs e) { this.flyout1.ShowAt(this.rect1); } private void HideFlyout(object sender, RoutedEventArgs e) { this.flyout1.Hide(); }
Dialog系
最後はダイアログ系の表示について。
MessageDialog
まずはダイアログの基本のMessageDialogクラス。
WPFでMessageBox使ってたような感覚で、こんな風にコードビハインドから呼び出したりできます。
(MVVMとかを意識してコーディングするときには、こういうのを安易に仕込むとビューとロジックが入り乱れるきっかけになっちゃったりするんで注意は必要かと思いますが。)
以下のように、インスタンスを作ってから、ShowAsyncメソッドで表示します。
UIの応答を待つメソッドになるので、Asyncな呼び出し方だけ用意されてるあたりが今風ですね。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var dlg = new MessageDialog("MessageDialogのサンプル", "タイトル"); await dlg.ShowAsync(); }
ボタンを追加する
CommandsプロパティにUICommandのインスタンスを追加していくと、ダイアログに表示するボタンをカスタマイズすることができます。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var dlg = new MessageDialog("MessageDialogのサンプル", "タイトル"); dlg.Commands.Add(new UICommand("はい")); dlg.Commands.Add(new UICommand("いいえ")); await dlg.ShowAsync(); }
デフォルト、キャンセル用のボタンを設定する
DefaultCommandIndex/CancelCommandIndexというプロパティで、ダイアログのデフォルト/キャンセル用のボタン設定を行うことができます。
ボタンは、Commandプロパティに追加した順に0から始まるインデックス値で指定します。
DefaultCommandIndexに設定すると、下のキャプチャ画像のようにボタンの色が変わります。
CancelCommandIndexを設定すると、ダイアログ表示時にESCキーを押したりしたときに、このボタンが選択されるようになります。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var dlg = new MessageDialog("MessageDialogのサンプル", "タイトル"); dlg.Commands.Add(new UICommand("ボタン1")); dlg.Commands.Add(new UICommand("デフォルト用")); dlg.Commands.Add(new UICommand("キャンセル用")); dlg.DefaultCommandIndex = 1; dlg.CancelCommandIndex = 2; var result = await dlg.ShowAsync(); System.Diagnostics.Debug.WriteLine(result.Label); }
ShowAsyncメソッドの戻り値
ダイアログ表示で使うShowAsyncメソッドは以下のような定義となっていて、戻り値として選択されたボタンのUIComamandのインスタンスが取得できるようになっています。
public IAsyncOperation<IUICommand> ShowAsync();
そこで、
UICommandのコンストラクタの第3引数には、コマンドのIDとしての値をobject型で任意の値を設定することができます。
ShowAsyncの戻り値でこのIDをチェックすれば、どちらのボタンが押されたかチェックできます。
以下の例では、IDにそれぞれtrue/falseを入れてチェックしてます。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var dlg = new MessageDialog("MessageDialogのサンプル", "タイトル"); dlg.Commands.Add(new UICommand("はい", null, true)); dlg.Commands.Add(new UICommand("いいえ", null, false)); var selectedCommand = await dlg.ShowAsync(); var result = (bool)selectedCommand.Id; if(result) { // 「はい」が選択された場合の動作 System.Diagnostics.Debug.WriteLine(selectedCommand.Label); } }
(↓のサンプルでは、デバッグ出力にログを吐いてるだけなので、実行時には特に変化ありませんが。。。)
各ボタン押下時のイベント処理を定義する
UICommandのコンストラクタでは、コマンドが選択されたときの処理を定義することもできます。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var dlg = new MessageDialog("MessageDialogのサンプル", "タイトル"); dlg.Commands.Add(new UICommand("はい", (cmd) => { System.Diagnostics.Debug.WriteLine("はい"); })); dlg.Commands.Add(new UICommand("いいえ", (cmd) => { System.Diagnostics.Debug.WriteLine("いいえ"); })); await dlg.ShowAsync(); }
MessageDialogのプロパティまとめ
MessageDialogで使う主なプロパティは以下の通り。
プロパティ名 | 内容 |
---|---|
Title | ダイアログのタイトルを設定。コンストラクタで指定するものと同じ |
Content | ダイアログの本文を設定。コンストラクタで指定するものと同じ |
Options | ダイアログボックスのオプションをMessageDialogOptions型で指定。あんま使わなそう。 |
Commands | ボタンを追加したい場合には、ここにUICommandのインスタンスをAddしていく。 |
DefaultCommandIndex | デフォルトのボタンにする要素をインデックス値で指定 |
CancelCommandIndex | キャンセル用のボタンにする要素をインデックス値で指定 |
これらを設定してからShowAsyncで表示、ということだけ覚えれば、なんとなく使えるかと思います。
ContentDialog
ダイアログ系その2
こちらはダイアログの描画内容をカスタマイズできるタイプのものです。
ダイアログの表示内容をXAMLで定義できるので、複雑な情報を表示したり、いろんなUIコントロールを張り付けて置いたりと、いろいろ使えます。
表示内容は、以下の二通りの方法で定義することができます。
- 呼び出し元のページ上に直接定義
- 別ファイルに独立したXAMLファイルとして定義
元のページ上に定義
まずは、表示内容を呼び出し元のPage上に定義する方法から。
以下のようにPage内に直接ContentDialog要素を定義します。
ですが、このコントロールは普段は表示されておらず、ContentDialogクラスのShowAsyncメソッドを呼び出すことで表示されます。
・MainWindow.xaml
<Page x:Class="LayoutTest.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:local="using:LayoutTest" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ContentDialog x:Name="dlg1" Title="タイトル" IsPrimaryButtonEnabled="True" IsSecondaryButtonEnabled="True" PrimaryButtonText="OK" SecondaryButtonText="Cancel"> <Grid HorizontalAlignment="Stretch" Width="Auto"> <TextBlock Text="ContentDialogのテスト" /> </Grid> </ContentDialog> <Button Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click_1" Content="ダイアログを表示" /> </Grid> </Page>
・MainWindow.xaml.cs
private async void Button_Click_1(object sender, RoutedEventArgs e) { await this.dlg1.ShowAsync(); }
また、このShowAsyncメソッドでは、ダイアログでどのボタンを押したか、という情報をContentDialogResult型で取得できます。
以下のようにして、ダイアログでどのボタンが押されたかを判断することができます。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var result = await this.dlg1.ShowAsync(); if (result == ContentDialogResult.Primary) { System.Diagnostics.Debug.WriteLine("Primary"); } else if (result == ContentDialogResult.Secondary) { System.Diagnostics.Debug.WriteLine("Secondary"); } else { System.Diagnostics.Debug.WriteLine("None"); } }
別のXAMLファイル上に定義
ContentDialogの表示内容は別ファイルに定義することもできます。
メニューの追加⇒新しい項目のダイアログで、「コンテンツダイアログ」を選びます。
で、プロジェクトに追加されたXAMLファイルを編集します。
<ContentDialog x:Class="LayoutTest.SampleContentDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:LayoutTest" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="TITLE" PrimaryButtonText="Button1" SecondaryButtonText="Button2" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" SecondaryButtonClick="ContentDialog_SecondaryButtonClick"> <Grid> <TextBlock Text="別ファイルに定義したContentDialog" /> </Grid> </ContentDialog>
表示方法は、ほぼ同じ感じですね。
private async void Button_Click_1(object sender, RoutedEventArgs e) { var dlg = new SampleContentDialog(); await dlg.ShowAsync(); }
ContentDialogのプロパティ
最後に主なプロパティを一通りまとめておきます。
プロパティ名 | 内容 |
---|---|
Title | ダイアログのタイトルを設定 |
FullSizeDesired | Trueにすると、ダイアログをフルスクリーンで表示します |
IsPrimaryButtonEnabled | PrimaryButtonの表示/非表示を設定 |
PrimaryButtonText | PrimaryButtonの表示文字列を設定 |
PrimaryButtonCommand | PrimaryButton押下時に実行するコマンドを設定 |
PrimaryButtonCommandParameter | ↑のコマンドに渡すパラメータを設定 |
IsSecondaryButtonEnabled | SecondaryButtonの表示/非表示を設定 |
SecondaryButtonText | SecondaryButtonの表示文字列を設定 |
SecondaryButtonCommand | SecondaryButton押下時に実行するコマンドを設定 |
SecondaryButtonCommandParameter | ↑のコマンドに渡すパラメータを設定 |
TitleTemplate | Titleの表示方法をカスタマイズするためのDataTemplateを設定 |