Window
のインスタンスを1つ保持し、必要に応じてShowDialog
を呼び出します。これはwinformsで見つかりましたが、WPFではこの例外を受け取ります:
System.InvalidOperationException:Visibilityを設定したり、Windowが閉じた後にShow、ShowDialog、またはWindowInteropHelper.EnsureHandleを呼び出したりすることはできません。
WPFでこのようなことをする方法はありますか?
MyWindow.Instance.ShowDialog();
public class MyWindow : Window
{
private static MyWindow _instance;
public static MyWindow Instance
{
if( _instance == null )
{
_instance = new Window();
}
return _instance();
}
}
ウィンドウを閉じるのではなく、可視性を変更した場合、couldを行うと思います。 Closing()イベントでそれを実行し、クローズをキャンセルする必要があります。クローズを許可すると、閉じたウィンドウを再び開くことはできません- here から:
Closingイベントがキャンセルされない場合、以下が発生します。
...
ウィンドウによって作成された管理されていないリソースは破棄されます。
その後、ウィンドウは二度と有効になりません。
しかし、努力する価値はないと思います-毎回新しいウィンドウを作成することは実際にはそれほどパフォーマンスに影響しませんし、デバッグが難しいバグ/メモリリークを導入する可能性ははるかに低くなります。 (さらに、アプリケーションがシャットダウンされたときに、それが閉じてリソースを解放したことを確認する必要があります)
ShowDialog()を使用していることを読んでください。これにより、ウィンドウがモーダルになり、非表示にするだけで親ウィンドウに制御が戻りません。モーダルウィンドウでこれを行うことはまったく不可能だと思います。
私が間違っていなければ、そのウィンドウのcloseイベントをキャンセルし、代わりに可視性を非表示に設定できます
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Visibility = Visibility.Hidden;
}
public class MyWindow : Window
public MyWindow ()
{
InitializeComponent();
Closed += new System.EventHandler(MyWindow_Closed);
}
private static MyWindow _instance;
public static MyWindow Instance
{
if( _instance == null )
{
_instance = new Window();
}
return _instance();
}
void MyWindow_Closed(object sender, System.EventArgs e)
{
_instance = null;
}
閉じているウィンドウを表示しようとすると、次の例外が発生します。
"Visibilityを設定したり、ウィンドウが閉じた後にShow、ShowDialog、WindowInteropHelper.EnsureHandleを呼び出したりすることはできません。"
したがって、このケースを処理するには、ウィンドウのVisibilityオプションを使用する方が良いでしょう。ウィンドウを直接閉じるのではなく、ウィンドウの可視性をHiddenまたはCollapsedに設定する必要があります。
this.Visibility = System.Windows.Visibility.CollapsedまたはHidden;
再度表示したい場合は、可視性をVisibleに設定するだけです
this.Visibility = System.Windows.Visibility.Visible;
closeイベントをキャンセルし、visibility = hiddenを設定すると、この問題をオーバーライドできます
Private Sub ChildWindow_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
e.Cancel = True
Me.Visibility = Windows.Visibility.Hidden
End Sub
なんとなく似たような問題がありました。したがって、モーダルダイアログですが、そのダイアログにはメインフォームに切り替える必要がある「選択」ボタンがあります(モーダルダイアログを閉じずに)、そこからいくつかの領域を選択してから、選択情報とともにモーダルダイアログに戻ります。私はモードレスダイアログ/表示/非表示で少し遊んでみましたが、Win32ネイティブ関数呼び出しを使用して何らかの方法でコード化されたハッキングアプローチをコーディングできませんでした。私がテストしたもの-winformsでもxamlでも動作します。
問題自体も簡単なものではありません-ユーザーが「選択」を押すと、何かを選択していることを忘れて、同じ選択ダイアログに戻り、同じダイアログの2つ以上のインスタンスが発生する可能性があります。 。
静的変数(インスタンス/親)を使用してこの問題に対処しようとしています-純粋なwinformsまたは純粋なwpfテクノロジがある場合、instance.Parentまたはinstance.Ownerから親を取得できます。
public partial class MeasureModalDialog : Window
{
// Dialog has "Select area" button, need special launch mechanism. (showDialog / SwitchParentChildWindows)
public static MeasureModalDialog instance = null;
public static object parent = null;
static public void showDialog(object _parent)
{
parent = _parent;
if (instance == null)
{
instance = new MeasureModalDialog();
// Parent is winforms, child is xaml, this is just glue to get correct window owner to child dialog.
if (parent != null && parent is System.Windows.Forms.IWin32Window)
new System.Windows.Interop.WindowInteropHelper(instance).Owner = (parent as System.Windows.Forms.IWin32Window).Handle;
// Enable parent window if it was disabled.
instance.Closed += (_sender, _e) => { instance.SwitchParentChildWindows(true); };
instance.ShowDialog();
instance = null;
parent = null;
}
else
{
// Try to switch to child dialog.
instance.SwitchParentChildWindows(false);
}
} //showDialog
public void SwitchParentChildWindows( bool bParentActive )
{
View3d.SwitchParentChildWindows(bParentActive, parent, this);
}
public void AreaSelected( String selectedAreaInfo )
{
if( selectedAreaInfo != null ) // Not cancelled
textAreaInfo.Text = selectedAreaInfo;
SwitchParentChildWindows(false);
}
private void buttonAreaSelect_Click(object sender, RoutedEventArgs e)
{
SwitchParentChildWindows(true);
View3d.SelectArea(AreaSelected);
}
...
public static class View3d
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnableWindow(IntPtr hWnd, bool bEnable);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowEnabled(IntPtr hWnd);
/// <summary>
/// Extracts window handle in technology independent wise.
/// </summary>
/// <param name="formOrWindow">form or window</param>
/// <returns>window handle</returns>
static public IntPtr getHandle( object formOrWindow )
{
System.Windows.Window window = formOrWindow as System.Windows.Window;
if( window != null )
return new System.Windows.Interop.WindowInteropHelper(window).Handle;
System.Windows.Forms.IWin32Window form = formOrWindow as System.Windows.Forms.IWin32Window;
if (form != null)
return form.Handle;
return IntPtr.Zero;
}
/// <summary>
/// Switches between modal sub dialog and parent form, when sub dialog does not needs to be destroyed (e.g. selecting
/// something from parent form)
/// </summary>
/// <param name="bParentActive">true to set parent form active, false - child dialog.</param>
/// <param name="parent">parent form or window</param>
/// <param name="dlg">sub dialog form or window</param>
static public void SwitchParentChildWindows(bool bParentActive, object parent, object dlg)
{
if( parent == null || dlg == null )
return;
IntPtr hParent = getHandle(parent);
IntPtr hDlg = getHandle(dlg);
if( !bParentActive )
{
//
// Prevent recursive loops which can be triggered from UI. (Main form => sub dialog => select (sub dialog hidden) => sub dialog in again.
// We try to end measuring here - if parent window becomes inactive -
// means that we returned to dialog from where we launched measuring. Meaning nothing special needs to be done.
//
bool bEnabled = IsWindowEnabled(hParent);
View3d.EndMeasuring(true); // Potentially can trigger SwitchParentChildWindows(false,...) call.
bool bEnabled2 = IsWindowEnabled(hParent);
if( bEnabled != bEnabled2 )
return;
}
if( bParentActive )
{
EnableWindow(hDlg, false); // Disable so won't eat parent keyboard presses.
ShowWindow(hDlg, 0); //SW_HIDE
}
EnableWindow(hParent, bParentActive);
if( bParentActive )
{
SetForegroundWindow(hParent);
BringWindowToTop(hParent);
} else {
ShowWindow(hDlg, 5 ); //SW_SHOW
EnableWindow(hDlg, true);
SetForegroundWindow(hDlg);
}
} //SwitchParentChildWindows
...
同じパラダイムでは、モードレスダイアログで問題が発生する可能性があります。各選択関数の呼び出しチェーンがスタックを消費し、最終的にスタックオーバーフローが発生したり、親ウィンドウの状態管理(有効化/無効化)で問題が発生する可能性があるためです.
したがって、これは問題に対する非常に軽量なソリューションであり、かなり複雑に見えます。
ここに私が扱う方法があります:
public partial class MainWindow
{
bool IsAboutWindowOpen = false;
private void Help_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!IsAboutWindowOpen)
{
var aboutWindow = new About();
aboutWindow.Closed += new EventHandler(aboutWindow_Closed);
aboutWindow.Show();
IsAboutWindowOpen = true;
}
}
void aboutWindow_Closed(object sender, EventArgs e)
{
IsAboutWindowOpen = false;
}
}