如果OpenFileDialog打开,阻塞关闭WPF窗口(从不同的线程)

本文关键字:线程 窗口 WPF 打开 OpenFileDialog 如果 | 更新日期: 2023-09-27 18:01:36

为了在你提到最明显的选择之前澄清一下,我已经调用了ShowDialog而不是Show方法!!

我想阻止关闭(从不同的线程调用)的WPF窗口,如果一个OpenFileDialog打开。

下面是我的代码(简化以显示我的问题):

public class FooWindow : Window
{
    public FooWindow()
    {
        InitializeComponent();
        this.Closing += OnClosing;    
    }
    public void OpenDialogAndCloseMe()
    {
        var ofd = new OpenFileDialog();
        Thread th = new Thread(() => CloseMe());
        th.Start();
        ofd.ShowDialog(this);
    }
    public void CloseMe()
    {
        System.Threading.Thread.Sleep(2000); //give the OpenFileDialog time to pop up...
        //since this method gets called from a different thread invoke it...
        this.Dispatcher.Invoke(() => this.Close());
    }
    private void OnClosing(object sender, CancelEventArgs e)
    {
        //check if OpenFileDialog is still open and block the close...
        e.Cancel = true;
    }
}

我面临的问题是OnClosing部分,我如何得到OpenFileDialog(或任何其他对话框在这种情况下)。

我搜索了web,发现Win32方法如下:

[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

我都试过了,但他们返回0个孩子,有什么想法是错的吗?

这是我到目前为止尝试的完整代码:

//replace the above OnClosing with this implementation... all 3 return false
private void OnClosing(object sender, CancelEventArgs e)
{
    //check if OpenFileDialog is still open and block the close...
    var hWnd = new WindowInteropHelper(this).Handle;
    if (WindowHandling.GetChildren(hWnd).Any())
        e.Cancel = true;
    if (WindowHandling.GetChildrenV2(hWnd).Any())
        e.Cancel = true;
    if (WindowHandling.GetChildrenV3(hWnd).Any())
        e.Cancel = true;
}
public static class WindowHandling
{
    private delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
    [DllImport("user32")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
    [DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
    static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
    {
        windowHandles.Add(windowHandle);
        return true;
    }
    public static IEnumerable<IntPtr> GetChildren(IntPtr hWnd)
    {
        if (hWnd == IntPtr.Zero)
            return Enumerable.Empty<IntPtr>();
        var x = new WindowHandleInfo(hWnd);
        return x.GetAllChildHandles();
    }
    public static IEnumerable<IntPtr> GetChildrenV2(IntPtr hWnd)
    {
        var windowHandles = new ArrayList();
        EnumedWindow callBackPtr = GetWindowHandle;
        EnumChildWindows(hWnd, callBackPtr, windowHandles);
        return windowHandles.OfType<IntPtr>();
    }
    public static IEnumerable<IntPtr> GetChildrenV3(IntPtr hParent)
    {
        var result = new List<IntPtr>();
        var ct = 0;
        var maxCount = 100;
        var prevChild = IntPtr.Zero;
        while (true && ct < maxCount)
        {
            var currChild = FindWindowEx(hParent, prevChild, null, null);
            if (currChild == IntPtr.Zero)
                break;
            result.Add(currChild);
            prevChild = currChild;
            ++ct;
        }
        return result;
    }
    //http://stackoverflow.com/questions/1363167/how-can-i-get-the-child-windows-of-a-window-given-its-hwnd
    private class WindowHandleInfo
    {
        private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);
        private readonly IntPtr _mainHandle;
        public WindowHandleInfo(IntPtr handle)
        {
            _mainHandle = handle;
        }
        public IEnumerable<IntPtr> GetAllChildHandles()
        {
            var childHandles = new List<IntPtr>();
            var gcChildhandlesList = GCHandle.Alloc(childHandles);
            var pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
            try
            {
                var childProc = new EnumWindowProc(EnumWindow);
                var x = EnumChildWindows(this._mainHandle, childProc, pointerChildHandlesList);
                if (x == false)
                {
                    var error = Marshal.GetLastWin32Error();
                }
            }
            finally
            {
                gcChildhandlesList.Free();
            }
            return childHandles;
        }
        private static bool EnumWindow(IntPtr hWnd, IntPtr lParam)
        {
            var gcChildhandlesList = GCHandle.FromIntPtr(lParam);
            if (gcChildhandlesList.Target == null)
                return false;
            var childHandles = gcChildhandlesList.Target as List<IntPtr>;
            if (childHandles != null)
                childHandles.Add(hWnd);
            return true;
        }
    }
}

如果OpenFileDialog打开,阻塞关闭WPF窗口(从不同的线程)

你可以用布尔值跟踪来解决这个问题,如果它是打开的:

bool dialogOpen = false;
public void OpenDialogAndCloseMe()
{
    var ofd = new OpenFileDialog();
    Thread th = new Thread(() => CloseMe());
    th.Start();
    dialogOpen = true;
    ofd.ShowDialog(this);
    dialogOpen = false;
}

private void OnClosing(object sender, CancelEventArgs e)
{
    //check if OpenFileDialog is still open and block the close...
    if(dialogOpen)
    {
        e.Cancel = true;
    }
}