WindowManager
次のように、事前に開始されたダイアログビューモデルにバインドされたダイアログウィンドウを開くダイアログサービス(と呼ばれる)を備えたMVVM-LightベースのWPFアプリケーションがあります。
private enum ViewModelKind
{
PlanningGridVM,
InputDialogVM,
TreeViewDialogVM,
SaveFileDialogVM,
MessageBoxVM
}
/// <summary>
/// Shows the Window linked to this ViewModel as a dialog window.
/// </summary>
/// <typeparam name="TViewModel">The type of the view model.</typeparam>
/// <returns>Tri-state boolean dialog response.</returns>
public bool? ShowDialog<TViewModel>(string key = null)
{
ViewModelKind name;
// Attempt to parse the type-parameter to enum
Enum.TryParse(typeof(TViewModel).Name, out name);
Window view = null;
switch (name)
{
// removed some irrelevant cases...
case ViewModelKind.InputDialogVM:
view = new InputDialogView();
System.Diagnostics.Debug.WriteLine(
view.GetHashCode(), "New Window HashCode");
view.Height = 200;
result = view.ShowDialog();
default:
return true;
}
}
ダイアログの XAML は次のように始まります。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:b="clr-namespace:MyCompany.Common.Behaviours"
x:Class="MyCompany.Common.Views.InputDialogView" mc:Ignorable="d"
DataContext="{Binding InputDialogVM, Source={StaticResource Locator}}"
Title="{Binding DisplayName}" MinHeight="200" MinWidth="300" MaxHeight="200"
b:WindowBehaviours.DialogResult="{Binding DialogResult}"
WindowStyle="ToolWindow" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen"
Height="200" Width="300">
ビューモデルは、コンストラクターで Messenger に適切に登録され、ビューモデルのプロパティをリセットすることで初期化メッセージに応答します。これはすべて意図したとおりに機能します。
「OK/キャンセル」ダイアログを適切に閉じるために、 という添付プロパティがありDialogResult
、これも期待どおりに機能します...
/// <summary>
/// DialogResult
/// </summary>
public static readonly DependencyProperty DialogResultProperty = DependencyProperty
.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(WindowBehaviours),
new PropertyMetadata(null, DialogResultChanged));
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
private static void DialogResultChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var window = obj as Window;
System.Diagnostics.Debug.WriteLine(
window.GetHashCode(), "Attempting to update DialogResult on Hashcode");
if (window != null && window.IsActive)
{
window.DialogResult = e.NewValue as bool?;
}
}
...ただし、1 つ注意点があります。ウィンドウ インスタンスの HashCode を追跡するために追加したデバッグ出力に気付きましたか? それは私にとって次のことを確認しました:
XAML の DataContext バインディングを介してダイアログ ビューによってアクセスされる 1 つの再利用可能なビュー モデル インスタンスがあり、新しいダイアログを数回連続して開いた場合、これらのダイアログ インスタンスは、OnClosed イベントが発生した後でもopen のままです。ダイアログが表示されなくなっても!
これの正味の効果は、IsActive
ウィンドウの null をチェックすることと併せて、ウィンドウのプロパティをチェックしなければならないことです。そうしないと、システムはwindow.DialogResult
まだ残っているすべてのダイアログファントムに設定しようとし、System.InvalidOperationException
例外が発生します:「DialogResultは、ウィンドウが作成されてダイアログとして表示された後にのみ設定できます」.
デバッグ出力
New Window HashCode: 4378943
Attempting to update DialogResult on Hashcode: 4378943
New Window HashCode: 53142588
Attempting to update DialogResult on Hashcode: 53142588
New Window HashCode: 47653507
Attempting to update DialogResult on Hashcode: 53142588
Attempting to update DialogResult on Hashcode: 47653507
New Window HashCode: 57770831
Attempting to update DialogResult on Hashcode: 53142588
Attempting to update DialogResult on Hashcode: 57770831
New Window HashCode: 49455573
Attempting to update DialogResult on Hashcode: 53142588
Attempting to update DialogResult on Hashcode: 57770831
Attempting to update DialogResult on Hashcode: 49455573
New Window HashCode: 20133242
Attempting to update DialogResult on Hashcode: 53142588
Attempting to update DialogResult on Hashcode: 57770831
Attempting to update DialogResult on Hashcode: 49455573
Attempting to update DialogResult on Hashcode: 20133242
質問
アタッチされたビヘイビアーがインスタンスに固有のプロパティの値を格納するということを何度も見てきました。なぜこれは反対の方法で動作するのですか?
これらの有効期限が切れたダイアログがまだ単一のビューモデル インスタンスの INPC イベントに登録されていることは明らかです。閉じたダイアログが INPC イベントから登録解除されるようにするにはどうすればよいですか?