4 Stimmen

Erzwingen des Schließens aller offenen Popups aus dem Code

Ich möchte, dass alle offenen Popups (mit StaysOpen == false) vom Code aus geschlossen werden. Grundsätzlich möchte ich den Benutzer simulieren, die Maus klicken (die die Popups schließen würde) von Code.

Ich muss den Klick nicht wirklich simulieren, ich brauche nur das resultierende Verhalten. Ich habe darüber nachgedacht, nur durch den visuellen Baum auf der Suche nach Popups und schließen jedes, aber das scheint nicht wie die sauberste Ansatz.

Vielen Dank im Voraus für jede Hilfe oder Meinung.

9voto

Thomas Levesque Punkte 277723

Ein WPF-Popup erstellt tatsächlich ein neues Fenster (ein Win32-Fenster, nicht ein WPF Window Instanz). Sie können es also nicht in der Application.Windows Sammlung, aber Sie können sie wahrscheinlich mit einer Win32-API wie EnumChildWindows .

Sobald Sie das Handle haben, können Sie die zugehörige HwndSource . Ich denke, die RootVisual de la HwndSource ist die Popup (Ich habe es nicht überprüft, vielleicht müssen Sie tiefer in den visuellen Baum schauen).

Der Code sollte also in etwa so lauten (völlig ungetestet):

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

}

1voto

Robert Rossney Punkte 91100

Der Weg über den visuellen Baum ist derjenige, der in keiner Weise davon abhängt, wie Sie sie überhaupt erstellt haben. Es gibt vielleicht viel sauberere Wege, aber sie alle hängen wirklich von Ihrer Implementierung ab.

Zum Beispiel sind alle Popups in meiner Anwendung an View Model Objekte gebunden, die eine Art von IsOpen Eigenschaft, an die das Popup gebunden ist. Wenn ich diese Funktionalität in meinem Projekt implementieren müsste, würde ich einfach eine Sammlung dieser Objekte (oder eine Generatormethode) pflegen, die ich durchlaufen könnte, und IsOpen jeweils auf false. Dies wird natürlich nicht funktionieren, wenn Sie nicht Ihre UI bauen, wie ich bin, aber.

1voto

jonzbonz Punkte 61

Die akzeptierte Antwort ( https://stackoverflow.com/a/3886139/12885902 ) hat bei mir nicht funktioniert, weil source.RootVisual war nie vom Typ Popup sondern vom internen Typ PopupRoot . Mit der folgenden Änderung funktioniert der Code nun:

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

Bitte denken Sie auch daran, dass die CloseAllPopups()-Methode vom Haupt-UI-Thread aufgerufen werden muss (z.B. Application.Current.Dispatcher.Invoke() )!

1voto

jonzbonz Punkte 61

Meine vorherige Antwort hat auch nicht immer funktioniert. Sie funktionierte nur, wenn der Visual Studio Debugger mit dem Prozess verbunden war. Was auf jeden Fall funktioniert hat, ist das Folgende:

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

0voto

bmi Punkte 540
  1. Deklarieren Sie irgendwo statisches Array von geöffneten Popups:

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

  2. Vor dem Öffnen eines Popups alle zuvor geöffneten Popups schließen:

    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 }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X