2010-03-18 38 views
12

Tôi có một cửa sổ WPF có thể thay đổi kích thước người dùng mà tôi muốn hạn chế thay đổi kích thước để tỷ lệ khung hình của cửa sổ không thay đổi.Thay đổi kích thước cửa sổ WPF, nhưng duy trì tỷ lệ?

Lý tưởng nhất là tôi muốn hạn chế vị trí chuột khi cửa sổ đang được thay đổi kích thước bằng cách kéo một góc tới các vị trí duy trì khẩu phần tỉ lệ phù hợp. Nếu một cạnh được thay đổi kích thước bằng chuột, thứ nguyên khác sẽ thay đổi cùng một lúc.

Có cách nào đơn giản để thực hiện việc này hoặc một ví dụ trực tuyến tốt mà bất kỳ ai biết không?

Nếu không có giải pháp nào tốt hơn, tôi sẽ đăng những gì tôi đã thực hiện sau khi tôi đã tinh chỉnh nó một chút.

+0

Xem nếu điều này giúp: http: // stackoverflow.com/questions/288954/how-do-i-keep-aspect-ratio-on-scalable-scrollable-content-in-wpf –

Trả lời

2

Liệu rằng làm các trick:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { 

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

Tìm thấy nó here.

2

Câu trả lời được đưa ra ở trên ủng hộ thay đổi chiều rộng so với thay đổi chiều cao vì vậy nếu bạn điều chỉnh chiều cao rất nhiều, nhưng vì vị trí chuột, chiều rộng cũng thay đổi một chút, người dùng sẽ vẫn thấy khá nhiều cửa sổ tương tự. Tôi có mã này làm việc ra những thay đổi phần trăm trong mỗi chiều ưu sự thay đổi lớn nhất là một trong những người dùng quan tâm nhất trong

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

Cảm ơn nó đã giải quyết được vấn đề của tôi. –

+1

Toán học là hoàn hảo, nhưng khi được thực hiện trong mã, việc thay đổi kích thước rất nhảy, không mượt mà chút nào. – mandarin

0

Vào Window - bạn có thể nghe thông điệp của Win32 API đơn giản:.

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

Trên mã này, bạn luôn lấy mặt ngắn hơn và đặt nó bằng với thời lượng dài hơn. Bạn luôn có thể sử dụng cách tiếp cận ngược lại và đặt thời gian dài hơn bằng ngắn hơn. Tôi tìm thấy giải pháp ở đây: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/b0df3d1f-e211-4f54-a079-09af0096410e

+0

Vấn đề với điều đó là 'WM_EXITSIZEMOVE' chỉ được gửi đến cửa sổ khi người dùng thay đổi kích thước. Thông báo 'WM_SIZING' là những gì xảy ra liên tục, nhưng tôi đã có kết quả không ổn định, dẻo dai. – lordcheeto

19

Tôi đã tìm thấy câu trả lời hay bởi Nir here. Vẫn còn một số sai sót, về cơ bản thay đổi kích thước ở góc trên bên phải, góc dưới cùng bên phải và phía dưới sẽ tốt, các bên và góc khác thì không. Mặt tươi sáng là, tỷ lệ co được giữ suốt mọi lúc.

EDIT: Tôi đã tìm thấy cách xóa hầu hết các sự cố. Khi kích thước bắt đầu, kích thước sẽ được điều chỉnh nhân tạo để giữ tỷ lệ khung hình được xác định bằng cách định vị vị trí chuột so với cửa sổ. Các khuyết điểm còn lại duy nhất tôi tìm thấy là vị trí của cửa sổ có thể thay đổi khi thay đổi kích thước từ các góc (trừ dưới cùng bên phải).

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> 

Mã đằng sau:

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

Điều này hoạt động thực sự, thực sự tốt. Chỉ có điều là, tại sao nó sẽ không cho phép tôi thay đổi kích cỡ từ hai bên? Nó chỉ cho phép tôi từ đầu/cuối, hoặc từ đường chéo. Cảm ơn mặc dù! –

+2

@ofstream: Thay đổi kích thước giờ đây sẽ hoạt động ở mọi góc độ. –

+1

Cảm ơn rất nhiều vì công việc của bạn! –

2

Mặc dù điều này không buộc các cửa sổ là có một tỷ lệ cụ thể (như OP hỏi), tôi đã được quản lý để có được NỘI DUNG của cửa sổ để mở rộng quy mô, đồng thời duy trì tỷ lệ khung hình gốc, bằng cách gói nội dung trong một số Viewbox và đặt độ dài căng như Stretch="Uniform". Không cần viết mã.

WPF:

<Viewbox Name="MainViewbox" Stretch="Uniform"> 
    ... your content here 
</Viewbox> 
Các vấn đề liên quan