I don't want my window to be resized either "only horizontally" or "only vertically." Is there a property I can set on my window that can enforce this, or is there a nifty code-behind trick I can use?
我不希望我的窗口“仅水平”或“仅垂直”调整大小。我可以在我的窗口上设置一个可以强制执行此操作的属性,还是有一个可以使用的漂亮的代码隐藏技巧?
8 个解决方案
#1
13
You can reserve aspect ratio of contents using WPF's ViewBox with control with fixed width and height inside.
您可以使用WPF的ViewBox保留内容的宽高比,并使用固定宽度和高度的控件。
Let's give this a try. You can change "Stretch" attribute of ViewBox to experience different results.
让我们试一试。您可以更改ViewBox的“Stretch”属性以体验不同的结果。
Here is my screeen shot:
这是我的screeen镜头:
<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>
#2
11
You can always handle the WM_WINDOWPOSCHANGING message, this let's you control the window size and position during the resizing process (as opposed to fixing things after the user finished resizing).
您始终可以处理WM_WINDOWPOSCHANGING消息,这可以让您在调整大小过程中控制窗口大小和位置(而不是在用户完成大小调整后修复内容)。
Here is how you do it in WPF, I combined this code from several sources, so there could be some syntax errors in it.
以下是您在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;
}
#3
8
This is what my solution was.
这就是我的解决方案。
You will need to add this to your control/window tag:
您需要将其添加到控件/窗口标记中:
Loaded="Window_Loaded"
And you will need to place this in your code behind:
你需要将它放在你的代码中:
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;
}
}
I tried the Viewbox trick and I did not like it. I wanted to lock the window border to a specific size. This was tested on a window control but I assume it would work on a border as well.
我尝试了Viewbox技巧,但我不喜欢它。我想将窗口边框锁定到特定大小。这是在窗口控件上测试的,但我认为它也适用于边框。
#4
1
You could try replicating an effect that I often see on Flash Video websites. They allow you to expand the browser window any way you like, but only stretch the presentation area so that it fits the smallest of the height or width.
您可以尝试复制我经常在Flash Video网站上看到的效果。它们允许您以任何方式扩展浏览器窗口,但只能拉伸显示区域以使其适合最小的高度或宽度。
For example, if you stretch the window vertically, your application would not resize. It would simple add black bars to the top and bottom of the display area and remain vertically centered.
例如,如果垂直拉伸窗口,则应用程序不会调整大小。只需在显示区域的顶部和底部添加黑条,并保持垂直居中。
This may or may not be possible with WPF; I don't know.
WPF可能会或可能不会这样;我不知道。
#5
1
This may be bit late but you can simply put it in your code behind....
这可能有点迟了但你可以简单地把它放在你的代码背后....
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
1
I had expected that you could two-way bind the width to the height using a value converter to maintain aspect ratio. Passing the aspect ratio as the converter parameter would make it more general purpose.
我原本以为你可以使用值转换器将宽度双向绑定到高度以保持纵横比。将纵横比作为转换器参数传递将使其更通用。
So, I tried this - the binding with no converter first:
所以,我试过这个 - 首先没有转换器的绑定:
<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>
Strangely, the binding is behaving as if it is one-way and the reported width of the window (as shown in the TextBlock) is not consistent with it's size on screen!
奇怪的是,绑定的行为就好像它是单向的,并且报告的窗口宽度(如TextBlock中所示)与屏幕上的大小不一致!
The idea might be worth pursuing, but this strange behavior would need to be sorted out first.
这个想法可能值得追求,但这种奇怪的行为需要首先解决。
Hope that helps!
希望有所帮助!
#7
0
In the code sample:
在代码示例中:
if (sizeInfo.WidthChanged)
{
this.Width = sizeInfo.NewSize.Height * aspectRatio;
}
else
{
this.Height = sizeInfo.NewSize.Width * aspectRatio;
}
I believe the second computation should be:
我相信第二个计算应该是:
this.Height = sizeInfo.NewSize.Width * (1/aspectRatio);
I made a variation of this work in a "SizeChanged" event handler. Since I wanted the width to be the controlling dimension, I simply forced the height to match to it with a computation of the form:
我在“SizeChanged”事件处理程序中对此工作进行了修改。因为我希望宽度是控制尺寸,所以我只是通过计算形式强制高度与它匹配:
if (aspectRatio > 0)
// enforce aspect ratio by restricting height to stay in sync with width.
this.Height = this.ActualWidth * (1 / aspectRatio);
You may note the check for an aspectRatio > 0, ... I did this because I found that it was tending to call my handlers that did the resizing before the "Load" method had even assigned the aspectRatio.
您可能会注意到aspectRatio> 0的检查,...我这样做是因为我发现在“Load”方法甚至分配了aspectRatio之前,它倾向于调用我的处理程序进行调整大小。
#8
0
Maybe too late, but i found a solution from Mike O'Brien blog, and it work really good. http://www.mikeobrien.net/blog/maintaining-aspect-ratio-when-resizing/ Below is code from his blog:
也许为时已晚,但我找到了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;
}
}
#1
13
You can reserve aspect ratio of contents using WPF's ViewBox with control with fixed width and height inside.
您可以使用WPF的ViewBox保留内容的宽高比,并使用固定宽度和高度的控件。
Let's give this a try. You can change "Stretch" attribute of ViewBox to experience different results.
让我们试一试。您可以更改ViewBox的“Stretch”属性以体验不同的结果。
Here is my screeen shot:
这是我的screeen镜头:
<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>
#2
11
You can always handle the WM_WINDOWPOSCHANGING message, this let's you control the window size and position during the resizing process (as opposed to fixing things after the user finished resizing).
您始终可以处理WM_WINDOWPOSCHANGING消息,这可以让您在调整大小过程中控制窗口大小和位置(而不是在用户完成大小调整后修复内容)。
Here is how you do it in WPF, I combined this code from several sources, so there could be some syntax errors in it.
以下是您在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;
}
#3
8
This is what my solution was.
这就是我的解决方案。
You will need to add this to your control/window tag:
您需要将其添加到控件/窗口标记中:
Loaded="Window_Loaded"
And you will need to place this in your code behind:
你需要将它放在你的代码中:
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;
}
}
I tried the Viewbox trick and I did not like it. I wanted to lock the window border to a specific size. This was tested on a window control but I assume it would work on a border as well.
我尝试了Viewbox技巧,但我不喜欢它。我想将窗口边框锁定到特定大小。这是在窗口控件上测试的,但我认为它也适用于边框。
#4
1
You could try replicating an effect that I often see on Flash Video websites. They allow you to expand the browser window any way you like, but only stretch the presentation area so that it fits the smallest of the height or width.
您可以尝试复制我经常在Flash Video网站上看到的效果。它们允许您以任何方式扩展浏览器窗口,但只能拉伸显示区域以使其适合最小的高度或宽度。
For example, if you stretch the window vertically, your application would not resize. It would simple add black bars to the top and bottom of the display area and remain vertically centered.
例如,如果垂直拉伸窗口,则应用程序不会调整大小。只需在显示区域的顶部和底部添加黑条,并保持垂直居中。
This may or may not be possible with WPF; I don't know.
WPF可能会或可能不会这样;我不知道。
#5
1
This may be bit late but you can simply put it in your code behind....
这可能有点迟了但你可以简单地把它放在你的代码背后....
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
1
I had expected that you could two-way bind the width to the height using a value converter to maintain aspect ratio. Passing the aspect ratio as the converter parameter would make it more general purpose.
我原本以为你可以使用值转换器将宽度双向绑定到高度以保持纵横比。将纵横比作为转换器参数传递将使其更通用。
So, I tried this - the binding with no converter first:
所以,我试过这个 - 首先没有转换器的绑定:
<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>
Strangely, the binding is behaving as if it is one-way and the reported width of the window (as shown in the TextBlock) is not consistent with it's size on screen!
奇怪的是,绑定的行为就好像它是单向的,并且报告的窗口宽度(如TextBlock中所示)与屏幕上的大小不一致!
The idea might be worth pursuing, but this strange behavior would need to be sorted out first.
这个想法可能值得追求,但这种奇怪的行为需要首先解决。
Hope that helps!
希望有所帮助!
#7
0
In the code sample:
在代码示例中:
if (sizeInfo.WidthChanged)
{
this.Width = sizeInfo.NewSize.Height * aspectRatio;
}
else
{
this.Height = sizeInfo.NewSize.Width * aspectRatio;
}
I believe the second computation should be:
我相信第二个计算应该是:
this.Height = sizeInfo.NewSize.Width * (1/aspectRatio);
I made a variation of this work in a "SizeChanged" event handler. Since I wanted the width to be the controlling dimension, I simply forced the height to match to it with a computation of the form:
我在“SizeChanged”事件处理程序中对此工作进行了修改。因为我希望宽度是控制尺寸,所以我只是通过计算形式强制高度与它匹配:
if (aspectRatio > 0)
// enforce aspect ratio by restricting height to stay in sync with width.
this.Height = this.ActualWidth * (1 / aspectRatio);
You may note the check for an aspectRatio > 0, ... I did this because I found that it was tending to call my handlers that did the resizing before the "Load" method had even assigned the aspectRatio.
您可能会注意到aspectRatio> 0的检查,...我这样做是因为我发现在“Load”方法甚至分配了aspectRatio之前,它倾向于调用我的处理程序进行调整大小。
#8
0
Maybe too late, but i found a solution from Mike O'Brien blog, and it work really good. http://www.mikeobrien.net/blog/maintaining-aspect-ratio-when-resizing/ Below is code from his blog:
也许为时已晚,但我找到了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;
}
}