2012-03-21 12 views
12

Estoy intentando centrar una ventana en la ventana del propietario. También necesito que la ventana secundaria se mueva a lo largo de la ventana del propietario. Se puede encontrar una publicación cruzada en el foro de MSDN WPF here.Los valores superiores e inferiores de la ventana no se actualizan correctamente al maximizar una ventana en .NET 4

Para lograr esto me suscribo a los eventos LocationChanged y SizeChanged (y también el evento StateChanged) del propietario de la ventana de mi hijo. Cuando esos eventos se desencadenan, vuelvo a calcular la ubicación de la ventana secundaria. Lo hago en el código subyacente de la ventana secundaria.

El código es muy sencillo:

Top = Owner.Top + ((Owner.ActualHeight - ActualHeight)/2); 
Left = Owner.Left + ((Owner.ActualWidth - ActualWidth)/2); 

Si compilar y ejecutar el programa de ejemplo que he proporcionado verá que funciona cuando la ventana principal es tal y como son, y se trasladó alrededor. Entonces esa parte funciona

El problema surge cuando se maximiza la ventana del propietario. (Y después de maximizarlo, vuelve a la normalidad.) Debido a que me suscribo a tres eventos, entro a la función de reubicación tres veces. Después de imprimir los datos del propietario, obtengo resultados diferentes. Lo más molesto es que los valores Top y Left de la ventana del propietario están desactivados. Parece que obtiene los valores correctos superior e izquierdo cuando el estado cambia, pero los valores ActualWidth y ActualHeight son incorrectos. Cuando se desencadenan los eventos LocationChanged o SizeChanged, los valores ActualWidth y ActualHeight son correctos, pero los valores superior e izquierdo son incorrectos. Parece que estos son los valores anteriores. ¿Cómo puede ser esto? ¿Qué está causando esto? ¿Y hay una solución adecuada para esto?

Dado que el mismo código parece haber funcionado en .net 3.5, tengo la impresión de que algo cambió en .net 4. (O tuve un problema de sincronización extraño que hizo que el problema no apareciera). Pero no puedo encontrar ningún cambio documentado en esta parte.

.NET 3.5:

OnOwnerLocationChanged 
T: -8; L: -8; W: 640; H: 480 
OnOwnerStateChanged 
T: -8; L: -8; W: 640; H: 480 
OnOwnerSizeChanged 
T: -8; L: -8; W: 1936; H: 1066 

.NET 4.0:

OnOwnerLocationChanged 
T: -8; L: -8; W: 640; H: 480 
OnOwnerStateChanged 
T: 494; L: 33; W: 640; H: 480 
OnOwnerSizeChanged 
T: 494; L: 33; W: 1936; H: 1066 

Así que la pregunta principal sigue siendo: ¿por qué son los valores de izquierda del propietario superior e incorrecto?

+0

encontré un hilo en SO que menciona cómo atrapar la maximiaze/restaurar eventos anulando WndProc: http://stackoverflow.com/questions/1295999/event-when-a-window-gets-maximized-un-maximized Pruebe el código en la respuesta http://stackoverflow.com/a/3382336/137972 – Christoffer

+1

Desde la T y L del evento LocationChanged es correcto, puedo guardarlos en la memoria caché y usar la W y H del evento SizeChanged ya que ese evento viene DESPUÉS del LocationChanged incluso, pero al igual que su sugerencia es una solución. Prefiero ahora por qué este comportamiento es diferente. – Jensen

+0

Estoy haciendo estos cálculos en dos ventanas, en despachadores de diferencias y estoy muy agradecido de haber encontrado esta publicación. Al final, también utilicé el enfoque de almacenamiento en caché. – RichardOD

Respuesta

12

El comment de Mataniko con respecto a los problemas de migración en .NET 4.0 era correcta. Dado que mi código estaba funcionando cuando el WindowState se configuró en Normal, podría conservarlo. Solo tenía que prever algo cuando el WindowState era Maximized.

Implementé la función nativa GetWindowRect() para lograr esto, ya que podría darme las dimensiones adecuadas.

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 
} 

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); 

// Make sure RECT is actually OUR defined struct, not the windows rect. 
public static RECT GetWindowRectangle(Window window) 
{ 
    RECT rect; 
    GetWindowRect((new WindowInteropHelper(window)).Handle, out rect); 

    return rect; 
} 

continuación, cuando el Owner.WindowState es Maximized, utilizamos la función GetWindowRectangle para obtener las dimensiones reales. No nos importa un borde en este punto, pero si es necesario, puede incorporarse utilizando la función GetSystemMetrics.

if (Owner.WindowState == WindowState.Maximized) 
{ 
    var rect = GetWindowRectangle(Owner); 

    Top = rect.Top + ((rect.Bottom - ActualHeight)/2); 
    Left = rect.Left + ((rect.Right - ActualWidth)/2); 
} 
2

No sé si es un error en .NET 4.0 o el comportamiento previsto. Mi sospecha es que hay una condición de carrera entre los eventos registrados y los disparos reales. Aunque LocationChanged se activa primero, SizeChanged se registra primero con los valores todavía incorrectos de la ubicación. Puede solucionar esto fácilmente creando una variable local en la ventana secundaria que registra el Propietario arriba y la izquierda en el evento LocationChanged.

Ejemplo:

private Point _ownerLocation; 

private void OnOwnerLocationChanged(object sender, EventArgs e) 
    { 
     Console.WriteLine("OnOwnerLocationChanged"); 
     _ownerLocation = new Point(Owner.Top, Owner.Left); 
     SetLocationToOwner(); 
    } 

private void SetLocationToOwner() 
    { 
     if (IsVisible && (Owner != null)) 
     { 
      Console.WriteLine("T: {0}; L: {1}; W: {2}; H: {3}", Owner.Top, Owner.Left, Owner.ActualWidth, Owner.ActualHeight); 

      Top = _ownerLocation.X + ((Owner.ActualHeight - ActualHeight)/2); 
      Left = _ownerLocation.Y + ((Owner.ActualWidth - ActualWidth)/2); 
     } 
    } 
+1

Una cosa más: LocationChanged también se activa cuando la WindowState que no necesitan regístrese en el evento StateChanged y solo use los valores Top Left de los valores de LocationChanged y Width/Height de SizeChanged. Fuente: [MSDN] (http://msdn.microsoft.com/en-us/library/system.windows.window.locationchanged.aspx) – Mataniko

+0

Esto todavía plantea un problema. Si la ventana del propietario se maximizó ANTES de que se abra la ventana secundaria, la ventana secundaria no puede obtener valores correctos Superior e Izquierda y la ventana secundaria nuevamente se coloca incorrectamente. – Jensen

+0

En ese caso, puede registrarse para los eventos SizeChanged/LocationChanged en el formulario principal y abrir el formulario secundario con esos valores. Es un clunk pero parece una regresión en .NET 4.0. Podría valer la pena abrir un informe con MS, ya que esto todavía ocurre con .NET 4.5 – Mataniko

0

Ha intentado ..

int left, top, width, height; 
bool maximised; 

if (WindowState == FormWindowState.Maximized) 
{ 
    maximised = true; 
    left = RestoreBounds.X; 
    top = RestoreBounds.Y; 
    width = RestoreBounds.Width; 
    height = RestoreBounds.Height; 
} 
else 
{ 
    maximised = false; 
    left = Left; 
    top = Top; 
    width = Width; 
    height = Height; 
} 

Y para revertir (el orden es importante) ...

StartPosition = FormStartPosition.Manual; 
Location = new Point(left, top); 
Size = new Size(width, height); 

if (maximised) 
    WindowState = FormWindowState.Maximized; 
+0

De acuerdo con sus especificaciones (https://msdn.microsoft.com/en-us/library/system.windows.window.restorebounds(v=vs.110).aspx) RestoreBounds dice que es el límite de la ventana justo antes de que llegue Maximizado o minimizado Así que no, esta no sería la solución al problema en cuestión. Lamentablemente RestoreBounds ni siquiera le da lo que dice que al menos no en mi prueba en Windows 7 .NET 4.5 –

+0

Todo lo que puedo decir es que funciona para mí, pero la declaración 'si' es importante, necesita Verifique el estado de Windows antes de obtener los parámetros. La asignación de los parámetros es obviamente la inversa. – damichab

+0

He actualizado la respuesta en caso de que alguien más haya tenido problemas con ella. ¡Tarde lo sé! – damichab

Cuestiones relacionadas