在 WPF 中旋转图像不考虑屏幕尺寸
Rotating image in WPF not respecting screen size
我目前在 WPF 中使用 RotateTransform
和 LayoutTransform
旋转图像时遇到问题。当像素高度尺寸大于显示器高度的图像旋转 90º 或 270º 时,window 尺寸将高于显示器屏幕分辨率尺寸。
示例截图:
Application running with image at 90º
Application running with image at 0º
我正在使用下面的代码(简化版),其中 mainWindow.img 是 System.Windows.Control.Image
:
static void Rotate(int degrees)
{
var rt = new RotateTransform { Angle = degrees };
mainWindow.img.LayoutTransform = rt;
}
它是一个图片查看器项目,完整的源代码可以在 https://github.com/Ruben2776/PicView
我试过移动图像的宽度和高度值,但它产生了不希望的结果(倾斜的比例)。
图像大小的大小计算是根据用户的屏幕高度进行的,使用以下修剪代码:
int interfaceHeight = 90;
double maxWidth = Math.Min(MonitorInfo.Width, width);
double maxHeight = Math.Min((MonitorInfo.Height - interfaceHeight), height);
double AspectRatio = Math.Min((maxWidth / width), (maxHeight / height));
mainWindow.img.Width = (width * AspectRatio);
mainWindow.img.Height = (height * AspectRatio);
其中 height
和 width
是图像的尺寸,MonitorInfo
是 class 检索当前显示器分辨率。
更新
下面是说明该问题的示例 WPF 应用程序的最少代码:
MainWindow.xaml
<Window x:Class="RotateTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
Title="MainWindow" >
<Grid>
<Image x:Name="img" Stretch="Fill" Source="https://w.wallhaven.cc/full/nk/wallhaven-nkrwz1.jpg"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace RotateTest
{
public partial class MainWindow : Window
{
int Degrees;
public MainWindow()
{
InitializeComponent();
ContentRendered += MainWindow_ContentRendered;
KeyDown += MainWindow_KeyDown;
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
Rotate(true);
break;
case Key.Down:
Rotate(false);
break;
}
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
int interfaceHeight = 90;
double maxWidth = Math.Min(SystemParameters.PrimaryScreenWidth, img.Source.Width);
double maxHeight = Math.Min(SystemParameters.PrimaryScreenHeight - interfaceHeight, img.Source.Height);
double AspectRatio = Math.Min((maxWidth / img.Source.Width), (maxHeight / img.Source.Height));
img.Width = (img.Source.Width * AspectRatio);
img.Height = (img.Source.Height * AspectRatio);
}
void Rotate(int degrees)
{
var rt = new RotateTransform { Angle = Degrees = degrees };
img.LayoutTransform = rt;
}
void Rotate(bool right)
{
switch (Degrees)
{
case 0:
if (right)
{
Rotate(270);
}
else
{
Rotate(90);
}
break;
case 90:
if (right)
{
Rotate(0);
}
else
{
Rotate(180);
}
break;
case 180:
if (right)
{
Rotate(90);
}
else
{
Rotate(270);
}
break;
case 270:
if (right)
{
Rotate(180);
}
else
{
Rotate(0);
}
break;
}
}
}
}
问题的根源在于,您只是根据未旋转的图像大小计算缩放比例。旋转图像后,img.ActualHeight
实际上变成了它的宽度,img.ActualWidth
实际上变成了它的高度,并且从图像未旋转时开始的计算不再正确。
以下是我对您的代码所做的更改和添加:
private double normalRatio;
private double rotatedRatio;
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
double interfaceHeight = this.ActualHeight - img.ActualHeight;
normalRatio = Math.Min(SystemParameters.WorkArea.Width / img.Source.Width, (SystemParameters.WorkArea.Height - interfaceHeight) / img.Source.Height);
rotatedRatio = Math.Min(SystemParameters.WorkArea.Width / img.Source.Height, (SystemParameters.WorkArea.Height - interfaceHeight) / img.Source.Width);
ScaleImage();
}
private void ScaleImage()
{
double ratio = Degrees == 0 || Degrees == 180 ? normalRatio : rotatedRatio;
img.Width = (img.Source.Width * ratio);
img.Height = (img.Source.Height * ratio);
}
void Rotate(bool right)
{
if (right)
{
Degrees -= 90;
if (Degrees < 0) { Degrees += 360; }
}
else
{
Degrees += 90;
if (Degrees >= 360) { Degrees -= 360; }
}
ScaleImage();
Rotate(Degrees);
}
//I left the other methods, including Rotate(int degrees), the same as in your question
以下是对我所做更改的解释:
interfaceHeight
的计算方法是从 window 的高度减去图像的高度,差值是其他所有内容的总大小。
- 我没有使用
MonitorInfo
,而是使用 SystemParameters.WorkArea
,因为它考虑了 Windows 任务栏的大小和位置。
- 我计算两个比例:
normalRatio
,当图像不旋转或垂直翻转(180°)时,rotatedRatio
,当图像在任一方向旋转 90° 时。我通过交换 img.Source.Height
和 img.Source.Width
. 来计算后者
- 我添加了一个
ScaleImage()
方法来根据预期的旋转进行实际图像缩放,因此我可以从两个不同的地方调用它。
- 我简化了
Rotate(bool right)
使用数学计算新角度,而不是列出每个可能的旋转。
上面的结果是在保持原始宽高比的同时,图像总是尽可能大。它会随着屏幕的旋转而增大和缩小。如果您希望图像保持恒定大小,只需使用 Math.Min(normalRatio, rotatedRatio)
.
请注意,以上仅在您调用 Rotate(bool right)
时有效,如果您直接调用 Rotate(int degrees)
,则 无效。这是因为使用两个比率的逻辑仅适用,因为图像只有两种可能的尺寸(纵向和横向),只有将旋转限制为 90° 的增量时才会出现这种情况。如果你想将角度设置为其他角度,比如 20°,计算图像有效尺寸的数学运算会变得有点复杂,你需要开始根据角度动态计算它。
我目前在 WPF 中使用 RotateTransform
和 LayoutTransform
旋转图像时遇到问题。当像素高度尺寸大于显示器高度的图像旋转 90º 或 270º 时,window 尺寸将高于显示器屏幕分辨率尺寸。
示例截图: Application running with image at 90º Application running with image at 0º
我正在使用下面的代码(简化版),其中 mainWindow.img 是 System.Windows.Control.Image
:
static void Rotate(int degrees)
{
var rt = new RotateTransform { Angle = degrees };
mainWindow.img.LayoutTransform = rt;
}
它是一个图片查看器项目,完整的源代码可以在 https://github.com/Ruben2776/PicView
我试过移动图像的宽度和高度值,但它产生了不希望的结果(倾斜的比例)。
图像大小的大小计算是根据用户的屏幕高度进行的,使用以下修剪代码:
int interfaceHeight = 90;
double maxWidth = Math.Min(MonitorInfo.Width, width);
double maxHeight = Math.Min((MonitorInfo.Height - interfaceHeight), height);
double AspectRatio = Math.Min((maxWidth / width), (maxHeight / height));
mainWindow.img.Width = (width * AspectRatio);
mainWindow.img.Height = (height * AspectRatio);
其中 height
和 width
是图像的尺寸,MonitorInfo
是 class 检索当前显示器分辨率。
更新
下面是说明该问题的示例 WPF 应用程序的最少代码:
MainWindow.xaml
<Window x:Class="RotateTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
Title="MainWindow" >
<Grid>
<Image x:Name="img" Stretch="Fill" Source="https://w.wallhaven.cc/full/nk/wallhaven-nkrwz1.jpg"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace RotateTest
{
public partial class MainWindow : Window
{
int Degrees;
public MainWindow()
{
InitializeComponent();
ContentRendered += MainWindow_ContentRendered;
KeyDown += MainWindow_KeyDown;
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
Rotate(true);
break;
case Key.Down:
Rotate(false);
break;
}
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
int interfaceHeight = 90;
double maxWidth = Math.Min(SystemParameters.PrimaryScreenWidth, img.Source.Width);
double maxHeight = Math.Min(SystemParameters.PrimaryScreenHeight - interfaceHeight, img.Source.Height);
double AspectRatio = Math.Min((maxWidth / img.Source.Width), (maxHeight / img.Source.Height));
img.Width = (img.Source.Width * AspectRatio);
img.Height = (img.Source.Height * AspectRatio);
}
void Rotate(int degrees)
{
var rt = new RotateTransform { Angle = Degrees = degrees };
img.LayoutTransform = rt;
}
void Rotate(bool right)
{
switch (Degrees)
{
case 0:
if (right)
{
Rotate(270);
}
else
{
Rotate(90);
}
break;
case 90:
if (right)
{
Rotate(0);
}
else
{
Rotate(180);
}
break;
case 180:
if (right)
{
Rotate(90);
}
else
{
Rotate(270);
}
break;
case 270:
if (right)
{
Rotate(180);
}
else
{
Rotate(0);
}
break;
}
}
}
}
问题的根源在于,您只是根据未旋转的图像大小计算缩放比例。旋转图像后,img.ActualHeight
实际上变成了它的宽度,img.ActualWidth
实际上变成了它的高度,并且从图像未旋转时开始的计算不再正确。
以下是我对您的代码所做的更改和添加:
private double normalRatio;
private double rotatedRatio;
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
double interfaceHeight = this.ActualHeight - img.ActualHeight;
normalRatio = Math.Min(SystemParameters.WorkArea.Width / img.Source.Width, (SystemParameters.WorkArea.Height - interfaceHeight) / img.Source.Height);
rotatedRatio = Math.Min(SystemParameters.WorkArea.Width / img.Source.Height, (SystemParameters.WorkArea.Height - interfaceHeight) / img.Source.Width);
ScaleImage();
}
private void ScaleImage()
{
double ratio = Degrees == 0 || Degrees == 180 ? normalRatio : rotatedRatio;
img.Width = (img.Source.Width * ratio);
img.Height = (img.Source.Height * ratio);
}
void Rotate(bool right)
{
if (right)
{
Degrees -= 90;
if (Degrees < 0) { Degrees += 360; }
}
else
{
Degrees += 90;
if (Degrees >= 360) { Degrees -= 360; }
}
ScaleImage();
Rotate(Degrees);
}
//I left the other methods, including Rotate(int degrees), the same as in your question
以下是对我所做更改的解释:
interfaceHeight
的计算方法是从 window 的高度减去图像的高度,差值是其他所有内容的总大小。- 我没有使用
MonitorInfo
,而是使用SystemParameters.WorkArea
,因为它考虑了 Windows 任务栏的大小和位置。 - 我计算两个比例:
normalRatio
,当图像不旋转或垂直翻转(180°)时,rotatedRatio
,当图像在任一方向旋转 90° 时。我通过交换img.Source.Height
和img.Source.Width
. 来计算后者
- 我添加了一个
ScaleImage()
方法来根据预期的旋转进行实际图像缩放,因此我可以从两个不同的地方调用它。 - 我简化了
Rotate(bool right)
使用数学计算新角度,而不是列出每个可能的旋转。
上面的结果是在保持原始宽高比的同时,图像总是尽可能大。它会随着屏幕的旋转而增大和缩小。如果您希望图像保持恒定大小,只需使用 Math.Min(normalRatio, rotatedRatio)
.
请注意,以上仅在您调用 Rotate(bool right)
时有效,如果您直接调用 Rotate(int degrees)
,则 无效。这是因为使用两个比率的逻辑仅适用,因为图像只有两种可能的尺寸(纵向和横向),只有将旋转限制为 90° 的增量时才会出现这种情况。如果你想将角度设置为其他角度,比如 20°,计算图像有效尺寸的数学运算会变得有点复杂,你需要开始根据角度动态计算它。