我有一个用户可resize的WPF窗口,我想约束resize,使窗口的纵横比保持不变。
理想情况下,我想通过拖拽一个angular来调整窗口大小来限制鼠标位置,以保持适当的纵横比。 如果边缘用鼠标resize,则其他维度应同时更改。
有没有一个简单的方法来做到这一点或任何人都知道的在线示例?
如果没有更好的解决scheme出现,我会发布我已经完成了一些我已经完成了。
尼尔在这里找到了很好的答案。 还有一些缺陷,基本上右上角的调整,右下角和下边都会好,其他的边角都没有。 好的一面是,纵横比一直保持平稳。
编辑:我找到了一种方法来消除大部分的问题。 在开始大小调整时,将通过相对于窗口定位鼠标位置来确定将被人为调整以保持纵横比的尺寸。 我发现的唯一剩余的缺陷是窗口的位置可能在从角落调整大小时发生变化(右下角除外)。
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>
代码后面:
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(pX - pos.x), Math.Abs(pX - pos.x - pos.cx)); double diffHeight = Math.Min(Math.Abs(pY - pos.y), Math.Abs(pY - 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; } } }
这是否有诀窍:
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { if (sizeInfo.WidthChanged) this.Width = sizeInfo.NewSize.Height * aspect; else this.Height = sizeInfo.NewSize.Width / aspect; }
在这里找到它。
上面给出的答案有利于高度变化的宽度变化,所以如果你调整高度很多,但由于鼠标定位,宽度也会有所变化,用户仍然会看到几乎相同的窗口。 我有这样的代码,在每个维度中的百分比变化作为用户最感兴趣的变化来支持最大的变化。
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); }
虽然这并不强制窗口是一个特定的比例(如OP问),我设法得到一个窗口的内容来缩放,同时保持原来的长宽比,包装内容在一个Viewbox
和设置Stretch="Uniform"
。 不需要代码隐藏。
WPF:
<Viewbox Name="MainViewbox" Stretch="Uniform"> ... your content here </Viewbox>
在窗口 – 你可以简单地听取Win32 API的消息:
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; }
在这个代码中,你总是采取较短的一面,并设置它等于更长的时间。 你总是可以采取相反的做法,并设置更长的时间等于更短。 我在这里找到了解决方案: http : //social.msdn.microsoft.com/forums/en-US/wpf/thread/b0df3d1f-e211-4f54-a079-09af0096410e