我有一些類似的問題。所以模態對話框,但在該對話框中你有按鈕「選擇」需要切換到主窗體(最好不關閉模態對話框),從那裏選擇一些區域,然後返回到模態對話框的選擇信息。我試圖用無模式對話框/顯示/隱藏來玩一點,之後找不到任何好的(易於編碼)的解決方案,使用win32本地函數調用以某種方式進行編碼。我測試過的 - 它適用於winforms和xaml。
問題本身也不是一件容易的事情 - 所以用戶按下「選擇」,然後他可能會忘記他正在選擇某件東西,並返回到同一個不同的選擇對話框,這可能導致兩個或更多相同對話的實例。我試圖通過使用靜態變量(實例/父母)解決這個問題 - 如果你有純粹的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
...
相同的範例可能有問題無模式對話框,因爲每個選擇的函數調用鏈吃堆棧,最終你可能會得到堆棧溢出,否則會產生問題,也有管理的父窗口狀態(啓用/禁用)。
所以我認爲這是相當輕量級的問題解決方案,即使它看起來相當複雜。
是否有一個特定的原因,你爲什麼不能每次都實例化一個新的?無論如何,我認爲它更安全,更好。 – 2010-08-26 16:02:59
@Alex問題的根源在於我正在使用的第三方控件。當投擲棱鏡和團結時會變得更加複雜。我非常認真地相信,像winform這樣的單身形式會更容易實現。在非模式對話框中嘗試顯示/隱藏時,性能非常棒。但是,要求聲明對話框必須是模態的。 – 2010-08-26 16:11:10
對話框的Show方法是否接受參數?我發現這個http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/f3565f01-f972-4aaf-80cc-986488d25261,這可能會有所幫助。 – 2010-08-26 16:56:17