如果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;
}
}
}
你可以用布尔值跟踪来解决这个问题,如果它是打开的:
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;
}
}