18

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

4

9 回答 9

13

您可以使用 WPF 的 ViewBox 保留内容的纵横比,其中包含固定宽度和高度的控件。

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

这是我的屏幕截图:在此处输入图像描述

<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>
于 2008-12-22T19:32:32.950 回答
13

您始终可以处理 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;
}
于 2009-03-30T07:11:55.647 回答
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 技巧,但我不喜欢它。我想将窗口边框锁定为特定大小。这是在窗口控件上测试的,但我认为它也可以在边框上工作。

于 2009-03-30T02:52:53.083 回答
1

在代码示例中:

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 > 0,... 我这样做是因为我发现它倾向于在“Load”方法甚至分配 aspectRatio 之前调用我的处理程序来调整大小。

于 2010-12-21T13:38:25.673 回答
1

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

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

WPF 可能会也可能不会;我不知道。

于 2008-12-22T18:37:20.797 回答
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
于 2010-09-15T22:17:26.593 回答
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 中所示)与它在屏幕上的大小不一致!

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

希望有帮助!

于 2008-12-23T01:00:54.220 回答
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;
    }
}
于 2013-07-04T02:46:45.243 回答
0

我得到了一种不依赖于 Windows 平台特定 API 的方法,也具有可接受的用户体验(拖动窗口时不会晃动)。它使用计时器在 0.1 秒后调整窗口大小,因此用户不会看到它抖动。

public partial class MainWindow : Window
{
    private DispatcherTimer resizeTimer;
    private double _aspectRatio;
    private SizeChangedInfo? _sizeInfo;

    public MainWindow()
    {
        InitializeComponent();
        _aspectRatio = Width / Height;
        resizeTimer = new DispatcherTimer();
        resizeTimer.Interval = new TimeSpan(100*10000); // 0.1 seconds
        resizeTimer.Tick += ResizeTimer_Tick;
    }

    private void ResizeTimer_Tick(object? sender, EventArgs e)
    {
        resizeTimer.Stop();
        if (_sizeInfo == null) return;
        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;
    }

    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        _sizeInfo = sizeInfo;
        resizeTimer.Stop();
        resizeTimer.Start();
        base.OnRenderSizeChanged(sizeInfo);
    }
}
于 2022-01-14T13:47:17.593 回答