ウィンドウの縦横比が一定に保たれるように、サイズ変更を制限したいユーザーがサイズ変更可能な WPF ウィンドウを持っています。
理想的には、適切な縦横比を維持する位置にコーナーをドラッグして、ウィンドウのサイズを変更するときにマウスの位置を制限したいと考えています。マウスでエッジのサイズを変更すると、他の寸法も同時に変更されます。
これを行う簡単な方法や、誰もが知っている良いオンラインの例はありますか?
より良い解決策が思い浮かばない場合は、少し改良した後に行ったことを投稿します。
ウィンドウの縦横比が一定に保たれるように、サイズ変更を制限したいユーザーがサイズ変更可能な WPF ウィンドウを持っています。
理想的には、適切な縦横比を維持する位置にコーナーをドラッグして、ウィンドウのサイズを変更するときにマウスの位置を制限したいと考えています。マウスでエッジのサイズを変更すると、他の寸法も同時に変更されます。
これを行う簡単な方法や、誰もが知っている良いオンラインの例はありますか?
より良い解決策が思い浮かばない場合は、少し改良した後に行ったことを投稿します。
ここでNirによる良い答えを見つけました。まだいくつかの欠陥があります。基本的に、右上隅、右下隅、および下側のサイズ変更は問題ありませんが、他の側面と隅は問題ありません。明るい面は、縦横比が常にスムーズに保たれていることです。
編集:ほとんどの問題を解決する方法を見つけました。サイジングが開始されると、アスペクト比を維持するために人為的に調整される寸法は、ウィンドウに対するマウスの位置を特定することによって決定されます。私が見つけた唯一の不完全な点は、コーナーからサイズ変更するとウィンドウの位置が変わる可能性があることです (右下を除く)。
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(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;
}
}
}
これはウィンドウを特定の比率にすることを強制するものではありませんが (OP が要求したように)、元の縦横比を維持しながら、コンテンツを a でラップしViewbox
、ストレッチ性としてStretch="Uniform"
。コード ビハインドは必要ありません。
WPF:
<Viewbox Name="MainViewbox" Stretch="Uniform">
... your content here
</Viewbox>
上記の答えは、高さの変更よりも幅の変更を優先するため、高さを大幅に調整すると、マウスの位置によって幅も少し変化しますが、ユーザーにはほぼ同じウィンドウが表示されます。私は、ユーザーが最も興味を持っているものとして最大の変化を支持する各次元の変化率を処理するこのコードを持っています。
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);
}
それはトリックを行いますか:
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) {
if (sizeInfo.WidthChanged) this.Width = sizeInfo.NewSize.Height * aspect;
else this.Height = sizeInfo.NewSize.Width / aspect;
}
ここで見つけました。
ウィンドウでは、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