web-dev-qa-db-fra.com

Amener une fenêtre à l'avant dans WPF

Comment puis-je amener mon application WPF à l'avant du bureau? Jusqu'ici j'ai essayé:

SwitchToThisWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle, true);

SetWindowPos(new WindowInteropHelper(Application.Current.MainWindow).Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

Aucune d'entre elles ne fait le travail (Marshal.GetLastWin32Error() indique que ces opérations ont abouti et que les attributs P/Invoke de chaque définition ont SetLastError=true).

Si je crée une nouvelle application WPF vide et que j'appelle SwitchToThisWindow avec une minuterie, elle fonctionne exactement comme prévu. Par conséquent, je ne sais pas pourquoi cela ne fonctionne pas dans mon cas initial.

Edit : Je le fais en conjonction avec un raccourci clavier global.

188
Factor Mystic

Eh bien, j'ai trouvé un moyen de contourner le problème. Je passe l'appel depuis un crochet du clavier utilisé pour implémenter un raccourci clavier. L'appel fonctionne comme prévu si je le mets dans un BackgroundWorker avec une pause. C'est un kludge, mais je n'ai aucune idée pourquoi cela ne fonctionnait pas à l'origine.

void hotkey_execute()
{
    IntPtr handle = new WindowInteropHelper(Application.Current.MainWindow).Handle;
    BackgroundWorker bg = new BackgroundWorker();
    bg.DoWork += new DoWorkEventHandler(delegate
        {
            Thread.Sleep(10);
            SwitchToThisWindow(handle, true);
        });
    bg.RunWorkerAsync();
}
2
Factor Mystic
myWindow.Activate();

Essaie de placer la fenêtre au premier plan et de l'activer.

Cela devrait faire l'affaire, à moins que je ne comprenne mal et que vous souhaitiez un comportement de type Always on Top. Dans ce cas, vous voulez:

myWindow.TopMost = true;
277
Morten Christiansen

J'ai trouvé une solution qui amène la fenêtre au sommet, mais elle se comporte comme une fenêtre normale:

if (!Window.IsVisible)
{
    Window.Show();
}

if (Window.WindowState == WindowState.Minimized)
{
    Window.WindowState = WindowState.Normal;
}

Window.Activate();
Window.Topmost = true;  // important
Window.Topmost = false; // important
Window.Focus();         // important
151
Jader Dias

Si vous avez besoin que la fenêtre se trouve au premier chargement, utilisez les éléments suivants:

private void Window_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
}

private void Window_Initialized(object sender, EventArgs e)
{
    this.Topmost = true;
}
23
Amir

Pour en faire un copier-coller rapide -
Utilisez la méthode 'DoOnProcess de cette classe pour déplacer la fenêtre principale du processus au premier plan (mais pas pour dérober le focus d'autres fenêtres) 

public class MoveToForeground
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);

    const int SWP_NOMOVE        = 0x0002;
    const int SWP_NOSIZE        = 0x0001;            
    const int SWP_SHOWWINDOW    = 0x0040;
    const int SWP_NOACTIVATE    = 0x0010;
    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);

    public static void DoOnProcess(string processName)
    {
        var allProcs = Process.GetProcessesByName(processName);
        if (allProcs.Length > 0)
        {
            Process proc = allProcs[0];
            int hWnd = FindWindow(null, proc.MainWindowTitle.ToString());
            // Change behavior by settings the wFlags params. See http://msdn.Microsoft.com/en-us/library/ms633545(VS.85).aspx
            SetWindowPos(new IntPtr(hWnd), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
        }
    }
}

HTH

19
Hertzel Guinness

Je sais que cette question est plutôt ancienne, mais je viens de rencontrer ce scénario précis et je voulais partager la solution que j'ai mise en œuvre.

Comme mentionné dans les commentaires sur cette page, plusieurs des solutions proposées ne fonctionnent pas sous XP, que je dois prendre en charge dans mon scénario. Bien que je partage l’opinion de @Matthew Xavier selon laquelle il s’agit en général d’une mauvaise pratique de l’expérience utilisateur, il ya des moments où c’est une expérience entièrement plausable.

La solution pour amener une fenêtre WPF vers le haut m'a en fait été fournie par le même code que celui que j'utilise pour fournir le raccourci clavier global. Un article de blog de Joseph Cooney contient un lien vers ses exemples de code qui contient le code original.

J'ai un peu nettoyé et modifié le code et je l'ai implémenté comme méthode d'extension à System.Windows.Window. J'ai testé cela sur XP 32 bits et Win7 64 bits, qui fonctionnent tous les deux correctement.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace System.Windows
{
    public static class SystemWindows
    {
        #region Constants

        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        #endregion

        /// <summary>
        /// Activate a window from anywhere by attaching to the foreground window
        /// </summary>
        public static void GlobalActivate(this Window w)
        {
            //Get the process ID for this window's thread
            var interopHelper = new WindowInteropHelper(w);
            var thisWindowThreadId = GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);

            //Get the process ID for the foreground window's thread
            var currentForegroundWindow = GetForegroundWindow();
            var currentForegroundWindowThreadId = GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero);

            //Attach this window's thread to the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);

            //Set the window position
            SetWindowPos(interopHelper.Handle, new IntPtr(0), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

            //Detach this window's thread from the current window's thread
            AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);

            //Show and activate the window
            if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal;
            w.Show();
            w.Activate();
        }

        #region Imports

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        #endregion
    }
}

J'espère que ce code aide les autres qui rencontrent ce problème.

17
Zodman

Si l'utilisateur interagit avec une autre application, il ne sera peut-être pas possible d'amener la vôtre au premier plan. En règle générale, un processus ne peut espérer définir la fenêtre de premier plan que si ce processus est déjà le processus de premier plan. (Microsoft documente les restrictions dans l'entrée SetForegroundWindow () MSDN.) Cela est dû au fait que:

  1. L'utilisateur "possède" l'avant-plan. Par exemple, il serait extrêmement ennuyeux si un autre programme volait l'avant-plan pendant que l'utilisateur tapait, interrompant à tout le moins son flux de travail et risquant de provoquer des conséquences inattendues, du fait que les frappes au clavier correspondant à une application seraient mal interprétées jusqu'à ce qu'elle remarque le changement. .
  2. Imaginez que chacun des deux programmes vérifie si sa fenêtre est au premier plan et tente de la définir au premier plan si ce n'est pas le cas. Dès que le deuxième programme est en cours d'exécution, l'ordinateur devient inutilisable car l'avant-plan rebondit entre les deux à chaque commutation de tâches.
13
Matthew Xavier

J'ai eu un problème similaire avec une application WPF qui est appelée à partir d'une application Access via l'objet Shell.

Ma solution est la suivante: fonctionne sous XP et Win7 x64 avec une application compilée sur une cible x86.

Je préférerais de beaucoup faire cela plutôt que de simuler un alt-tab.

void Window_Loaded(object sender, RoutedEventArgs e)
{
    // make sure the window is normal or maximised
    // this was the core of the problem for me;
    // even though the default was "Normal", starting it via Shell minimised it
    this.WindowState = WindowState.Normal;

    // only required for some scenarios
    this.Activate();
}
6
Seth

Je sais que c'est une réponse tardive, peut-être utile pour les chercheurs

 if (!WindowName.IsVisible)
 {
     WindowName.Show();
     WindowName.Activate();
 }
6
Jamaxack

Pourquoi certaines des réponses sur cette page sont fausses!

  • Toute réponse utilisant window.Focus() est fausse. 

    • Pourquoi? Si un message de notification apparaît, window.Focus() détournera le focus de tout ce que l'utilisateur est en train de taper à ce moment-là. C'est incroyablement frustrant pour les utilisateurs finaux, surtout si les popups se produisent assez fréquemment.
  • Toute réponse utilisant window.Activate() est fausse.

    • Pourquoi? Cela rendra également visible toutes les fenêtres parentes.
  • Toute réponse qui omet window.ShowActivated = false est fausse .
    • Pourquoi? Lorsque le message apparaît, cela détourne l'attention de la fenêtre suivante, ce qui est très gênant!
  • Toute réponse qui n'utilise pas Visibility.Visible pour masquer/afficher la fenêtre est fausse .
    • Pourquoi? Si nous utilisons Citrix, si la fenêtre n’est pas réduite à la fermeture, elle laissera un étrange blocage rectangulaire noir à l’écran. Ainsi, nous ne pouvons pas utiliser window.Show() et window.Hide().

Essentiellement:

  • La fenêtre ne doit pas éloigner le focus des autres fenêtres lors de son activation;
  • La fenêtre ne doit pas activer son parent quand elle est affichée.
  • La fenêtre devrait être compatible avec Citrix.

Solution MVVM

Ce code est 100% compatible avec Citrix (pas de zones vierges de l'écran). Il est testé avec WPF normal et DevExpress.

Cette réponse est destinée à tout cas d'utilisation dans lequel nous souhaitons une petite fenêtre de notification toujours devant les autres fenêtres (si l'utilisateur le sélectionne dans les préférences).

Si cette réponse semble plus complexe que les autres, c'est qu'il s'agit d'un code robuste, de niveau entreprise. Certaines des réponses sur cette page sont simples, mais ne fonctionnent pas réellement.

XAML - Propriété attachée

Ajoutez cette propriété attachée à une variable UserControl dans la fenêtre. La propriété attachée va:

  • Attendez que l'événement Loaded soit déclenché (sinon, il ne peut pas rechercher d'arborescence visuelle pour rechercher la fenêtre parente).
  • Ajoutez un gestionnaire d'événements garantissant que la fenêtre est visible ou non.

A tout moment, vous pouvez définir la fenêtre pour qu'elle soit devant ou non, en retournant la valeur de la propriété attachée.

<UserControl x:Class="..."
         ...
         attachedProperties:EnsureWindowInForeground.EnsureWindowInForeground=
             "{Binding EnsureWindowInForeground, Mode=OneWay}">

C # - Méthode d'assistance

public static class HideAndShowWindowHelper
{
    /// <summary>
    ///     Intent: Ensure that small notification window is on top of other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoForeground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Visible)
            {
                window.Visibility = Visibility.Visible;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = true;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }

    /// <summary>
    ///     Intent: Ensure that small notification window can be hidden by other windows.
    /// </summary>
    /// <param name="window"></param>
    public static void ShiftWindowIntoBackground(Window window)
    {
        try
        {
            // Prevent the window from grabbing focus away from other windows the first time is created.
            window.ShowActivated = false;

            // Do not use .Show() and .Hide() - not compatible with Citrix!
            if (window.Visibility != Visibility.Collapsed)
            {
                window.Visibility = Visibility.Collapsed;
            }

            // We can't allow the window to be maximized, as there is no de-maximize button!
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;
            }

            window.Topmost = false;
        }
        catch (Exception)
        {
            // Gulp. Avoids "Cannot set visibility while window is closing".
        }
    }
}

Usage

Pour utiliser cela, vous devez créer la fenêtre dans votre ViewModel:

private ToastView _toastViewWindow;
private void ShowWindow()
{
    if (_toastViewWindow == null)
    {
        _toastViewWindow = new ToastView();
        _dialogService.Show<ToastView>(this, this, _toastViewWindow, true);
    }
    ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(_toastViewWindow);
    HideAndShowWindowHelper.ShiftWindowIntoForeground(_toastViewWindow);
}

private void HideWindow()
{
    if (_toastViewWindow != null)
    {
        HideAndShowWindowHelper.ShiftWindowIntoBackground(_toastViewWindow);
    }
}

Liens supplémentaires

Pour savoir comment faire en sorte qu'une fenêtre de notification réapparaisse toujours sur l'écran visible, reportez-vous à ma réponse: Sous WPF, comment déplacer une fenêtre sur l'écran si elle est hors écran? .

5
Contango

Eh bien, comme il s'agit d'un sujet d'actualité, voici ce qui fonctionne pour moi. J'ai des erreurs si je ne l'ai pas fait de cette façon car Activate () se trompera si vous ne pouvez pas voir la fenêtre.

Xaml:

<Window .... 
        Topmost="True" 
        .... 
        ContentRendered="mainWindow_ContentRendered"> .... </Window>

Codebhind:

private void mainWindow_ContentRendered(object sender, EventArgs e)
{
    this.Topmost = false;
    this.Activate();
    _UsernameTextBox.Focus();
}

C’était la seule façon pour moi d’obtenir la fenêtre à montrer en haut. Activez-le ensuite pour pouvoir taper dans la case sans avoir à définir le focus avec la souris. control.Focus () ne fonctionnera que si la fenêtre est active ();

4
Omzig

Pour afficher TOUTE fenêtre ouverte, importez ces DLL:

public partial class Form1 : Form
{
    [DllImportAttribute("User32.dll")]
    private static extern int FindWindow(String ClassName, String WindowName);
    [DllImportAttribute("User32.dll")]
    private static extern int SetForegroundWindow(int hWnd);

et dans le programme Nous recherchons une application dont le titre est spécifié (écrivez le titre sans première lettre (index> 0))

  foreach (Process proc in Process.GetProcesses())
                {
                    tx = proc.MainWindowTitle.ToString();
                    if (tx.IndexOf("Title of Your app WITHOUT FIRST LETTER") > 0)
                    {
                        tx = proc.MainWindowTitle;
                        hWnd = proc.Handle.ToInt32(); break;
                    }
                }
                hWnd = FindWindow(null, tx);
                if (hWnd > 0)
                {
                    SetForegroundWindow(hWnd);
                }
2
rahmud

Le problème peut être que le thread appelant votre code à partir du hook n’a pas été initialisé par le runtime; les méthodes d’appel runtime ne fonctionnent donc pas.

Peut-être que vous pourriez essayer de faire un Invoke pour diriger votre code sur le thread d'interface utilisateur pour appeler votre code qui met la fenêtre au premier plan.

1
joshperry

Ces codes fonctionneront bien à tout moment.

D'abord, définissez le gestionnaire d'événements activé dans XAML:

Activated="Window_Activated"

Ajoutez la ligne ci-dessous à votre bloc constructeur de la fenêtre principale:

public MainWindow()
{
    InitializeComponent();
    this.LocationChanged += (sender, e) => this.Window_Activated(sender, e);
}

Et à l'intérieur du gestionnaire d'événements activé, copiez ces codes:

private void Window_Activated(object sender, EventArgs e)
{
    if (Application.Current.Windows.Count > 1)
    {
        foreach (Window win in Application.Current.Windows)
            try
            {
                if (!win.Equals(this))
                {
                    if (!win.IsVisible)
                    {
                        win.ShowDialog();
                    }

                    if (win.WindowState == WindowState.Minimized)
                    {
                        win.WindowState = WindowState.Normal;
                    }

                    win.Activate();
                    win.Topmost = true;
                    win.Topmost = false;
                    win.Focus();
                }
            }
            catch { }
    }
    else
        this.Focus();
}

Ces étapes fonctionneront bien et afficheront toutes les autres fenêtres dans la fenêtre de leurs parents.

1
Matrix

Si vous essayez de masquer la fenêtre, par exemple si vous réduisez la fenêtre, j’ai constaté que l’utilisation de 

    this.Hide();

va le cacher correctement, puis simplement en utilisant 

    this.Show();

montrera alors la fenêtre comme l'élément le plus haut encore une fois.

0
Chris

Je voulais juste ajouter une autre solution à cette question. Cette implémentation fonctionne pour mon scénario, où CaliBurn est responsable de l'affichage de la fenêtre principale.

protected override void OnStartup(object sender, StartupEventArgs e)
{
    DisplayRootViewFor<IMainWindowViewModel>();

    Application.MainWindow.Topmost = true;
    Application.MainWindow.Activate();
    Application.MainWindow.Activated += OnMainWindowActivated;
}

private static void OnMainWindowActivated(object sender, EventArgs e)
{
    var window = sender as Window;
    if (window != null)
    {
        window.Activated -= OnMainWindowActivated;
        window.Topmost = false;
        window.Focus();
    }
}
0
d.moncada

N'oubliez pas de ne pas mettre le code qui montre cette fenêtre dans un gestionnaire PreviewMouseDoubleClick car la fenêtre active reviendra à la fenêtre qui a géré l'événement. Il suffit de le mettre dans le gestionnaire d’événement MouseDoubleClick ou d’arrêter le bouillonnement en définissant e.Handled sur True. 

Dans mon cas, je gérais PreviewMouseDoubleClick sur une liste et ne définissais pas le paramètre e.Handled = true, alors il soulevait l'événement MouseDoubleClick qui renvoyait à la fenêtre d'origine.

0
Michel P.

J'ai construit une méthode d'extension pour faciliter la réutilisation.

using System.Windows.Forms;
    namespace YourNamespace{
        public static class WindowsFormExtensions {
            public static void PutOnTop(this Form form) {
                form.Show();
                form.Activate();
            }// END PutOnTop()       
        }// END class
    }// END namespace

Appeler dans le constructeur de formulaire 

namespace YourNamespace{
       public partial class FormName : Form {
       public FormName(){
            this.PutOnTop();
            InitalizeComponents();
        }// END Constructor
    } // END Form            
}// END namespace
0
Mike