ventanas ventana una traer tener siempre resto primer poner plano navegacion mantener frente formulario form entre encima emergente control aplicacion c# .net wpf winapi pinvoke

c# - traer - Trae una ventana al frente en WPF



traer ventana al frente c# (18)

¿Cómo puedo llevar mi aplicación WPF al frente del escritorio? Hasta ahora lo he intentado:

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

Ninguno de los cuales está haciendo el trabajo ( Marshal.GetLastWin32Error() dice que estas operaciones se completaron con éxito, y los atributos de P / Invoke para cada definición tienen SetLastError=true ).

Si creo una nueva aplicación WPF en blanco y llamo a SwitchToThisWindow con un temporizador, funciona exactamente como se esperaba, así que no estoy seguro de por qué no funciona en mi caso original.

Editar : estoy haciendo esto junto con una tecla de acceso rápido global.


¿Por qué algunas de las respuestas en esta página son incorrectas?

  • Cualquier respuesta que use window.Focus() es incorrecta.

    • ¿Por qué? Si aparece un mensaje de notificación, window.Focus() el foco de lo que esté escribiendo el usuario en ese momento. Esto es increíblemente frustrante para los usuarios finales, especialmente si las ventanas emergentes ocurren con bastante frecuencia.
  • Cualquier respuesta que use window.Activate() es incorrecta.

    • ¿Por qué? También hará visibles las ventanas principales.
  • Cualquier respuesta que omita window.ShowActivated = false es incorrecta.
    • ¿Por qué? Agarrará el foco de otra ventana cuando aparezca el mensaje que es muy molesto.
  • Cualquier respuesta que no use Visibility.Visible para ocultar / mostrar la ventana es incorrecta.
    • ¿Por qué? Si estamos usando Citrix, si la ventana no se contrae cuando está cerrada, dejará una extraña retención rectangular negra en la pantalla. Por lo tanto, no podemos usar window.Show() y window.Hide() .

Esencialmente:

  • La ventana no debe quitar el foco de ninguna otra ventana cuando se activa;
  • La ventana no debe activar su padre cuando se muestra;
  • La ventana debe ser compatible con Citrix.

Solución MVVM

Este código es 100% compatible con Citrix (sin áreas en blanco de la pantalla). Se prueba con WPF normal y DevExpress.

Esta respuesta está pensada para cualquier caso de uso en el que deseemos una pequeña ventana de notificación que esté siempre frente a otras ventanas (si el usuario selecciona esto en las preferencias).

Si esta respuesta parece más compleja que las otras, es porque es un código robusto de nivel empresarial. Algunas de las otras respuestas en esta página son simples, pero en realidad no funcionan.

XAML - Propiedad adjunta

Agregue esta propiedad adjunta a cualquier UserControl dentro de la ventana. La propiedad adjunta:

  • Espere hasta que se Loaded evento Loaded (de lo contrario, no puede buscar el árbol visual para encontrar la ventana primaria).
  • Agregue un controlador de eventos que asegure que la ventana esté visible o no.

En cualquier punto, puede establecer que la ventana esté delante o no, volteando el valor de la propiedad adjunta.

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

C # - Método auxiliar

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". } } }

Uso

Para usar esto, debe crear la ventana en su 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); } }

Enlaces adicionales

Para obtener sugerencias sobre cómo garantizar que una ventana de notificación siempre cambie a la pantalla visible, consulte mi respuesta: en WPF, ¿cómo desplazar una ventana hacia la pantalla si está fuera de la pantalla? .


Bueno, me di cuenta de un trabajo completo. Estoy haciendo la llamada desde un gancho de teclado utilizado para implementar una tecla de acceso directo. La llamada funciona como se espera si la pongo en un BackgroundWorker con una pausa. Es una tontería, pero no tengo idea de por qué no funcionaba originalmente.

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


Bueno, ya que este es un tema tan candente ... aquí está lo que funciona para mí. Obtuve errores si no lo hice de esta manera porque Activate () se equivocará si no puede ver la ventana.

Xaml:

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

Código detrás:

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

Esta era la única forma en que podía mostrar la ventana en la parte superior. Luego, actívelo para que pueda escribir en el cuadro sin tener que establecer el foco con el mouse. control.Focus () no funcionará a menos que la ventana sea Active ();


Construí un método de extensión para facilitar la reutilización.

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

Llamar en el formulario Constructor

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


El problema podría ser que el hilo que llama al código desde el gancho no haya sido inicializado por el tiempo de ejecución, por lo que los métodos de ejecución no funcionan.

Tal vez podría intentar hacer una Invocación para ordenar su código en el hilo de la interfaz de usuario para llamar a su código que lleva la ventana al primer plano.


En caso de que necesite que la ventana esté en frente la primera vez que se carga, debe usar lo siguiente:

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


Estos códigos funcionarán bien todo el tiempo.

Al principio configura el controlador de eventos activado en XAML:

Activated="Window_Activated"

Agregue la línea siguiente a su bloque constructor de la ventana principal:

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

Y dentro del controlador de eventos activado copie estos códigos:

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

Estos pasos funcionarán bien y traerán al frente todas las otras ventanas en la ventana de sus padres.


He encontrado una solución que lleva la ventana a la cima, pero se comporta como una ventana normal:

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


He tenido un problema similar con una aplicación WPF que se invoca desde una aplicación de Access a través del objeto Shell.

Mi solución está a continuación: funciona en XP y Win7 x64 con la aplicación compilada para el objetivo x86.

Prefiero hacer esto que simular una 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(); }


Para hacer esto un rápido copiar y pegar uno -
Utilice esta clase '' DoOnProcess método para mover proceso'' ventana principal a primer plano (pero no para robar el foco de otras ventanas)

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


Para mostrar CUALQUIER ventana abierta actualmente, importe esas 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);

y en el programa Buscamos la aplicación con el título especificado (escriba el título sin primera letra (índice> 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); }


Recuerde no colocar el código que muestra esa ventana dentro de un controlador PreviewMouseDoubleClick ya que la ventana activa volverá a la ventana que manejó el evento. Simplemente colóquelo en el controlador de eventos MouseDoubleClick o deje de burbujear configurando e.Handled en True.

En mi caso, estaba manejando el PreviewMouseDoubleClick en una vista de lista y no estaba configurando e.Handled = true, entonces se activó el evento MouseDoubleClick que volvió a enfocar la ventana original.


Sé que esta pregunta es bastante antigua, pero acabo de encontrarme con este escenario preciso y quería compartir la solución que implementé.

Como mencioné en los comentarios en esta página, varias de las soluciones propuestas no funcionan en XP, que necesito admitir en mi escenario. Si bien estoy de acuerdo con el sentimiento de @Matthew Xavier de que, en general, esta es una mala práctica de UX, hay veces en que es completamente un UX plausible.

La solución para llevar una ventana de WPF a la cima me la proporcionó el mismo código que estoy usando para proporcionar la tecla de acceso rápido global. Un artículo de blog de Joseph Cooney contiene un enlace a sus ejemplos de código que contiene el código original.

Limpié y modifiqué un poco el código, y lo implementé como un método de extensión para System.Windows.Window. He probado esto en XP 32 bit y Win7 64 bit, ambos funcionan correctamente.

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

Espero que este código ayude a otros que enfrentan este problema.


Sé que esto es una respuesta tardía, tal vez útil para los investigadores

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


Si el usuario está interactuando con otra aplicación, puede que no sea posible llevar la suya al frente. Como regla general, un proceso solo puede esperar establecer la ventana de primer plano si ese proceso ya es el proceso en primer plano. (Microsoft documenta las restricciones en la entrada de MSDN SetForegroundWindow() . Esto se debe a que:

  1. El usuario "posee" el primer plano. Por ejemplo, sería extremadamente molesto si otro programa robara el primer plano mientras el usuario está escribiendo, al menos interrumpiendo su flujo de trabajo, y posiblemente causando consecuencias imprevistas, ya que sus pulsaciones de teclas para una aplicación son malinterpretadas por el delincuente hasta que nota el cambio .
  2. Imagine que cada uno de los dos programas comprueba si su ventana es el primer plano e intenta establecerlo en primer plano si no lo está. Tan pronto como se ejecuta el segundo programa, la computadora se vuelve inútil ya que el primer plano rebota entre los dos en cada cambio de tarea.

Si está tratando de ocultar la ventana, por ejemplo, minimiza la ventana, he encontrado que usar

this.Hide();

lo ocultará correctamente, y luego simplemente usará

this.Show();

luego mostrará la ventana como el elemento más alto una vez más.


Solo quería agregar otra solución a esta pregunta. Esta implementación funciona para mi escenario, donde CaliBurn es responsable de mostrar la ventana principal.

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


myWindow.Activate();

Intenta llevar la ventana al primer plano y lo activa.

Eso debería ser el truco, a menos que lo malinterprete y quiera un comportamiento Siempre activo. En ese caso, usted quiere:

myWindow.TopMost = true;