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.
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();
}
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;
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
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;
}
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
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.
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:
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();
}
Je sais que c'est une réponse tardive, peut-être utile pour les chercheurs
if (!WindowName.IsVisible)
{
WindowName.Show();
WindowName.Activate();
}
Toute réponse utilisant window.Focus()
est fausse.
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.
window.ShowActivated = false
est fausse .Visibility.Visible
pour masquer/afficher la fenêtre est fausse .window.Show()
et window.Hide()
.Essentiellement:
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.
Ajoutez cette propriété attachée à une variable UserControl
dans la fenêtre. La propriété attachée va:
Loaded
soit déclenché (sinon, il ne peut pas rechercher d'arborescence visuelle pour rechercher la fenêtre parente).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}">
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".
}
}
}
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);
}
}
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? .
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 ();
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);
}
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.
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.
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.
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();
}
}
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.
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