2010-03-18 2 views
12

У меня есть изменяемое пользователем окно WPF, которое я хочу ограничить изменение размера, поэтому соотношение сторон окна остается постоянным.Изменить размер окна WPF, но сохранить пропорции?

В идеале я хотел бы ограничить местоположение мыши при изменении размера окна, перетаскивая угол в позиции, поддерживающие правильный аспектный рацион. Если ребро изменено с помощью мыши, другое измерение должно измениться в одно и то же время.

Есть ли простой способ сделать это или хороший онлайн-пример, о котором все знают?

Если лучшие решения не появятся, я опубликую то, что я сделал после того, как немного уточнил его.

+0

Смотрите, если это помогает: HTTP: // StackOverflow.com/questions/288954/how-do-i-keep-aspect-ratio-on-scalable-scrollable-content-in-wpf – 2012-06-28 11:18:18

ответ

2

Значит ли это сделать трюк:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { 

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

Нашел here.

2

Ответ, приведенный выше, способствует изменению ширины по сравнению с изменением высоты, поэтому, если вы отрегулируете высоту много, но из-за расположения мыши ширина также немного изменится, пользователь по-прежнему будет видеть почти то же окно. У меня есть этот код, который работает от процентного изменения в каждом измерении, благоприятствующей наибольшее изменение в качестве одного пользователя наиболее заинтересован в

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

Спасибо, что решила мою проблему. –

+1

Математика - это префект, но когда он реализован в коде, размер изменен очень неустойчивым, а не гладким. – mandarin

0

На окне - вы можете прослушать сообщения из 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

+0

Проблема заключается в том, что 'WM_EXITSIZEMOVE' отправляется только в окно, когда пользователь будет изменять размер. Сообщение 'WM_SIZING' - это то, что срабатывает непрерывно, но у меня были изменчивые, спастические результаты. – lordcheeto

19

Я нашел хороший ответ Nir here. Есть еще некоторые недостатки, в основном изменение размеров в верхнем правом углу, нижний правый угол и нижняя сторона будут в порядке, другие стороны и углы - нет. Светлая сторона, соотношение сторон плавно сохраняется все время.

EDIT: Я нашел способ устранить большинство проблем. Когда начинается калибровка, размер, который будет искусственно изменен для сохранения пропорции, определяется путем размещения положения мыши относительно окна. Единственные оставшиеся дефекты, которые я обнаружил, это то, что положение окна может измениться при изменении размера из углов (кроме нижнего правого).

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

Это работает действительно, очень хорошо. Единственное, почему это не позволит мне изменить размеры с боков? Это позволяет мне только сверху или снизу, или по диагонали. Спасибо хоть! –

+2

@ofstream: изменение размера теперь должно работать со всех сторон. –

+1

Большое спасибо за вашу работу! –

2

Хотя это не заставит окно, чтобы быть в определенном соотношении (как просили ОП), мне удалось получить СОДЕРЖАНИЕ окна для масштабирования, сохраняя при этом исходное соотношение сторон, путем обертывания содержимого в Viewbox и установки растягивающей простейшей как Stretch="Uniform". Никакой код не нужен.

WPF:

<Viewbox Name="MainViewbox" Stretch="Uniform"> 
    ... your content here 
</Viewbox> 
Смежные вопросы