Resize a WPF window, but maintain proportions? Resize a WPF window, but maintain proportions? wpf wpf

Resize a WPF window, but maintain proportions?


I've found a good answer by Nir here. There are still some flaws, basically resizing in top right corner, bottom right corner and bottom side will be fine, other sides and corners are not. The bright side is, the aspect ratio is smoothly kept all the time.

EDIT: I found a way to remove most of the problems. When sizing starts, the dimension that will be artificially adjusted to keep the aspect ratio is determined by locating the mouse position relative to the window. The only remaining imperfections I found are that the position of the window may change when resizing from the corners (except bottom right).

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>

Code behind:

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


Although this doesn't force the Window to be of a specific ratio (as the OP asked), I have managed to get the CONTENT of a window to scale, while maintaining the original aspect ratio, by wrapping the contents in a Viewbox and setting the stretch propety as Stretch="Uniform". No code-behind is needed.

WPF:

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


Does that do the trick:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) {    if (sizeInfo.WidthChanged) this.Width = sizeInfo.NewSize.Height * aspect;    else this.Height = sizeInfo.NewSize.Width / aspect;}

Found it here.