otra - Cargando una ventana de WPF sin mostrarlo
abrir una ventana dentro de otra wpf (11)
Creé el método de extensión para mostrar la ventana invisible, luego Show
llamadas se comportará 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;
}
}
Creo una tecla rápida global para mostrar una ventana mediante Invocar RegisterHotKey()
. Pero para hacer esto necesito HWND
esa ventana, 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 sea invisible para el usuario?
Este es un truco sucio, pero debería funcionar, y no tiene los inconvenientes de cambiar la opacidad:
- establecer el
WindowStartupLocation
enManual
- establecer las propiedades
Top
eLeft
en algún lugar fuera de la pantalla - establece
ShowInTaskbar
en falso para que el usuario no se dé cuenta de que hay una nueva ventana -
Show
yHide
la ventana
Ahora debería poder recuperar el HWND
EDITAR: otra opción, probablemente mejor: establezca ShowInTaskBar
en falso y WindowState
en Minimized
, luego muéstrelo: no será visible en absoluto
Haga que el tamaño de la ventana sea de 0 x 0 px, ponga ShowInTaskBar en falso, muéstrelo y luego cambie el tamaño cuando sea necesario.
La clase WindowInteropHelper debería permitirle obtener HWND para la ventana de WPF.
MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);
IntPtr hwnd = helper.Handle;
Me di cuenta de que lo último que sucede cuando se inicializa la ventana es el cambio de WindowState
, si difiere de lo normal. Entonces, puedes usarlo:
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();
}
}
Eso funciona bastante bien para mí. Y no requiere trabajar con ningún manipulador ni nada, y, lo que es más importante, no requiere tener una clase personalizada para una ventana. Lo cual es genial para XAML cargado dinámicamente. Y también es una gran manera si estás haciendo una aplicación de pantalla completa. Ni siquiera necesita cambiar su estado a normal o establecer el ancho y la altura adecuados. Solo ve con
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
Y tu estas listo.
E incluso si me equivoco al suponer que el último cambio de estado se realiza cuando se carga la ventana, aún puede cambiar a cualquier otro evento, realmente no importa.
No sé absolutamente nada sobre WPF, pero ¿podría crear una ventana de solo mensaje 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í.
Nunca intenté hacer lo que está haciendo, pero si necesita mostrar la ventana para obtener el HWND, pero no desea mostrarlo , configure Opacidad de ventana en 0. Esto también evitará que ocurra cualquier prueba de detección. . Entonces podría tener un método público en la Ventana para cambiar la Opacidad a 100 cuando quiera hacerla visible.
Otra opción similar a establecer la opacidad en 0 es establecer el tamaño en 0 y configurar la posición para que esté fuera de la pantalla. Esto no requerirá AllowsTransparency = True.
También recuerde que una vez que lo haya mostrado una vez, puede ocultarlo y obtener el hwnd.
Si tiene como objetivo .NET 4.0, puede utilizar el nuevo método EnsureHandle
disponible en WindowInteropHelper
:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(Gracias a Thomas Levesque por señalar esto ) .
Si se dirige a una versión anterior de .NET Framework, la forma más fácil es mostrar la ventana para acceder al HWND mientras configura algunas propiedades para asegurarse de que la ventana sea invisible y no se robe 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 desee mostrar la ventana real, puede configurar el contenido, el tamaño y cambiar el estilo a una ventana normal.
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:
SetParent(hwnd, (IntPtr)HWND_MESSAGE);
Cree una ventana de mensaje dedicada que siempre estará oculta, o use la ventana de GUI real y cámbiela a una ventana normal cuando quiera mostrarla. 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 el estilo a ninguno no funcionó, ya que aún mostraba una pequeña ventana, con una sombra molesta de lo que parece ser el borde alrededor de una ventana de 0x0 (probado en Windows 7 ) Por lo tanto, estoy proporcionando esta opción alternativa.
Ya había publicado una respuesta a esa pregunta, pero acabo de encontrar una mejor solución.
Si solo necesita asegurarse de que se creó el HWND, sin mostrar realmente la ventana, puede hacer esto:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(en realidad, el método EnsureHandle
no estaba disponible cuando se publicó la pregunta, se introdujo en .NET 4.0)