前回記事「FolderBrowserDialog の為ならば、Prism の InteractionRequest はもしかしたらコモンダイアログも Popup できるかもしれない。【episode: 15 WPF Prism】」
前回の episode: 15 で WPF Prism episode シリーズは一旦完結と書きましたが、Prism 7.2 が 2019/7/25 未明にリリースされ episode: 12 ~ 15 で紹介したメッセージボックスやダイアログウィンドウを表示する PopupWindowAction に変わって IDialogService が新規に追加され、ダイアログウィンドウの表示方法が大きく変わったので Prism 7.2 の新機能も紹介することにしました。
尚、この記事は Visual Studio 2019 Community Edition で .NET Framework 4.8 以上 と C# + Prism 7.2 + ReactiveProperty を使用して 、WPF アプリケーションを MVVM パターンで作成するのが目的で、C# での基本的なコーディング知識を持っている人が対象です。
Prism 7.2 の新機能 Prism 7.1 まではメッセージボックスやダイアログウィンドウ(別ウィンドウ)の表示に InteractionRequest と PopupWindowAction を使用していましたが、Prism 7.2 以降では IDialogService を使用するよう変更されました。 そして IDialogService の追加に併せて InteractionRequest と PopupWindowAction は Obsolete でマーキングされコンパイル時に警告が表示されるようになったため「廃止(非推奨)」になりました 。
InteractionRequest と PopupWindowAction の「廃止」と IDialogService の導入で Prism の INotification、IConfirmation から表示していた Prism 内臓のメッセージボックスも「廃止」されたためメッセージボックス等は個々で作成しなければならなくなりました。
fig.1 Prism 7.1 まで含まれていた確認メッセージボックス
確かに fig. 1 のように Prism 組み込みのメッセージボックスはイケてるとは言えないでしょうし、Prism メンテナーの Brian Lagunas もリリースノートで以下のように述べています。
The idea here is that Prism will no longer provide any built-in dialogs like Notification or Confirmation. Mainly because the Prism implementations are UGLY and will never match the styling of your beautiful WPF application. So, it’s important that you are able to register your own dialogs.
【機械翻訳】 ここでの考えは、Prism が通知や確認のような組み込みのダイアログをもう提供しないということです。主な理由は、Prism の実装は醜く、あなたの美しい WPF アプリケーションのスタイルとは決して一致しないからです。だから、あなたはあなた自身のダイアログを登録できることが重要です。Prism 7.2.0.1367 リリースノート
以降では Prism 7.2 から追加された IDialogService を使用してダイアログウィンドウ(メッセーボックス)を表示する方法を紹介します。
Prism 7.2 の IDialogService からメッセージボックスを表示する。 このエントリでは、今まで紹介してきたサンプルアプリではなく Prism Template Pack から新規の Prism Blank App と Prism Module を 1 つずつ作成して新規ソリューションを作成します。 新規ソリューションの作成については episode: 3 で紹介しているのでそちらを見てください。
今回の Prism 7.2 リリースに Prism Template Pack のアップデートは含まれない ようなので episode :3 で紹介した手順の『Nuget パッケージの復元』実行後、『ソリューションの Nuget パッケージの管理』から Prism 7.2 へ手動でアップデートしないと IDialogService が含まれる Prism 7.2 は使用できない ので注意してください。
今回作成した Module プロジェクトは、どんなプロジェクトでも汎用的に使用できるメッセージボックス Module としたい ので別プロジェクトとして作成します。
メッセージボックス用の View を作成する 作成した Module プロジェクトに自動で作成される ViewA は削除して、新たに追加した Prism UserControl(WPF)をメッセージボックス用の View として scr. 1 のように作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<UserControl x: Class = "WpfPrism72.Views.ConfirmedMessageBox"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns: x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns: prism = "http://prismlibrary.com/"
prism: ViewModelLocator. AutoWireViewModel = "True"
Width = "300" Height = "150" >
<Grid Margin = "10, 10, 10, 15" >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height = "Auto" />
</Grid.RowDefinitions>
<TextBlock Grid. Row = "0"
HorizontalAlignment = "Stretch"
VerticalAlignment = "Stretch"
TextWrapping = "Wrap"
Text = "{Binding Message.Value}" />
<StackPanel Grid. Row = "1"
Orientation = "Horizontal"
HorizontalAlignment = "Right" >
<Button Content = "はい"
Margin = "0, 10, 10, 0"
Width = "75" Height = "25"
Command = "{Binding YesCommand}" />
<Button Content = "いいえ"
Margin = "0, 10, 0, 0"
Width = "75" Height = "25"
Command = "{Binding NoCommand}" />
</StackPanel>
</Grid>
</UserControl>
src. 1 の XAML は余白等を管理人好みに調整していますが、メッセージ表示用の TextBlock と、はい・いいえボタンを 1 つずつ配置しただけの何の変哲もない View です。
scr. 2 はメッセージボックス View 用の VM です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System ;
using Prism . Mvvm ;
using Prism . Services . Dialogs ;
using Reactive . Bindings ;
namespace WpfPrism72 . ViewModels
{
/// <summary>通知メッセージボックスのVMを表します。</summary>
public class ConfirmedMessageBoxViewModel : BindableBase , IDialogAware
{
/// <summary>メッセージボックスのタイトルを取得します。</summary>
public string Title = > "メッセージボックスTEST" ;
/// <summary>ダイアログのCloseを要求するAction。</summary>
public event Action < IDialogResult > RequestClose ;
/// <summary>ダイアログがClose可能かを取得します。</summary>
/// <returns></returns>
public bool CanCloseDialog ( ) { return true ; }
/// <summary>ダイアログClose時のイベントハンドラ。</summary>
public void OnDialogClosed ( ) { }
/// <summary>ダイアログOpen時のイベントハンドラ。</summary>
/// <param name="parameters">IDialogServiceに設定されたパラメータを表すIDialogParameters。</param>
public void OnDialogOpened ( IDialogParameters parameters ) { }
public ConfirmedMessageBoxViewModel ( ) { }
}
}
Prism 7.2 以降のメッセージボックスやダイアログ用に作成する VM は 9 行目のように Prism.Services.Dialogs.IDialogAware を継承する必要があります 。
そして src. 3 の 18 行目のようにダイアログ用の View であることを Prism へ登録するために IModule や PrismApplication の RegisterTypes で Prism 7.2 から追加された RegisterDialog を View と VM の Type を指定して呼び出します 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using Prism . Ioc ;
using Prism . Modularity ;
using WpfPrism72 . Views ;
namespace WpfPrism72
{
/// <summary>メッセーボックスモジュールを表します。</summary>
public class MessageBoxLibModule : IModule
{
/// <summary>モジュールを初期化します。</summary>
/// <param name="containerProvider"></param>
public void OnInitialized ( IContainerProvider containerProvider ) { }
/// <summary>DIコンテナへTypeを登録します。</summary>
/// <param name="containerRegistry">登録用のDIコンテナを表すIContainerRegistry</param>
public void RegisterTypes ( IContainerRegistry containerRegistry )
{
containerRegistry . RegisterDialog < ConfirmedMessageBox , ViewModels . ConfirmedMessageBoxViewModel > ( ) ;
}
}
}
このサンプルはメッセージボックスを別プロジェクトに分けているので、IModule.RegisterTypes で RegisterDialog を呼び出していますが、Shell にダイアログ用の View を作成した場合は PrismApplication.RegisterTypes へ記述します。
IDialogService からダイアログウィンドウを呼び出す そして作成したダイアログを呼び出すには src. 4 のように IDialogService.ShowDialog メソッドを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using Prism . Mvvm ;
using Prism . Services . Dialogs ;
using Reactive . Bindings ;
using WpfPrism72 . Extensions ;
namespace WpfPrism72 . ViewModels
{
/// <summary>ShellのViewModelを表します。</summary>
public class MainWindowViewModel : BindableBase
{
/// <summary>画面のタイトルを取得・設定します。</summary>
private string _title = "Prism Application" ;
public string Title
{
get { return _title ; }
set { SetProperty ( ref _title , value ) ; }
}
/// <summary>OKボタンのCommandを取得します。</summary>
public ReactiveCommand ShowMessageButtonCommand { get ; }
/// <summary>OKボタンのみのメッセージボックスを表示します。</summary>
/// <param name="message">表示するメッセージを表す文字列。</param>
/// <returns>メッセージボックスの戻り値を表すButtonResult列挙型の内の1つ。</returns>
private ButtonResult showInformationMessage ( string message )
{
var ret = ButtonResult . No ;
this . dlgService . ShowDialog ( "ConfirmedMessageBox" , null , r = > ret = r . Result ) ;
return ret ;
}
/// <summary>OKボタンのClickイベントハンドラ。</summary>
private void onShowMessageButtonCommand ( )
{
this . dlgService . ShowInformationMessage ( string . Empty ) ;
}
/// <summary>ダイアログ表示サービスを表します。</summary>
private IDialogService dlgService = null ;
/// <summary>コンストラクタ。</summary>
/// <param name="dialogService">Prismのダイアログサービスを表すIDialogService。</param>
public MainWindowViewModel ( IDialogService dialogService )
{
this . dlgService = dialogService ;
this . ShowMessageButtonCommand = new ReactiveCommand ( )
. WithSubscribe ( this . onShowMessageButtonCommand ) ;
}
}
}
ダイアログを表示するための IDialogService は 44 行目のように Shell のコンストラクタへ DI コンテナからインジェクションしてもらいます。 ※ ここでは【ダイアログ表示ボタン】の処理に ReactiveCommand を使用していますが、Prism の DelegateCommand を使用した場合も変わりません。
インジェクションされた IDialogService の ShowDialog メソッドへ表示するダイアログ View のクラス名(第 1 パラメータ)、ダイアログへ渡すパラメータ(第 2 パラメータ)、ShowDialog の戻り値を処理するコールバック(第 3 パラメータ)を指定して呼び出すと fig. 2 のようにメッセージボックスが表示されます 。
fig. 2 メッセージボックスの表示
ShowDialog メソッドの第 1 パラメータは仮引数名が【name】なので最初は何を指定すれば良いか分からず管理人は 10 分程悩みましたが、src. 3 で RegisterDialog する際に指定した View 名を文字列で指定 します。 IDialogService からは複数のダイアログを表示することができる ので、どのダイアログを表示したいか指定しないと IDialogService 側では分からないので指定するのは当然ですね。
ShowDialog メソッドは episode: 12 で紹介した InteractionRequest<INotification>.Raise メソッドと似た構文 なので、以前に InteractionRequest を使用したことがあれば悩むことは無いと思います。
ここでは ShowDialog メソッドの第 2 パラメータへ null を指定していますし、メッセージボックスのボタンに何の処理も記述していないので空のメッセージボックスが表示されるだけですが、Prism 7.1 までの InteractionRequest と PopupWindowAction を組み合わせる方法と比べて非常にシンプルな設計で作成されています。
ダイアログを制御する IDialogAware 作成したダイアログをメッセーボックスとして使用するためにはダイアログ用の VM が継承している IDialogAware へ src. 5 でハイライトした部分を追加します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System ;
using Prism . Mvvm ;
using Prism . Services . Dialogs ;
using Reactive . Bindings ;
namespace WpfPrism72 . ViewModels
{
/// <summary>問い合わせメッセージボックスのVMを表します。</summary>
public class ConfirmedMessageBoxViewModel : BindableBase , IDialogAware
{
/// <summary>メッセージボックスのタイトルを取得します。</summary>
public string Title = > "メッセージボックスTEST" ;
/// <summary>メッセージボックスへ表示する文字列を取得します。</summary>
public ReactivePropertySlim < string > Message { get ; }
/// <summary>はいボタンのCommandを取得します。</summary>
public ReactiveCommand YesCommand { get ; } = new ReactiveCommand ( ) ;
/// <summary>いいえボタンのCommandを取得します。</summary>
public ReactiveCommand NoCommand { get ; } = new ReactiveCommand ( ) ;
/// <summary>ダイアログのCloseを要求するAction。</summary>
public event Action < IDialogResult > RequestClose ;
/// <summary>ダイアログがClose可能かを取得します。</summary>
/// <returns></returns>
public bool CanCloseDialog ( ) { return true ; }
/// <summary>ダイアログClose時のイベントハンドラ。</summary>
public void OnDialogClosed ( ) { }
/// <summary>ダイアログOpen時のイベントハンドラ。</summary>
/// <param name="parameters">IDialogServiceに設定されたパラメータを表すIDialogParameters。</param>
public void OnDialogOpened ( IDialogParameters parameters )
{
this . Message . Value = parameters . GetValue < string > ( "Message" ) ;
}
public ConfirmedMessageBoxViewModel ( )
{
this . Message = new ReactivePropertySlim < string > ( string . Empty ) ;
this . YesCommand . Subscribe ( ( ) = > this . RequestClose ? . Invoke ( new DialogResult ( ButtonResult . Yes ) ) ) ;
this . NoCommand . Subscribe ( ( ) = > this . RequestClose ? . Invoke ( new DialogResult ( ButtonResult . No ) ) ) ;
}
}
}
問い合わせメッセーボックスに必要なのはパラメータで渡されるメッセージ文字列の表示と、はい・いいえボタンの Command を処理するだけなので、OnDialogOpened イベントで表示メッセージの設定と、両ボタンの Command で RequestClose を呼び出す処理を追加します。 これは問い合わせメッセーボックスだけでなく、通知メッセーボックスや複雑なダイアログウィンドウを表示する場合も記述は変わりません。 ※ src. 5 ではデータバインディングを ReactiveProperty で処理していますが、Prism の BindableBase を使用する場合も同じように記述できます。
拡張メソッドを追加してダイアログを表示する 又、src. 4 では IDialogService.ShowDialog を MainWindow の VM で呼び出していますが、src. 6 のような拡張メソッドを作成 すれば src. 4 の showInformationMessage を書く必要が無くなります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using Prism . Services . Dialogs ;
namespace WpfPrism72 . Extensions
{
/// <summary>IDialogServiceの拡張メソッド。</summary>
public static class DialogServiceExtensions
{
/// <summary>問い合わせメッセーボックスを表示します。</summary>
/// <param name="dlgService">IDialogService。</param>
/// <param name="message">表示するメッセージを表す文字列。</param>
/// <returns>問い合わせメッセーボックスの戻り値を表すButtonResult列挙型の内の1つ。</returns>
public static ButtonResult ShowConfirmationMessage ( this IDialogService dlgService , string message )
{
var param = new DialogParameters ( $ "Message={message}" ) ;
var tempRet = ButtonResult . Cancel ;
dlgService . ShowDialog ( "ConfirmedMessageBox" , param , r = > tempRet = r . Result ) ;
return tempRet ;
}
}
}
src. 6 の拡張メソッドを追加するとメッセーボックス呼び出し側は src. 7 のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using Prism . Mvvm ;
using Prism . Services . Dialogs ;
using Reactive . Bindings ;
using WpfPrism72 . Extensions ;
namespace WpfPrism72 . ViewModels
{
/// <summary>ShellのViewModelを表します。</summary>
public class MainWindowViewModel : BindableBase
{
/// <summary>ダイアログの表示結果を取得します。</summary>
public ReactivePropertySlim < string > DialogMessage { get ; }
/// <summary>画面のタイトルを取得・設定します。</summary>
private string _title = "Prism Application" ;
public string Title
{
get { return _title ; }
set { SetProperty ( ref _title , value ) ; }
}
/// <summary>OKボタンのCommandを取得します。</summary>
public ReactiveCommand ShowMessageButtonCommand { get ; }
/// <summary>OKボタンのClickイベントハンドラ。</summary>
private void onShowMessageButtonCommand ( )
{
//this.dlgService.ShowInformationMessage("通知メッセージを表示するよ!");
//this.DialogMessage.Value = "OKボタンが押されたよ!";
if ( this . dlgService . ShowConfirmationMessage ( "通知メッセージを表示するよ!" ) == ButtonResult . Yes )
this . DialogMessage . Value = "OKボタンが押されたよ!" ;
else
this . DialogMessage . Value = string . Empty ;
}
/// <summary>ダイアログ表示サービスを表します。</summary>
private IDialogService dlgService = null ;
/// <summary>コンストラクタ。</summary>
/// <param name="dialogService">Prismのダイアログサービスを表すIDialogService。</param>
public MainWindowViewModel ( IDialogService dialogService )
{
this . dlgService = dialogService ;
this . ShowMessageButtonCommand = new ReactiveCommand ( )
. WithSubscribe ( this . onShowMessageButtonCommand ) ;
this . DialogMessage = new ReactivePropertySlim < string > ( string . Empty ) ;
}
}
}
IDialogService を DI コンテナからインジェクションする必要があるのは変わりませんが、src. 4 と比べて更にシンプルになり、Window Form のメッセージボックスとほとんど同じような呼び出しができるようになります。 fig. 3 は実行時の動作イメージです。
fig.3 パラメータで渡した内容を表示したメッセーボックス
ダイアログウィンドウのプロパティ設定 episode: 12 でも書きましたが、fig. 3 をよく見るとこのメッセージボックスは最大化・最小化もリサイズもできますし、Prism 7.1 までの PopupWindowAction と同じくタスクバーにも表示されてしまいます。 PopupWindowAction ではメッセージボックスを表示する Window 又は UserControl へ Style タグを追加してプロパティを設定していましたが、Prism 7.2 からはダイアログ用の View(UserControl)から設定できる添付プロパティが追加されています。
リサイズ不可・タスクバーへ非表示のメッセージボックスを表示するには src. 8 の Style を追加します。
<UserControl x: Class = "WpfPrism72.Views.ConfirmedMessageBox"
~ 略 ~
Width = "300" Height = "150" >
<prism:Dialog.WindowStyle>
<Style TargetType ="Window">
<Setter Property ="ResizeMode" Value ="NoResize" />
<Setter Property ="ShowInTaskbar" Value ="False" />
<Setter Property ="SizeToContent" Value ="WidthAndHeight" />
</Style>
</prism:Dialog.WindowStyle>
<Grid Margin = "10, 10, 10, 15" >
~ 略 ~
</Grid>
</UserControl>
src. 8 の 3 つの Style を設定すると、fig. 4 のようにリサイズできず、最大化・最小化ボタンも表示されない所謂メッセージボックスらしいメッセージボックスが表示されるようになります。
fig.4 リサイズ不可のダイアログ
このサンプルを書いた時の管理人の失敗談ですが、4 行目の『prism:Dialog.WindowStyle』 をキーボードから入力する時『dialog』まで入力して IntelliSense の候補に表示される『prism:DialogWindow』を間違えて選択してしまってビルドエラーが消えなくて困りました。 Prism 7.2 でダイアログウィンドウの Style を設定する際に『プロパティ ‘Contents’ が複数回指定されています。』と言うようなビルドエラーが表示される場合はタグの打ち間違い なのでよく見直してみてください。
メッセージボックスの Style に必須と言えるのは src. 8 で挙げた 3 つですが、他の Window プロパティも設定できるようなので試してみてください。
まとめ Prism 7.1 までの InteractionRequest と PopupWindowAction に比べてダイアログ用に分割した Module 内で記述を完結できるため、より依存度が下がる構造になりました。
確かにわざわざメッセージボックス用の画面(UserControl)を作成する手間はありますが、ここで紹介した方法で Module へ分割すれば他のプロジェクトへ簡単に使い回すことができるのでメリットは十分にあると言えます。
メッセーボックスを表示したいだけであれば手間は増えますが、複雑なダイアログウィンドウを表示したい場合等は手間がかかっても余りあるメリットがあると思いますし、IDialogService を使うためだけに Prism 7.2 にバージョンアップする価値はあると思います。
とりあえず Prism 7.2.0.1367 のリリースノート に書かれている IDialogService についての使用法を追検証しただけですが、Prism 7.2 には他にも新機能が追加されています。
管理人の残業が増えていて追加機能の検証に手を付ける時間がなかなか取れない状況なので次回エントリの予告は書けませんが、紹介したい内容があれば書こうとは思っています。
そしていつもの通り今回紹介したソースコードも GitHub リポジトリ に上げています。
次回記事「IDialogService にコモンダイアログを求めるのは間違っているだろうか【episode: 17 WPF Prism】」
はじめまして、いつも参考にさせてもらっています。
ありがとうございます!
一通り目を通したつもりですが見当たらなかったので質問させてください。
RegisterForNavigationで幾つかのページを宣言した際に、初回起動時に全てもしくは指定したページのインスタンス生成をすることは可能でしょうか?
背景としては、VM側でファイルウォッチャーを作成しており、該当のページまで一度遷移しないとVM自体がインスタンス生成されないのでページ遷移するまでフォルダ監視が開始されない事で悩んでいます。最初に起動されるページに全てのファイルウォッチャーを起動するように書いてもよいのですが、責務的にどうなの?と思ってしまうので、初回起動時に全てのページのインスタンス生成がしたいのですが、可能でしょうか。
読んで頂いてありがとうございます!
RegisterForNavigationでViewのインスタンスを登録する方法は無さそうです。
Viewのインスタンス生成はIRegionManager.RequestNavigateで行われるため、裏技的な方法であればShellのLoad時に登録済みViewを全てLoadする方法があるにはあります。
方法としてはShellのLoadイベントで非表示のContentControlへ仮にRequestNavigateする方法です。
GitHubリポジトリへサンプルを上げました。
Prism7.2 + ReactivePropertyで書いています。
ただ、管理人的にサンプルの方法はあまりお勧めしません。
かりまたさんが作成されているアプリの構造が分からないのでズレた回答になるかもしれませんが、VMとファイルウォッチャーの生存期間が違っているのであれば分離した方が良いのではないでしょうか?
今の構造だとファイルウォッチャーがVMに依存しているのでPrismの設計思想に合ってないように感じます。
ありがとうございます!
意図した通りに動きました!
>VMとファイルウォッチャーの生存期間が違っているのであれば分離した方が良いのではないでしょうか?
言われてみれば、確かに違いますね…。
・特定のフォルダ以下のファイルに更新があった際にページAのデータグリッドに追加
・クリップボードに変化があればページBのデータグリッドに追加
というUIへの処理をしているので、各ページにファイルウォッチャーを記述するのはしょうがないのかな?と思ってます。
あとは、ページに4000枚程度の画像をバインドしていたり、前回情報のローディングに5秒かかっています。
そうした場合に、初回起動時に全てのVMのインスタンス生成をしていれば、ナビゲーション時にカクつかなくて非常に嬉しくなりました。
立て続けにすみません。
RequestNavigateで遷移する先を UserControl ではなく、Windowに画面を切り替える事は可能でしょうか。
背景としては、クリップボードの監視ライブラリを使用したいと考えておりますが、UserControlには適用できず、Windowに対してのみ適用できるようなので、Windowに遷移したいと考えています。
https://github.com/Bassman2/WpfClipboardMonitor/wiki
調べてみますと、クリップボードの監視をするにはWindowHandle が必要で、UserControl には持っていないようです。
前回の質問と同様に、MainWindowで監視したとしても、その情報をRequestNavigateの遷移先に反映する為には遷移先内で処理を発火させる必要があると認識している為、Windowに遷移したいと考えています。
2つのコメントにまとめて返信します。
> RequestNavigateで遷移する先を UserControl ではなく、Windowに画面を切り替える事は可能でしょうか。
Windowに遷移と言うとダイアログウィンドウを表示したいと言う事でしょうか?
Prismの場合ShellであるWindowは常に表示されていて内部へ表示するView(UserControl)をRequestNavigateで切り替えるので、『Windowへの遷移』がどのような状態を指すのか分かりません…
コメントを読む限りですが、何となくVM層とモデル層が切り分けられてないように感じられるので、管理人ならこんな感じの構造にするかな…?的なサンプルを作ってGitHubリポジトリに上げました。
サンプルとしては4プロジェクト構成のソリューションなので結構大きめのサンプルになっています。
サンプルが必ず正しい!と言う訳ではありませんが、MVVMで作成するのであればVMは薄めにしてモデルに処理を集めるのが定石かと考えています。
クリップボードの監視まで導入する余裕はなかったのでMainWindowに配置したボタンClickで代用していますが、ClipboardMonitorのサンプル通りCommandで受け取れるなら変わらないはずです。
サンプルのようにViewの中継にモデルを挟む形で作成すればRequestNavigateは単純に遷移先のViewを指定するだけで良くなると思います。
かりまたさんがReactivePropertyを使用しているかは分かりませんが、ReactiveProperty無しだともっとコードを書く必要があるかもしれません。
管理人はReactiveProperty無しの書き方を知らないのですいません…
サンプルの言い訳になりますがFilesystemWatcher画面へのRequestNavigateをMainWindowではなくView側で行っているのは苦肉の策で、FilesystemWatcherのイベントがUIスレッドとは別のスレッドで動くのでRequestNavigateに失敗するからです。
そのためView側で定義しているReactiveCollectionのSubscribeで呼び出す苦肉の策を採りました。
本来であればRequestNavigateはMainWindowへまとめた方が分かり易いと思いますが、色々試してみましたがサンプルの形でしか画面遷移が成功しませんでした。
又、DataGridは使ったことも調べたことも無いのでズレているかもしれませんが、DataGrid(ListBox)を仮想化して画像は非同期で読み込む等すればViewのLoad時間も減ると思います。
参考になれば幸いです。
PrismのTipsとして1記事書けるレベルのサンプルソースを提示して頂きありがとうございます。
また、管理人様の天才的な発想に感謝致します。
ファイルウォッチャーに関して、
① ISampleAppData を持った SampleAppData クラスを作成
② DIコンテナに ISampleAppData を突っ込んでおく
③ ISampleAppData を コンストラクタインジェクションで使いまわす
④ SampleAppData のコレクションに変化があった際に発火するイベントハンドラをVMに作成する
ということですね。
無事に実装出来ました。ありがとうございます!!
かりまたさん
参考になったのなら良かったです。
公開したサンプルの構造は管理人の発想ではなく Prism + ReactiveProperty でMVVMなアプリを作る場合の定石の手法です。
管理人の発想と言えるのはクリップボード監視Viewが表示されている場合にファイルウォッチャーの通知を受けてRequestNavigateする部分だけです。
PrismのDIはサンプルのような構造を組み立てるために使用し、更新したModelのデータをView側へ通知するためにReactivePropertyを使用するとView + VM とモデル間の責務が分離しやすくなりテストコードも書きやすくなります。
MVVMの構造の紹介にはWPF Prism episodeシリーズで紹介したサンプルより今回作ったサンプルの方が向いていそうなので今回作成したサンプルを使っていつか記事を書いた方が良いかもと考えたくらいです。
今回のサンプルですが、初回表示がファイルウォッチャーの結果画面なので正常に動作していますが、クリップボード監視Viewが初回表示の場合は、監視フォルダに変更があってもファイルウォッチャーViewに切り替わらないはずなので、何か手段を講じる必要があるかもしれません。
早速の情報ありがとうございます。大変参考になります。
ep 15 のような、コモンダイアログをIDialogServiceで表示する方法検討してます。
もし何か方法あればぜひ記事化お願いします
コメントありがとうございます。
リクエストまで頂けると記事を書くモチベーションになるのでありがたいです。
一応、次回 ep: 17 はコモンダイアログを表示するネタで書こうと思っていますが、最短でも公開はお盆明けになりそうです…
ただ、ep: 17 で書こうと思っている内容は IDialogService を使用するのではなく単純な Service クラスからコモンダイアログを表示する、 Prism 7.1 でも実装できる内容になりそうです。
特に隠すようなことでもないので書いちゃいますが、Prism GitHub の IDialogService の Issue に書かれている方法を紹介しようと思っています。
https://github.com/PrismLibrary/Prism/issues/1666
↑ ここで gojanpaolo さんがリプしている実装を紹介予定です。
返信ありがとうございます。
私もgithubのIssue追ってましたがいまいちよくわからなかったので、記事お待ちしております!
ありがとうございます。
できるだけお盆明けには公開できるよう頑張ります!w