如何才能在WPF窗口中允许统一调整大小?

时间:2008-12-22 15:17:08

标签: c# .net wpf resize aspect-ratio

我不希望我的窗口“仅水平”或“仅垂直”调整大小。我可以在我的窗口设置一个可以强制执行此操作的属性,还是有一个可以使用的漂亮的代码隐藏技巧?

8 个答案:

答案 0 :(得分:13)

您可以使用WPF的ViewBox保留内容的宽高比,并且内部有固定宽度和高度的控件。

让我们试一试。您可以更改ViewBox的“Stretch”属性以体验不同的结果。

以下是我的屏幕截图:enter image description here

<Window x:Class="TestWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <Viewbox Stretch="Uniform">
        <StackPanel Background="Azure" Height="400" Width="300" Name="stackPanel1" VerticalAlignment="Top">
            <Button Name="testBtn" Width="200" Height="50">
                <TextBlock>Test</TextBlock>
            </Button>
        </StackPanel>
    </Viewbox>

</Window>

答案 1 :(得分:11)

您始终可以处理WM_WINDOWPOSCHANGING消息,这可以让您在调整大小过程中控制窗口大小和位置(而不是在用户完成大小调整后修复内容)。

以下是如何在WPF中执行此操作,我将来自多个源的代码组合在一起,因此可能存在一些语法错误。

internal enum WM
{
   WINDOWPOSCHANGING = 0x0046,
}

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

private void Window_SourceInitialized(object sender, EventArgs ea)
{
   HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
   hwndSource.AddHook(DragHook);
}

private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
   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;
          }

          bool changedPos = false;

          // ***********************
          // Here you check the values inside the pos structure
          // if you want to override tehm just change the pos
          // structure and set changedPos to true
          // ***********************

          if (!changedPos)
          {
             return IntPtr.Zero;
          }

          Marshal.StructureToPtr(pos, lParam, true);
          handeled = true;
       }
       break;
   }

   return IntPtr.Zero;
}

答案 2 :(得分:8)

这就是我的解决方案。

您需要将其添加到控件/窗口标记中:

Loaded="Window_Loaded"

你需要将它放在你的代码中:

private double aspectRatio = 0.0;

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    aspectRatio = this.ActualWidth / this.ActualHeight;
}

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

我尝试了Viewbox技巧,我不喜欢它。我想将窗口边框锁定到特定大小。这是在窗口控件上测试的,但我认为它也适用于边框。

答案 3 :(得分:1)

您可以尝试复制我经常在Flash Video网站上看到的效果。它们允许您以任何方式展开浏览器窗口,但只展开演示区域,使其适合最小的高度或宽度。

例如,如果垂直拉伸窗口,则应用程序不会调整大小。只需在显示区域的顶部和底部添加黑条,并保持垂直居中。

WPF可能会或可能不会这样;我不知道。

答案 4 :(得分:1)

我原本以为你可以使用值转换器将宽度双向绑定到高度以保持纵横比。将纵横比作为转换器参数传递将使其更加通用。

所以,我试过这个 - 首先没有转换器的绑定:

<Window 
    ...
    Title="Window1" Name="Win" Height="500" 
    Width="{Binding RelativeSource={RelativeSource self}, 
                    Path=Height, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <StackPanel>
        <TextBlock>Width:</TextBlock>
        <TextBlock Text="{Binding ElementName=Win, Path=Width}" />
        <TextBlock>Height:</TextBlock>
        <TextBlock Text="{Binding ElementName=Win, Path=Height}" />
    </StackPanel>    
</Window>

奇怪的是,绑定的行为就好像它是单向的,并且报告的窗口宽度(如TextBlock中所示)与屏幕上的大小不一致!

这个想法可能值得追求,但这种奇怪的行为需要首先解决。

希望有所帮助!

答案 5 :(得分:1)

这可能有点迟了但你可以简单地把它放在你的代码后面......

Private Sub UserControl1_SizeChanged(ByVal sender As Object, ByVal e As System.Windows.SizeChangedEventArgs) Handles Me.SizeChanged
    If e.HeightChanged Then
        Me.Width = Me.Height
    Else
        Me.Height = Me.Width
    End If
End Sub

答案 6 :(得分:0)

在代码示例中:

if (sizeInfo.WidthChanged)     
{         
    this.Width = sizeInfo.NewSize.Height * aspectRatio;    
}     
else     
{         
    this.Height = sizeInfo.NewSize.Width * aspectRatio; 
} 

我相信第二个计算应该是:

this.Height = sizeInfo.NewSize.Width * (1/aspectRatio);  

我在“SizeChanged”事件处理程序中对此工作进行了修改。由于我希望宽度是控制尺寸,我只是通过计算形式强制高度匹配它:

if (aspectRatio > 0)
// enforce aspect ratio by restricting height to stay in sync with width.  
this.Height = this.ActualWidth * (1 / aspectRatio);

您可以注意检查aspectRatio&gt; 0,...我这样做是因为我发现在“加载”方法甚至分配了aspectRatio之前,它倾向于调用我的处理程序进行调整大小。

答案 7 :(得分:0)

也许为时已晚,但我找到了Mike O'Brien博客的解决方案,而且效果非常好。 http://www.mikeobrien.net/blog/maintaining-aspect-ratio-when-resizing/ 以下是他博客中的代码:

<Window ... SourceInitialized="Window_SourceInitialized" ... >
    ...
Window>

public partial class Main : Window
{
    private void Window_SourceInitialized(object sender, EventArgs ea)
    {
        WindowAspectRatio.Register((Window)sender);
    }
    ...
}


internal class WindowAspectRatio
{
    private double _ratio;

    private WindowAspectRatio(Window window)
    {
        _ratio = window.Width / window.Height;
        ((HwndSource)HwndSource.FromVisual(window)).AddHook(DragHook);
    }

    public static void Register(Window window)
    {
        new WindowAspectRatio(window);
    }

    internal enum WM
    {
        WINDOWPOSCHANGING = 0x0046,
    }

    [Flags()]
    public enum SWP
    {
        NoMove = 0x2,
    }

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

    private IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
    {
        if ((WM)msg == WM.WINDOWPOSCHANGING)
        {
            WINDOWPOS position = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));

            if ((position.flags & (int)SWP.NoMove) != 0 || 
                HwndSource.FromHwnd(hwnd).RootVisual == null) return IntPtr.Zero;

            position.cx = (int)(position.cy * _ratio);

            Marshal.StructureToPtr(position, lParam, true);
            handeled = true;
        }

        return IntPtr.Zero;
    }
}