web-dev-qa-db-fra.com

Chargement d'une fenêtre WPF sans l'afficher

Je crée un raccourci clavier global pour afficher une fenêtre à l'aide de PInvoking RegisterHotKey(). Mais pour ce faire, j'ai besoin de la variable HWND de cette fenêtre, qui n'existe que lorsque la fenêtre est chargée, c'est-à-dire affichée pour la première fois. Mais je ne veux pas afficher la fenêtre avant de pouvoir définir le raccourci clavier. Existe-t-il un moyen de créer une HWND pour cette fenêtre qui soit invisible pour l'utilisateur?

49
svick

Si vous ciblez .NET 4.0, vous pouvez utiliser la nouvelle méthode EnsureHandle disponible sur la WindowInteropHelper:

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

(merci à Thomas Levesque pour pour l'avoir signalé. )

Si vous ciblez une ancienne version du .NET Framework, le moyen le plus simple consiste à afficher la fenêtre pour accéder au HWND tout en définissant quelques propriétés pour vous assurer que la fenêtre est invisible et ne dérobe pas le focus:

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

Une fois que vous souhaitez afficher la fenêtre réelle, vous pouvez définir le contenu, la taille et redéfinir le style sur une fenêtre normale.

61
Patrick Klug

Vous pouvez également changer la fenêtre en une fenêtre appelée message uniquement. Comme ce type de fenêtre ne supporte pas les éléments graphiques, il ne sera jamais affiché. Fondamentalement, il s'agit d'appeler:

    SetParent(hwnd, (IntPtr)HWND_MESSAGE);

Soit créer une fenêtre de message dédiée qui sera toujours masquée, soit utiliser la fenêtre réelle de l’interface graphique et la replacer en fenêtre normale lorsque vous souhaitez l’afficher. Voir le code ci-dessous pour un exemple plus complet.

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

Pour moi, la solution consistant à régler la largeur, la hauteur à zéro et le style à aucun n'a pas fonctionné, car elle affichait toujours une petite fenêtre, avec une ombre agaçante de ce qui semblait être la bordure autour d'une fenêtre 0x0 (testé sous Windows 7). ). Par conséquent, je fournis cette option alternative.

17
DJP

C'est un sale bidouillage, mais il devrait fonctionner et ne présente pas l'inconvénient de changer d'opacité:

  • mettre la WindowStartupLocation à Manual
  • définissez les propriétés Top et Left en dehors de l'écran
  • définir ShowInTaskbar sur false pour que l'utilisateur ne réalise pas qu'il y a une nouvelle fenêtre
  • Show et Hide la fenêtre

Vous devriez maintenant pouvoir récupérer le HWND

EDIT: une autre option, probablement meilleure: définissez ShowInTaskBar sur false et WindowState sur Minimized, puis montrez-le: cela ne sera pas visible

16
Thomas Levesque

J'avais déjà posté une réponse à cette question, mais je viens de trouver une meilleure solution.

Si vous avez juste besoin de vous assurer que le HWND est créé, sans afficher la fenêtre, vous pouvez faire ceci:

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

(en fait, la méthode EnsureHandle n'était pas disponible lorsque la question a été publiée, elle a été introduite dans .NET 4.0)

10
Thomas Levesque

Je n'ai jamais essayé de faire ce que vous faites, mais si vous avez besoin d'afficher la fenêtre pour obtenir le HWND, mais que vous ne voulez pas le montrer, réglez l'opacité de la fenêtre sur 0. empêcher tout test de frappe de se produire. Vous pouvez ensuite avoir une méthode publique sur la fenêtre pour modifier l'opacité à 100 lorsque vous souhaitez la rendre visible.

5
Joel Cochran

Je ne connais absolument rien à WPF, mais pourriez-vous créer une fenêtre message uniquement en utilisant un autre moyen (PInvoke par exemple) pour recevoir le message WM_HOTKEY? Si oui, une fois que vous avez reçu le message WM_HOTKEY, vous pouvez lancer la fenêtre WPF à partir de là.

3
Agnel Kurian

J'ai remarqué que la dernière chose qui se produit lorsque la fenêtre est en cours d'initialisation est le changement de WindowState, si elle diffère de la normale. Donc, vous pouvez en faire usage:

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

Cela fonctionne assez bien pour moi. Et cela ne nécessite pas de manipuler des poignées ni d'autres éléments, et plus important encore, ne nécessite pas de classe personnalisée pour une fenêtre. Ce qui est génial pour XAML chargé dynamiquement. Et c'est aussi un excellent moyen de créer une application en plein écran. Vous n'avez même pas besoin de revenir à l'état normal ou de régler correctement la largeur et la hauteur. Il suffit d'aller avec

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

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

Et tu as fini.

Et même si je me trompe dans mon hypothèse selon laquelle le changement d'état est la dernière chose à faire lorsque la fenêtre est chargée, vous pouvez toujours changer d'événement, cela n'a pas d'importance.

0
Nullcaller

Faites la taille de la fenêtre 0 x 0 px, mettez ShowInTaskBar sur false, montrez-la, puis redimensionnez-la si nécessaire.

0
luvieere

La classe WindowInteropHelper devrait vous permettre d’obtenir le HWND pour la fenêtre WPF.

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

IntPtr hwnd = helper.Handle;

Documentation MSDN

0
Shannon Cornish

Une autre option dans le même ordre d'idées pour définir l'opacité sur 0 consiste à définir la taille sur 0 et à définir la position pour qu'elle ne soit pas à l'écran. Cela ne nécessitera pas la valeur AllowsTransparency = True.

Rappelez-vous également qu’une fois que vous l’avez montré une fois, vous pouvez le masquer tout en obtenant l’affichage.

0
Ben Childs

J'ai créé une méthode d'extension pour afficher une fenêtre invisible, les prochains appels Show se comporteront correctement.

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