我有一些类似的问题。所以模态对话框,但在该对话框中你有按钮“选择”需要切换到主窗体(最好不关闭模态对话框),从那里选择一些区域,然后返回到模态对话框的选择信息。我试图用无模式对话框/显示/隐藏来玩一点,之后找不到任何好的(易于编码)的解决方案,使用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