强制从代码中关闭所有打开的弹出窗口

时间:2010-10-07 21:22:10

标签: c# wpf xaml

我想让所有打开的弹出窗口(使用StaysOpen == false)从代码中关闭。基本上我想模拟用户点击代码鼠标(这会关闭弹出窗口)。

我不需要实际模拟点击,我只需要产生的行为。我想过只是通过视觉树寻找弹出窗口并关闭每个弹出窗口,但这似乎不是最干净的方法。

提前感谢您的任何帮助或意见。

5 个答案:

答案 0 :(得分:9)

WPF弹出窗口实际上创建了一个新窗口(Win32窗口,而不是WPF Window实例)。因此,您无法在Application.Windows集合中找到它,但您可以使用EnumChildWindows等Win32 API找到它。

获得句柄后,您可以检索关联的HwndSource。我认为RootVisual的{​​{1}}是HwndSource(没有检查,您可能需要更深入地查看可视树)。

所以代码应该与此类似(完全未经测试):

Popup

答案 1 :(得分:1)

通过可视化树是这样做的方式,它不依赖于你如何创建它们的方式。可能有更清洁的方法,但它们都取决于您的实施。

例如,我的应用程序中的所有弹出窗口都必须查看模型对象,这些对象会暴露Popup绑定的某种IsOpen属性。如果我需要在我的项目中实现这个功能,我只需要维护这些对象(或生成器方法)的集合,我可以迭代它们,并在每个对象上设置IsOpen为false。如果你不像我那样构建你的UI,这显然是行不通的。

答案 2 :(得分:1)

被接受的答案(https://stackoverflow.com/a/3886139/12885902)并非对我有用,因为source.RootVisual的类型从不为Popup,而是内部类型为PopupRoot。以下调整使代码起作用:

private void CloseAllPopups()
{
    foreach (Window window in Application.Current.Windows)
    {
        IntPtr handle = new WindowInteropHelper(window).Handle;
        EnumThreadWindows(handle, ClosePopup, IntPtr.Zero);
    }
}

private static bool ClosePopup(IntPtr hwnd, IntPtr lParam)
{
    HwndSource source = HwndSource.FromHwnd(hwnd);
    if (source?.RootVisual?.GetType().Name == "PopupRoot")
    {
        if (LogicalTreeHelper.GetParent(source.RootVisual) is Popup popup)
        {
            popup.IsOpen = false;
        }
    }
    return true; // to continue enumeration
}

private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumThreadWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

还请记住,必须由主UI线程(例如Application.Current.Dispatcher.Invoke())调用CloseAllPopups()方法!

答案 3 :(得分:0)

  1. 声明打开的弹出窗口的静态数组:

    static List<Popup> openedPopups = new List<Popup>();

  2. 在打开弹出窗口之前关闭所有预先打开的弹出窗口:

    private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { // close all before opene popup openedPopups.ForEach(p => p.IsOpen = false); openedPopups.Clear(); // clear opened popus's collection, because they were closed Popup1.IsOpen = true; // open popup I need open now openedPopups.Add(Popup1); // remember it for future close }

答案 4 :(得分:0)

我以前的回答也并非一直有效。仅当将Visual Studio调试器附加到该进程时,该函数才起作用。在任何情况下最终起作用的是以下内容:

Application.Current.Dispatcher.Invoke(() =>
{
    PresentationSource.CurrentSources.OfType<HwndSource>()
        .Select(h => h.RootVisual)
        .OfType<FrameworkElement>()
        .Select(f => f.Parent)
        .OfType<Popup>()
        .Where(popup => popup.IsOpen)
        .ToList()
        .ForEach(popup => popup.IsOpen = false);
});