如果OpenFileDialog处于打开状态,则阻止关闭WPF窗口(来自不同的线程)

时间:2015-07-08 19:34:13

标签: c# wpf multithreading

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

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

这是我的代码(缩小以显示我的问题):

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(或那种情况下的任何其他Dialog)。

我在网上搜索过并找到了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;
        }
    }
}

1 个答案:

答案 0 :(得分:3)

如果它是打开的话,您可以使用布尔值跟踪来解决此问题:

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;
    }
}