webentwicklung-frage-antwort-db.com.de

Laden eines WPF-Fensters ohne Anzeigen

Ich erstelle einen globalen Hotkey zum Anzeigen eines Fensters durch PInvoking RegisterHotKey(). Aber dazu brauche ich die HWND dieses Fensters, die erst existiert, wenn das Fenster geladen ist, also zum ersten Mal angezeigt wird. Ich möchte das Fenster jedoch nicht anzeigen, bevor ich den Hotkey einstellen kann. Gibt es eine Möglichkeit, eine HWND für dieses Fenster zu erstellen, die für den Benutzer nicht sichtbar ist?

49
svick

Wenn Sie auf .NET 4.0 abzielen, können Sie die neue EnsureHandle-Methode verwenden, die für WindowInteropHelper verfügbar ist:

public void InitHwnd()
{
    var helper = new WindowInteropHelper(this);
    helper.EnsureHandle();
}

(Danke an Thomas Levesque für weist darauf hin. )

Wenn Sie auf eine ältere Version von .NET Framework abzielen, zeigen Sie am einfachsten das Fenster, um zum HWND zu gelangen, und legen Sie einige Eigenschaften fest, um sicherzustellen, dass das Fenster unsichtbar ist und den Fokus nicht stiehlt:

var window = new Window() //make sure the window is invisible
{
    Width = 0,
    Height = 0,
    WindowStyle = WindowStyle.None,
    ShowInTaskbar = false,
    ShowActivated = false
};
window.Show();

Wenn Sie das tatsächliche Fenster anzeigen möchten, können Sie den Inhalt und die Größe festlegen und den Stil wieder in ein normales Fenster ändern.

61
Patrick Klug

Sie können das Fenster auch in ein sogenanntes Meldungsfenster umwandeln. Da dieser Fenstertyp keine grafischen Elemente unterstützt, wird er niemals angezeigt. Im Grunde geht es darum zu rufen:

    SetParent(hwnd, (IntPtr)HWND_MESSAGE);

Erstellen Sie entweder ein dediziertes Meldungsfenster, das immer ausgeblendet ist, oder verwenden Sie das echte GUI-Fenster und ändern Sie es wieder in ein normales Fenster, wenn Sie es anzeigen möchten. Ein vollständigeres Beispiel finden Sie im Code unten.

    [DllImport("user32.dll")]
    static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent);

    private const int HWND_MESSAGE = -3;

    private IntPtr hwnd;
    private IntPtr oldParent;

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;

        if (hwndSource != null)
        {
            hwnd = hwndSource.Handle;
            oldParent = SetParent(hwnd, (IntPtr)HWND_MESSAGE);
            Visibility = Visibility.Hidden;
        }
    }

    private void OpenWindowMenuItem_Click(object sender, RoutedEventArgs e)
    {
        SetParent(hwnd, oldParent);
        Show();
        Activate();
    }

Für mich war die Lösung, die Breite, Höhe auf Null und den Stil auf Keine zu setzen, nicht erfolgreich, da sie immer noch ein kleines Fenster mit einem ärgerlichen Schatten des scheinbar scheinenden Rahmens um ein 0x0-Fenster zeigte (getestet unter Windows 7) ). Deshalb biete ich diese alternative Option an.

17
DJP

Dies ist ein schmutziger Hack, aber es sollte funktionieren und hat nicht die Nachteile, die Deckkraft zu ändern:

  • setze WindowStartupLocationauf Manualname__
  • legen Sie die Eigenschaften Topund Leftaußerhalb des Bildschirms fest
  • setzen Sie ShowInTaskbarauf false, damit der Benutzer nicht erkennt, dass ein neues Fenster vorhanden ist
  • Showund Hidedas Fenster

Sie sollten jetzt in der Lage sein, die HWND abzurufen

BEARBEITEN: Eine andere Option, wahrscheinlich besser: Setzen Sie ShowInTaskBarauf false und WindowStateauf Minimizedname__, und zeigen Sie es dann an: es ist überhaupt nicht sichtbar

16
Thomas Levesque

Ich hatte bereits eine Antwort auf diese Frage gepostet, fand aber gerade eine bessere Lösung.

Wenn Sie nur sicherstellen müssen, dass die HWND erstellt wird, ohne das Fenster tatsächlich anzuzeigen, können Sie Folgendes tun:

    public void InitHwnd()
    {
        var helper = new WindowInteropHelper(this);
        helper.EnsureHandle();
    }

(Die EnsureHandle-Methode war zum Zeitpunkt der Veröffentlichung der Frage nicht verfügbar, sie wurde in .NET 4.0 eingeführt.)

10
Thomas Levesque

Ich habe nie versucht, das zu tun, was Sie tun, aber wenn Sie das Fenster anzeigen müssen, um die HWND zu erhalten, aber nicht show it möchten, setzen Sie die Fenster-Opazität auf 0. Dadurch werden auch alle verhindert Hit-Tests vom Auftreten. Dann könnten Sie eine öffentliche Methode im Fenster verwenden, um die Deckkraft auf 100 zu ändern, wenn Sie sie sichtbar machen möchten.

5
Joel Cochran

Ich kenne absolut nichts über WPF, aber könnten Sie ein message only window mit anderen Mitteln erstellen (zB PInvoke), um die WM_HOTKEY-Nachricht zu erhalten? Wenn ja, können Sie nach Erhalt des WM_HOTKEY das WPF-Fenster von dort aus starten.

3
Agnel Kurian

Ich habe bemerkt, dass das letzte, was passiert, wenn das Fenster initialisiert wird, die Änderung von WindowState ist, wenn es sich von der normalen unterscheidet. Sie können es also tatsächlich nutzen:

public void InitializeWindow(Window window) {
    window.Top = Int32.MinValue;
    window.Left = Int32.MinValue;

    window.Width = 0;
    window.Height = 0;

    window.ShowActivated = false;
    window.ShowInTaskbar = false;
    window.Opacity = 0;

    window.StateChanged += OnBackgroundStateChanged;

    window.WindowStyle = WindowStyle.None;
}

public void ShowWindow(Window window) {
    window.Show();
    window.WindowState = WindowState.Maximized;
}

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.Top = 300;
        window.Left = 200;

        window.Width = 760;
        window.Height = 400;

        window.WindowState = WindowState.Normal;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

Das geht mir gut. Und es erfordert keine Arbeit mit irgendwelchen Ziehpunkten und Sachen und, was noch wichtiger ist, keine benutzerdefinierte Klasse für ein Fenster. Das ist ideal für dynamisch geladene XAML. Dies ist auch eine großartige Möglichkeit, wenn Sie eine Vollbild-App erstellen. Sie müssen nicht einmal den Status wieder in den normalen Zustand zurückversetzen oder die richtige Breite und Höhe einstellen. Geh einfach mit

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

Und du bist fertig.

Und selbst wenn ich falsch bin, dass die Änderung des Zustands das letzte ist, wenn Fenster geladen wird, können Sie immer noch zu jedem anderen Ereignis wechseln, es spielt keine Rolle.

0
Nullcaller

Stellen Sie die Größe des Fensters auf 0 x 0 px, setzen Sie ShowInTaskBar auf false, zeigen Sie es an und passen Sie es bei Bedarf an.

0
luvieere

Die WindowInteropHelper-Klasse sollte es Ihnen ermöglichen, die HWND für das WPF-Fenster abzurufen.

MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);

IntPtr hwnd = helper.Handle;

MSDN-Dokumentation

0
Shannon Cornish

Eine andere Option in ähnlicher Weise zum Einstellen der Deckkraft auf 0 ist die Einstellung der Größe auf 0 und die Einstellung der Position außerhalb des Bildschirms. Dies erfordert nicht die AllowsTransparency = True.

Denken Sie auch daran, wenn Sie es einmal gezeigt haben, können Sie es ausblenden und trotzdem das Hwnd erhalten.

0
Ben Childs

Ich habe eine Erweiterungsmethode zum Anzeigen eines unsichtbaren Fensters erstellt. Die nächsten Show-Aufrufe verhalten sich in Ordnung.

public static class WindowHelper
{
    public static void ShowInvisible(this Window window)
    {
        // saving original settings
        bool needToShowInTaskbar = window.ShowInTaskbar;
        WindowState initialWindowState = window.WindowState;

        // making window invisible
        window.ShowInTaskbar = false;
        window.WindowState = WindowState.Minimized;

        // showing and hiding window
        window.Show();
        window.Hide();

        // restoring original settings
        window.ShowInTaskbar = needToShowInTaskbar;
        window.WindowState = initialWindowState;
    }
}
0