2009-09-09 27 views
45

Creo una tecla de acceso directo global para mostrar una ventana mediante PInvoking RegisterHotKey(). Pero para hacer esto necesito esa ventana HWND, que no existe hasta que se cargue la ventana, eso significa que se muestra por primera vez. Pero no quiero mostrar la ventana antes de poder configurar la tecla de acceso rápido. ¿Hay alguna manera de crear un HWND para esa ventana que es invisible para el usuario?Cargando una ventana de WPF sin mostrarlo

Respuesta

56

Si el idioma de .NET 4.0 se puede hacer uso del nuevo método EnsureHandle disponible en el WindowInteropHelper:

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

(gracias a Thomas Levesque para pointing this out.)

Si el idioma de una versión anterior de .NET Framework, la forma más fácil es para mostrar la ventana para llegar a la HWND al establecer algunas propiedades para asegurarse de que la ventana es invisible y no se roba el foco:

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

una vez que desea mostrar la ventana real a continuación, puede establecer el contenido, el tamaño y cambiar el estilo de nuevo a una ventana normal.

+2

Sí, esto funciona, gracias. Establecer WindowState incluso no es necesario. Además, establezco el contenido de la ventana en XAML, pero eso no es importante. Otra cosa es que WindowStartupLocation = CenterScreen no funciona correctamente de esta manera, pero eso es fácil de arreglar. – svick

+0

eliminado el setter WindowState ... gracias por informarme. –

+1

Agregaría 'ResizeMode = ResizeMode.NoResize' también, porque elimina el borde de la ventana para cambiar el tamaño. – schnaader

0

La clase WindowInteropHelper debería permitirle obtener el HWND para la ventana de WPF.

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

IntPtr hwnd = helper.Handle; 

MSDN Documentation

+1

Eso es lo que estoy haciendo, pero de esta manera, la ventana no tiene un HWND todavía, entonces helper.Handle es 0, que no es lo que necesito. – svick

5

Nunca he tratado de hacer lo que está haciendo, pero si usted necesita para mostrar la ventana para obtener el HWND, pero no quiere mostrar , establezca el Opacidad de la ventana a 0. Esto también evitará que ocurra cualquier prueba de impacto. Entonces podría tener un método público en la Ventana para cambiar la Opacidad a 100 cuando quiera hacerla visible.

+0

Esto es una especie de hack sucio, pero funcionará con seguridad. –

+0

Desafortunadamente, para que la configuración de Opacidad sea efectiva, AllowsTransparency debe establecerse en true y esto a su vez fuerza a WindowStyle a WindowStyle.None, que no es lo que quiero. Además, AllowsTransparency no se puede cambiar después de que se muestre la ventana, por lo que no puedo volver a configurarlo después. – svick

0

Otra opción similar a configurar la opacidad en 0 es establecer el tamaño en 0 y configurar la posición para que no esté en la pantalla. Esto no requerirá AllowsTransparency = True.

También recuerde que una vez que lo haya mostrado una vez, puede ocultarlo y seguir obteniendo el hwnd.

16

Este es un truco sucio, pero debería funcionar, y no tiene las desventajas de cambiar la opacidad:

  • establecer el WindowStartupLocation a Manual
  • Top establezca las propiedades y Left a algún lugar fuera la pantalla
  • ShowInTaskbar establece en false para que el usuario no se da cuenta que hay una ventana nueva
  • Show y Hide º ventana de correo

Ahora debería ser capaz de recuperar el HWND

EDIT: otra opción, probablemente mejor: set ShowInTaskBar false y WindowState a Minimized, a continuación, mostrar que: no será visible en todo

+2

+1 ShowInTaskBar = falso y WindowState = Minimized funciona. –

+2

Con su otra opción, puedo ver la ventana minimizada en la esquina inferior izquierda de la pantalla. Pero el primero parece prometedor. – svick

+0

@svick: ¿qué SO estás usando? En Windows 7 la ventana minimizada no está visible –

0

Haga que el tamaño de la ventana sea de 0 x 0 px, ponga ShowInTaskBar en falso, muéstrelo y luego ajústelo cuando sea necesario.

3

No sé absolutamente nada sobre WPF, pero ¿podría crear un message only window utilizando otros medios (PInvoke por ejemplo) para recibir el mensaje WM_HOTKEY? En caso afirmativo, una vez que reciba el WM_HOTKEY, puede abrir la ventana de WPF desde allí.

+0

+1, puede usar una Ventana de Winforms en otro hilo si desea hacer esto. –

15

También puede cambiar la ventana a la llamada ventana de solo mensaje. Como este tipo de ventana no admite elementos gráficos, nunca se mostrará. Básicamente se trata de llamar a:

SetParent(hwnd, (IntPtr)HWND_MESSAGE); 

bien crear una ventana de mensaje específico que se encontrará siempre oculto, o utilizar la ventana de interfaz gráfica de usuario real y cambiarlo de nuevo a una ventana normal cuando se quiere mostrarlo. Vea el código a continuación para obtener un ejemplo más completo.

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

Para mí la solución de establecer el ancho, la altura a cero y estilo a ninguno no funcionó, ya que todavía mostraba una pequeña ventana, con una sombra molesta de lo que parece ser la frontera en torno a una Ventana 0x0 (probado en Windows 7). Por lo tanto, estoy proporcionando esta opción alternativa.

+1

Esta parece ser la única solución al 100%. –

+0

Gracias por este gran consejo.Realmente me ayudó, porque todas las otras soluciones aquí causaron algunos efectos secundarios que no fueron bonitos. Pero tu también lo hace, por desgracia. Obtuve una MetroWindow (usando Fluent Ribbon Suite). Luego, la ventana tiene un borde de ventana típico, que normalmente no es visible para esas MetroWindows ... ¿Alguna idea de cómo resolver esto? – SharpShade

+0

Perfecto. Solo necesita: ShowActivated = false; después de Visibilidad, porque sin él parpadea. – blez

10

Ya había publicado una respuesta a esa pregunta, pero acabo de encontrar una solución mejor.

Si sólo tiene que asegurarse de que se crea el HWND, sin llegar a mostrar la ventana, usted puede hacer esto:

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

(en realidad el método EnsureHandle no estaba disponible cuando la pregunta fue publicada, se introdujo en .NET 4.0)

+0

muy bien hecho Thomas – tofutim

+0

esto probablemente debería ser la respuesta aceptada ahora, o debería actualizar mi respuesta para incluir esto también? no estoy seguro de cuál es la práctica respetada para las diferencias de la versión del marco. –

+0

@PatrickKlug, no sé tampoco ... Puede incluir esto en su respuesta, o simplemente hacer referencia a mi respuesta, lo que crea que es mejor. –

0

He creado el método de extensión para mostrar la ventana invisible, las próximas Show llamadas se comportarán bien.

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