2010-03-18 30 views
12

Tengo una ventana de WPF de tamaño variable que quiero restringir el redimensionamiento para que la relación de aspecto de la ventana se mantenga constante.Cambiar el tamaño de una ventana WPF, pero mantener las proporciones?

Idealmente me gustaría restringir la ubicación del mouse cuando la ventana se está cambiando de tamaño arrastrando una esquina a las posiciones que mantienen la relación de aspecto adecuada. Si se modifica el tamaño de un borde con el mouse, la otra dimensión debe cambiar al mismo tiempo.

¿Hay alguna manera simple de hacer esto o un buen ejemplo en línea que todos conozcan?

Si no aparecen mejores soluciones, publicaré lo que hice después de haberlo refinado un poco.

+0

ver si esto ayuda: http: // stackoverflow.com/questions/288954/how-do-i-keep-aspect-ratio-on-scalable-scrollable-content-in-wpf –

Respuesta

2

hace eso el truco:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { 

    if (sizeInfo.WidthChanged) this.Width = sizeInfo.NewSize.Height * aspect; 
    else this.Height = sizeInfo.NewSize.Width/aspect; 
} 

encontrado que here.

2

La respuesta anterior favorece el cambio de ancho sobre el cambio de altura por lo que si ajusta mucho la altura pero, debido al posicionamiento del mouse, el ancho también cambia un poco, el usuario seguirá viendo la misma ventana. Tengo este código que funciona con cambios porcentuales en cada dimensión que favorece el cambio más grande como la que el usuario está más interesado en

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) 
    { 
     var percentWidthChange = Math.Abs(sizeInfo.NewSize.Width - sizeInfo.PreviousSize.Width)/sizeInfo.PreviousSize.Width; 
     var percentHeightChange = Math.Abs(sizeInfo.NewSize.Height - sizeInfo.PreviousSize.Height)/sizeInfo.PreviousSize.Height; 

     if (percentWidthChange > percentHeightChange) 
      this.Height = sizeInfo.NewSize.Width/_aspectRatio; 
     else 
      this.Width = sizeInfo.NewSize.Height * _aspectRatio; 

     base.OnRenderSizeChanged(sizeInfo); 
    } 
+0

Gracias, resolvió mi problema. –

+1

Las matemáticas son perfectas, pero cuando se implementan en código, el cambio de tamaño es muy nervioso, nada fluido. – mandarin

0

En Ventana - se puede escuchar el mensaje de Win32 API simplemente:.

private double ratio = 1.33; // retio of 3:4 

     protected override void OnSourceInitialized(EventArgs e) 
     { 
      base.OnSourceInitialized(e); 
      HwndSource source = HwndSource.FromVisual(this) as HwndSource; 
      if (source != null) 
      { 
       source.AddHook(new HwndSourceHook(WinProc)); 
      } 
     } 

     public const Int32 WM_EXITSIZEMOVE = 0x0232; 
     private IntPtr WinProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled) 
     { 
      IntPtr result = IntPtr.Zero; 
      switch (msg) 
      { 
       case WM_EXITSIZEMOVE: 
        { 
         if (Width < Height) 
         { 
          Width = Height * ratio; 
         } 
         else 
         { 
          Height = Width/ratio; 
         } 
        } 
        break; 
      } 

      return result; 
     } 

En este código, siempre toma el lado más corto y lo configura para que sea igual al más largo. Siempre puede tomar el enfoque opuesto y configurar el más largo para que sea igual al más corto. He encontrado la solución aquí: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/b0df3d1f-e211-4f54-a079-09af0096410e

+0

El problema con eso es que 'WM_EXITSIZEMOVE' solo se envía a la ventana cuando el usuario finaliza el cambio de tamaño. El mensaje 'WM_SIZING' es el que se dispara continuamente, pero he tenido resultados espásticos y espasmódicos con eso. – lordcheeto

19

He encontrado una buena respuesta por Nir here. Todavía hay algunos defectos, básicamente cambiar el tamaño en la esquina superior derecha, la esquina inferior derecha y el lado inferior estará bien, otros lados y las esquinas no lo son. El lado positivo es que la relación de aspecto se mantiene suavemente todo el tiempo.

EDIT: Encontré una manera de eliminar la mayoría de los problemas. Cuando comienza el dimensionamiento, la dimensión que se ajustará artificialmente para mantener la relación de aspecto se determina ubicando la posición del mouse relativa a la ventana. Las únicas imperfecciones que encontré son que la posición de la ventana puede cambiar al cambiar el tamaño de las esquinas (excepto en la esquina inferior derecha).

xaml:

<Window x:Class="WpfApplication1.ConstantAspectRatioWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="ConstantAspectRatioWindow" MinHeight="100" MinWidth="150" SizeToContent="WidthAndHeight"> 
    <Grid> 
     <Border Width="300" Height="200" Background="Navy"/> 
     <Border Width="150" Height="100" Background="Yellow" /> 
    </Grid> 
</Window> 

Código atrás:

using System; 
using System.Collections.Generic; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Input; 
using System.Windows.Interop; 

namespace WpfApplication1 
{ 
    public partial class ConstantAspectRatioWindow : Window 
    { 
     private double _aspectRatio; 
     private bool? _adjustingHeight = null; 

     internal enum SWP 
     { 
      NOMOVE = 0x0002 
     } 
     internal enum WM 
     { 
      WINDOWPOSCHANGING = 0x0046, 
      EXITSIZEMOVE = 0x0232, 
     } 

     public ConstantAspectRatioWindow() 
     { 
      InitializeComponent(); 
      this.SourceInitialized += Window_SourceInitialized; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     internal struct WINDOWPOS 
     { 
      public IntPtr hwnd; 
      public IntPtr hwndInsertAfter; 
      public int x; 
      public int y; 
      public int cx; 
      public int cy; 
      public int flags; 
     } 

     [DllImport("user32.dll")] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     internal static extern bool GetCursorPos(ref Win32Point pt); 

     [StructLayout(LayoutKind.Sequential)] 
     internal struct Win32Point 
     { 
      public Int32 X; 
      public Int32 Y; 
     }; 

     public static Point GetMousePosition() // mouse position relative to screen 
     { 
      Win32Point w32Mouse = new Win32Point(); 
      GetCursorPos(ref w32Mouse); 
      return new Point(w32Mouse.X, w32Mouse.Y); 
     } 


     private void Window_SourceInitialized(object sender, EventArgs ea) 
     { 
      HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender); 
      hwndSource.AddHook(DragHook); 

      _aspectRatio = this.Width/this.Height; 
     } 

     private IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
     { 
      switch ((WM)msg) 
      { 
       case WM.WINDOWPOSCHANGING: 
        { 
         WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS)); 

         if ((pos.flags & (int)SWP.NOMOVE) != 0) 
          return IntPtr.Zero; 

         Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual; 
         if (wnd == null) 
          return IntPtr.Zero; 

         // determine what dimension is changed by detecting the mouse position relative to the 
         // window bounds. if gripped in the corner, either will work. 
         if (!_adjustingHeight.HasValue) 
         { 
          Point p = GetMousePosition(); 

          double diffWidth = Math.Min(Math.Abs(p.X - pos.x), Math.Abs(p.X - pos.x - pos.cx)); 
          double diffHeight = Math.Min(Math.Abs(p.Y - pos.y), Math.Abs(p.Y - pos.y - pos.cy)); 

          _adjustingHeight = diffHeight > diffWidth; 
         } 

         if (_adjustingHeight.Value) 
          pos.cy = (int)(pos.cx/_aspectRatio); // adjusting height to width change 
         else 
          pos.cx = (int)(pos.cy * _aspectRatio); // adjusting width to heigth change 

         Marshal.StructureToPtr(pos, lParam, true); 
         handled = true; 
        } 
        break; 
       case WM.EXITSIZEMOVE: 
        _adjustingHeight = null; // reset adjustment dimension and detect again next time window is resized 
        break; 
      } 

      return IntPtr.Zero; 
     } 
    } 
} 
+1

Esto funciona realmente, muy bien. Lo único es, ¿por qué no me permite cambiar el tamaño de los lados? Solo me permite desde la parte superior/inferior, o desde las diagonales. ¡Gracias! –

+2

@ofstream: el redimensionamiento debería funcionar ahora desde todos los ángulos. –

+1

¡Muchas gracias por su trabajo! –

2

Aunque esto no obliga a la ventana para ser de una proporción específica (tal como pidió el OP), he logrado obtener el CONTENIDO de una ventana para escalar, mientras se mantiene la relación de aspecto original, envolviendo los contenidos en un Viewbox y estableciendo la propiedad de estiramiento como Stretch="Uniform". No se necesita código subyacente.

WPF:

<Viewbox Name="MainViewbox" Stretch="Uniform"> 
    ... your content here 
</Viewbox> 
Cuestiones relacionadas