こんな風に、LightBox的なダイアログ表示を行うライブラリを作ってみました。
概要
WPFでLightBox的なダイアログ表示を行うライブラリです。
ダイアログは、別ウィンドウではなく、呼び出し元ウィンドウ上にAdornerとして重ねて表示します。
コードビハインドからMessageBoxを呼ぶのと同じ感覚で、イイ感じのLightBox表示ができます。
LightBox.Show(this, new SampleDialog());
ざっくり特徴をまとめると以下のような感じ。
- WindowのXAMLコードには手をいれずに、元のウィンドウ上にLightBoxを重ねて表示できる。
- モーダル/モードレスどちらの表示にも対応
- Show/ShowDialog/ShowAsyncの3種類の呼び出し方法
- LightBoxの中身には、任意のFrameworkElement派生クラスの要素を表示できる。
- ImageコントロールのようなPrimitiveな型や、独自に作成したUserControlなどを表示可能。
- アニメーション対応
- LightBoxを表示するとき/閉じるときにアニメーションを付けることができます。
- XAMLで各種デザインをカスタマイズ可能
- 複数ダイアログの同時表示
コードは短めですが、割とカスタマイズ性の高いライブラリになっているのでは、と思います。
使い方
インストール
Nugetからの以下のコマンドでインストールできます。
Install-Package Lighty
また、GUIからでもLighty
と検索すれば見つかります。
コイツをインストールすればOK!!
準備
lightyの機能を、c#コードから利用する場合には、以下の名前空間のusingを追加しておきます。
using SourceChord.Lighty;
また、xaml側でlightbox表示のカスタマイズをしたい場合には、このxml名前空間を使います。
xmlns:lighty="clr-namespace:SourceChord.Lighty;assembly=Lighty"
lightbox表示
lightyを使ってポップアップ表示を行うには、コードビハインドから以下のコードを呼び出します。
(SampleDialogは任意のユーザーコントロール)
LightBox.Show(this, new SampleDialog());
すると、このようにウィンドウ全体が暗くなって、ダイアログが表示されます。
第一引数には、LightBox表示を行う領域。
第二引数には、表示するコンテンツを指定します。
ウィンドウ全体に表示したい場合には、上記の例のようにコードビハインドから呼び出す際にthisを指定すればOKです。
モーダル/モードレス/Asyncな表示
ダイアログ表示用に、以下の3種類のメソッドを用意しています。
Method Name | Description |
---|---|
Show | LightBoxをモードレス表示します。 |
ShowDialog | LightBoxをモーダル表示します。すべてのLightBoxを閉じるまで、後続のコードは実行しません。 |
ShowAsync | Show()メソッドのAsyncバージョン。asyncメソッド内でawaitと併用して使うことができます。 |
それぞれ、こんな風に呼び出すことができます。
private async void OnClickButton(object sender, RoutedEventArgs e) { // modeless dialog LightBox.Show(this, new SampleDialog()); // async method await LightBox.ShowAsync(this, new SampleDialog()); // modal dialog LightBox.ShowDialog(this, new SampleDialog()); }
表示内容の指定
Showメソッドなどの第二引数には、FrameworkElement派生の任意のオブジェクトを指定できます。
usercontrolをコンテンツとして表示
LightBox.Show(this, new SampleDialog());
任意のエレメントを表示
var image = new Image(); image.Source = new BitmapImage(new Uri("Images/1.jpg", UriKind.Relative)); LightBox.Show(this, image);
任意の領域への表示
Showメソッドなどの第一引数には、XAMLのロジカルツリー上の任意の要素を指定できます。
こんな風に、任意のgridなどを指定して、その要素上にLightBox表示ができます。
<Window x:Class="LightySample.Window1"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:lighty="clr-namespace:SourceChord.Lighty;assembly=Lighty"xmlns:local="clr-namespace:LightySample"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Title="Window1"Width="500"Height="300"mc:Ignorable="d"><Grid><Button x:Name="button"Width="75"Margin="10,10,0,0"HorizontalAlignment="Left"VerticalAlignment="Top"Click="button_Click"Content="Button" /><Grid x:Name="subGrid"Width="300"Height="200"Margin="10"HorizontalAlignment="Right"VerticalAlignment="Bottom" /></Grid></Window>
privatevoid button_Click(object sender, RoutedEventArgs e) { LightBox.Show(this.subGrid, new SampleDialog()); }
ダイアログの閉じ方
ダイアログを閉じるには以下のような方法があります。
ApplicationCommandを利用
WPFの定義済みルーティングコマンドApplicationCommands.Close
を実行すると、そのコマンドを発行した要素を含むダイアログが閉じられます。
<Button Width="75"Margin="10"HorizontalAlignment="Center"VerticalAlignment="Bottom"Command="ApplicationCommands.Close"Content="Close" />
コードビハインドから閉じる
ユーザーコントロールを表示している場合、ボタンクリック時にダイアログを閉じたい、というような場合には、以下のようにCloseメソッドを呼び出します。
LightyでFrameworkElementにCloseという拡張メソッドを定義しているので、using SourceChord.Lighty;
と名前空間を設定していれば、Close()拡張メソッドでダイアログを閉じることができます。
privatevoid Button_Click(object sender, RoutedEventArgs e) { // Lightyで定義している、ダイアログクローズ用の拡張メソッド// ※「using SourceChord.Lighty;」が必要this.Close(); }
背景領域のクリック
デフォルトの状態だと、LightBox表示の背景部分をクリックすると、すべてのダイアログ表示を閉じるようになっています。
(後述するCloseOnClickBackgroundプロパティでカスタマイズ可能です。)
MVVMとの連携
MVVM的にVMから表示する方法とかは?って話ですが、その手のMVVMとの連携は用意していません。
ダイアログ表示のような動作は完全にView側の責務だと思うので、VMからライトボックス表示をさせる方法を提供するのは、レイヤーが異なる話になるかな、と。
通常のWindowをShow/ShowDialogするのと同じ感じで表示できるので、MVVMな開発スタイルと併用したい場合は、自分が使ってるMVVMフレームワークの作法に合わせて、LightyのShow/ShowDialog/ShowAsyncメソッドを呼び出してもらえばよいかと思います。
応用編
表示のカスタマイズ
LightyではXAMLの機能をフルに使って、表示方法を自由にカスタマイズできます。
表示方法は、WindowのリソースとしてLightBoxクラスのデフォルトスタイルを設定することでカスタマイズできます。
(Windowのリソースで定義すれば、ウィンドウ単位でのスタイルの設定になり、App.xamlで定義すればアプリケーション全体の設定として定義することができます。)
Property | Description |
---|---|
Template | LightBoxとして表示する領域全体の定義。LightBoxの背景部分などはここでカスタマイズします。 |
ItemsPanel | 各ダイアログを格納するItemsPanelを設定します。StackPanelなどを併用することで、複数ダイアログを並べて表示することもできます。 |
ItemContainerStyle | 各ダイアログを格納するコンテナのスタイルを設定します。ダイアログに共通のデザインを適用したい場合などは、ここで設定できます。 |
CloseOnClickBackground | LightBoxの背景領域をクリックした際に、すべてのダイアログを閉じるか否かを設定します。(デフォルトはTrue) |
こんな風にカスタマイズできます。
<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="Template"><Setter.Value><ControlTemplate><Grid Background="#88F5F5DC"><ItemsPresenter /></Grid></ControlTemplate></Setter.Value></Setter></style><Setter Property="CloseOnClickBackground"Value="False" /></Window.Resources>
ビルトイン スタイル
いくつかのデザインやアニメーションを、ライブラリ側で標準で定義しています。
これらのビルトインスタイルを使えばお手軽に表示方法をカスタマイズできます。
準備
ビルトインスタイルを使うには、まずApp.xamlで以下のリソースディクショナリを読み込んでおきます。
<ResourceDictionary Source="pack://application:,,,/Lighty;component/Styles.xaml" />
するとLightBoxのデフォルトスタイルを設定する際に、StaticResourceでビルトインスタイルを指定できるようになります。
<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="Template"Value="{StaticResource DarkGlassTemplate}" /><Setter Property="ItemsPanel"Value="{StaticResource HorizontalPanel}" /><Setter Property="ItemContainerStyle"Value="{StaticResource PhotoCardStyle}" /></Style></Window.Resources>
詳細は割愛しますが、現時点では以下のようなスタイル類を定義しています。
Property | Name |
---|---|
Template | DarkBackgroundTemplate |
LightBackgroundTemplate | |
DarkGlassTemplate | |
LightGlassTemplate | |
ItemsPanel | HorizontalPanel |
VerticalPanel | |
ItemContainerStyle | ClosableContainerStyle |
PhotoCardStyle | |
Animations | FadeInAnimation |
FadeOutAnimation | |
ZoomInAnimation | |
ZoomOutAnimation |
※ビルトインスタイルは、今後もう少し手を入れたり追加したりしていこうかと思ってます。
個人的におすすめのスタイルは、「DarkGlassTemplate/LightGlassTemplate」
iOS風のポップアップ表示みたいに背景に強めのブラーをかけてからダイアログ表示をします。
以前↓で書いた方法を併用して、ウィンドウ全体に強烈なぼかしをかけても、パフォーマンスが落ちないように工夫してます。
アニメーション
LightBoxを表示したり閉じたりする際のアニメーションもカスタマイズ可能です。
以下のようなプロパティを用意しています。
Property | Description |
---|---|
InitializeStoryboard | LightBoxの背景部分を最初に表示する際のアニメーション |
DisposeStoryboard | LightBoxの背景部分を消す際のアニメーション |
OpenStoryboard | LightBoxとして各ダイアログを表示する際のアニメーション |
CloseStoryboard | LightBoxとして各ダイアログを閉じる際のアニメーション |
こんな風に設定をします。
<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="InitializeStoryboard"Value="{StaticResource ZoomInAnimation}" /><Setter Property="DisposeStoryboard"Value="{StaticResource ZoomOutAnimation}" /><Setter Property="OpenStoryboard"Value="{StaticResource ZoomInAnimation}" /><Setter Property="CloseStoryboard"Value="{StaticResource ZoomOutAnimation}" /></Style></Window.Resources>
アニメーションの並列実行
デフォルトでは以下のアニメーションは、シーケンシャルに順次実行します。
- InitializeStoryboard⇒OpenStoryboard
- CloseStoryboard⇒DisposeStoryboard
(背景を暗くして、そのアニメーションが終わったら、各ダイアログを表示するためのアニメーションを開始する、、など。)
これらを並列で同時に実行したい場合には以下のプロパティを設定します。
Property | Description |
---|---|
IsParallelInitialize | InitializeStoryboard & OpenStoryboardを並列に実行するか否かを指定します。(デフォルトはFalse) |
IsParallelDispose | CloseStoryboard & DisposeStoryboardを並列に実行するか否かを指定します。(デフォルトはFalse) |
複数ダイアログの同時表示
Lightyでは、複数のダイアログを同時表示することもできます。
以下のようにItemsPanelをStackPanelなどにしておき、Showメソッドを複数回呼び出すと複数ダイアログの表示をすることができます。
<Window.Resources><Style TargetType="{x:Type lighty:LightBox}"><Setter Property="ItemsPanel"Value="{StaticResource HorizontalPanel}" /></Style></Window.Resources>
private async void button_Click(object sender, RoutedEventArgs e) { LightBox.Show(this, new SampleDialog()); await Task.Delay(1000); LightBox.Show(this, new SampleDialog()); await Task.Delay(1000); LightBox.Show(this, new SampleDialog()); }
その他
MahApps.Metroなどのライブラリと組み合わせて使うこともできます。
↓こんな感じ。
こういうライブラリと組み合わせることで、お手軽にかっこいいUIを作れるのでは、と思います。