【问题标题】:Ookii.Dialogs - making sure another dialog appears above Progress dialogOokii.Dialogs - 确保另一个对话框出现在进度对话框上方
【发布时间】:2012-03-24 14:44:40
【问题描述】:

如果你不熟悉Ookii.Dialogs,我建议你先look at this web page。它是开源的,您可以在那里找到源代码、编译的二进制文件、文档和示例应用程序作为下载。

在我的应用程序中,我使用 Ookii.Dialogs.Wpf.ProgressDialogShowDialog(this) 一个对文件进行一些处理的进度对话框( this 始终是应用程序的主窗口)。正如预期的那样,进度对话框大约需要一秒钟才能真正可见(即使它已经在处理我的文件)。

在进度对话框的 DoWork 线程中,我还在检查输出文件是否已经存在并询问用户是覆盖每个文件还是跳过输出。我使用 Ookii.Dialogs.Wpf.TaskDialogShowDialog(this) 一个 “带有命令链接的任务对话框”(看起来像 this)并询问用户覆盖问题 - 除非操作系统不支持它,否则我会退回到常规 MessageBox问题也适用于消息框 )。

当我的应用程序在进度对话框的 DoWork 线程的开头找到一个现有文件时,就会出现问题。当任务对话框出现询问用户是否覆盖时:

预期行为:任务对话框必须保持在顶部。当进度对话框出现时(延迟 1s 后),它必须出现在任务对话框的后面。

实际行为:任务对话框没有停留在最前面。延迟 1 秒后出现进度对话框时),它出现在任务对话框的顶部。

当进度对话框已经可见时,后续覆盖请求中不会发生实际行为。任务对话框正确显示在后续对话框的进度对话框顶部,尽管用户可以来回切换两个对话框(只是不能从其中任何一个切换到主窗口)。

我正在寻找这个:

  • 最佳解决方案:使任务对话框显示为模态在进度对话框中。如果用户尝试切换到进度对话框,则不得允许用户这样做。
  • 次优:即使进度对话框在 1 秒延迟后出现,任务对话框的首次出现也保持在最前面。

不是在寻找以下内容:

  • 设置任务对话框的始终处于顶部的标志。我不希望任务对话框始终显示在用户计算机上每个窗口的顶部。
  • 在任务对话框出现之前添加延迟。我试过 Thread.Sleep() 甚至在一个循环中,它只是挂起所有的执行,并没有解决问题。
  • 等到进度对话框出现后再显示任务对话框。理论上可以工作,但我没有找到方法知道进度对话框是否出现。
  • 使对话框无模式。我希望它们都是模态的。 (此外,我已经尝试将它们设为无模式;这无助于解决问题。)
  • 用于任务对话框的解决方案。它也必须适用于常规消息框。

【问题讨论】:

  • 解决问题的方法是使用进度对话框的窗口句柄作为任务对话框的父级。不幸的是,Ookii.Dialogs 目前没有公开该功能(主要是因为我在编写这个库时认为没有任何方法可以获取它;结果发现有一种未记录的方法)。
  • @Sven :) 很高兴收到您的来信!!我可以在自己的源代码中应用这种未记录的方式来获取进度对话框的窗口句柄吗?还是您必须在 Ookii.Dialogs 本身中使用它才能将句柄暴露给我?另外,你想尽快做吗?谢谢!
  • 您需要修改 Ookii.Dialogs 源代码。我会考虑进行这些修改。
  • 在实施该解决方案时还有一个问题。 MessageBox.Show 方法以及 TaskDialog 需要一个 Window 实例作为它们的父对象。我可以检索并公开进度对话框的窗口句柄,但我永远无法将其变成Window。我当然可以更改 TaskDialog 以接受非Window 父级,但这对于MessageBox 是不可能的。
  • 嗯,进度对话框的窗口句柄的类型是什么?为什么不能是Window 或转换为Window?如果您能给我一个您将要使用的未记录方式的链接(如果有的话),那就太好了。谢谢@Sven

标签: wpf c#-4.0 modal-dialog


【解决方案1】:

我知道这可能有点晚了,但我只是在我自己的程序中预先解决了一个类似的问题,它让我想起了这个问题。 我使用 windows FindWindowEx API 函数针对任务对话窗口标题属性来查找对话窗口句柄,然后使用我自己的类将其设为 Iwin32Window,然后可以将其用作消息框或任务对话的父级。 相应的代码如下(VB.NET)

    'allows us to use the handle as a window
    class window
        Implements IWin32Window
        private _handle

        public sub new(handle as intptr)
            _handle = handle
        end sub

        Public ReadOnly Property Handle As System.IntPtr Implements System.Windows.Forms.IWin32Window.Handle
            Get
                Return _h
            End Get
        End Property
    end class

    'Declare the needed DLL import
    class NativeMethods
        <DllImport("user32.dll", SetLastError:=True, ThrowOnUnmappableChar:=True, CharSet:=CharSet.Unicode, bestFitMapping:=False)>
        Public Shared Function FindWindowEx(hwndParent As IntPtr, hwndChildAfter As IntPtr, lpszClass As String, lpszWindow As String) As IntPtr
        End Function
    end class

    'Make sure you've set a window title when you run your task
    sub runTask()
        dim myDlg as Ookii.Dialogs.ProgressDialog
        'Do what you need to here
        myDlg.WindowTitle = "make sure you set a title"
    end sub
    sub myTask(sender as object, e As System.ComponentModel.DoWorkEventArgs)
         'Checks and balances here
         if fileExists then
              'this is the dialog that's running our task
              Dim dlg As Ookii.Dialogs.ProgressDialog = DirectCast(sender, Ookii.Dialogs.ProgressDialog)
              'Find the window handle
              Dim dlgHandle As IntPtr = NativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, Nothing, dlg.WindowTitle)
              'make it an Iwin32Window
              dim dlgWindow as new window(dlgHandle)
              'That can then be used as a parent for the message box or the task dialog
              MessageBox.Show(dlgWindow,"The file exists, overwrite?")
         end if
    end sub

我不太擅长评论我的代码或让我的解释被理解,所以如果您对发生的事情有任何疑问,我会尽力提供帮助。

【讨论】:

  • 或许@Sven 可以看看。谢谢!
  • @ADTC:这基本上是我已经提出的(除了我使用IOleWindow 来获取窗口句柄,这比这里的FindWindowEx hack 更干净)。问题在于,在 WPF 中,MessageBox 之类的东西不会将IWin32Window 作为父级,而是需要Window。并且不可能为实际上不是 WPF 窗口的任何东西创建 Window 实例。因此,虽然它适用于 Windows 窗体,但它不适用于 WPF,这是由于 WPF 中的设计问题。
【解决方案2】:

今天我已经阅读了这个问题并为我找到了解决方案。为TaskDialog尝试过,这有效。没有尝试使用其他对话框。 我写了一个 DialogControler 来处理 MVVM。 此 DialogControlar 有一个名为“所有者”的属性,其中包含 WPF-MainWindow。 然后我用它来调用(在我的例子中,我来自 viewmodel 中的后台线程)并在 ShowDialog 中设置它。

我的代码:

using (var dialog = new Ookii.Dialogs.Wpf.TaskDialog())
        {
            dialog.WindowTitle = "My title";
            dialog.MainInstruction = "My text";
            var okButton = new Ookii.Dialogs.Wpf.TaskDialogButton(Ookii.Dialogs.Wpf.ButtonType.Ok);
            dialog.Buttons.Add(okButton);
            Owner.Dispatcher.Invoke(new Action(() => dialog.ShowDialog(Owner)));

        }

有了这个,我得到了一个最上面的窗口。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-07
    相关资源
    最近更新 更多