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

ig9co6j1  于 2022-12-07  发布在  其他
关注(0)|答案(5)|浏览(153)

我想通过代码关闭所有打开的弹出窗口(StaysOpen == false)。基本上我想通过代码模拟用户点击鼠标(这将关闭弹出窗口)。
我不需要实际模拟点击,我只需要结果行为。我曾考虑过遍历可视化树查找弹出窗口并关闭每个弹出窗口,但这似乎不是最干净的方法。
提前感谢您的任何帮助或意见。

puruo6ea

puruo6ea1#

WPF弹出窗口实际上创建了一个新窗口(Win32窗口,而不是WPF Window示例)。因此,您无法在Application.Windows集合中找到它,但您可能可以使用Win32 API(如EnumChildWindows)找到它。
我认为HwndSourceRootVisualPopup(没有检查,您可能需要在可视树中更深入地查看)。
因此代码应该类似于下面的代码(完全未经测试):

public static class PopupCloser
{
    public static void CloseAllPopups()
    {
        foreach(Window window in Application.Current.Windows)
        {
            CloseAllPopups(window);
        }
    }

    public static void CloseAllPopups(Window window)
    {
        IntPtr handle = new WindowInteropHelper(window).Handle;
        EnumChildWindows(handle, ClosePopup, IntPtr.Zero);
    }

    private static bool ClosePopup(IntPtr hwnd, IntPtr lParam)
    {
        HwndSource source = HwndSource.FromHwnd(hwnd);
        if (source != null)
        {
            Popup popup = source.RootVisual as Popup;
            if (popup != null)
            {
                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 EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

}
az31mfrm

az31mfrm2#

通过可视化树是一种不依赖于你最初是如何创建它们的方法。可能有很多更干净的方法来完成它,但它们都真正依赖于你的实现。
例如,我的应用程序中的所有Popup都绑定到视图模型对象,这些对象公开Popup绑定到的某种IsOpen属性。(或者一个生成器方法),我可以迭代这些方法,并在每个方法上将IsOpen设置为false。

3xiyfsfu

3xiyfsfu3#

可接受的答案(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);

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

xsuvu9jc

xsuvu9jc4#

我之前的答案也不是一直有效。它只在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.SetCurrentValue(Popup.IsOpenProperty, false));
});
kh212irz

kh212irz5#

1.在某处声明打开的弹出窗口的静态数组:
static List<Popup> openedPopups = new List<Popup>();
1.在打开弹出窗口之前,请关闭之前打开的所有弹出窗口:
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 }

相关问题