我找不到在Windows窗体按钮中缩放图像的方法。请参阅下面DPI 200%上显示的Windows窗体设计器的样子(我知道Windows窗体设计器应仅用于DPI 100%/ 96,这个截图只是说明了我的观点)。
当按钮尺寸正确缩放(34x33)时,按钮尺寸中的图像不会缩放/拉伸/缩放(它仍为16x16)。我做了很多尝试来解决这个问题:
AutoScaleMode
设置为Font
,将其设置为Dpi
不会使其正常工作。AutoSize
设置为true
或false
无效。AutoSizeMode
设置为任何值都不起作用。Button.ImageLayout
可以设置为Stretch
或Zoom
。App.Config
设置<add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />
无效。FlatStyle
或ImageAlign
无效。你是如何在你的应用中解决这个问题的?
答案 0 :(得分:6)
因此,尽管MS哲学是to go toward out-of-the-box stretched images for Windows Form Controls when high DPI,但看起来Button上的图像需要手动拉伸。当然,更好的解决方案是,为用户显示的每个位图(在按钮上和其他任何位置)定义几个适应250%200%150%和125%DPI的位图。
以下是代码:
public static IEnumerable<IDisposable> AdjustControlsThroughDPI(this Control.ControlCollection controls) {
Debug.Assert(controls != null);
if (DPIRatioIsOne) {
return new IDisposable[0]; // No need to adjust on DPI One
}
var list = new List<IDisposable>();
foreach (Control control in controls) {
if (control == null) { continue; }
var button = control as ButtonBase;
if (button != null) {
button.AdjustControlsThroughDPI(list);
continue;
}
// Here more controls tahn button can be adjusted if needed...
// Recursive
var nestedControls = control.Controls;
Debug.Assert(nestedControls != null);
if (nestedControls.Count == 0) { continue; }
var disposables = nestedControls.AdjustControlsThroughDPI();
list.AddRange(disposables);
}
return list;
}
private static void AdjustControlsThroughDPI(this ButtonBase button, IList<IDisposable> list) {
Debug.Assert(button != null);
Debug.Assert(list != null);
var image = button.Image;
if (image == null) { return; }
var imageStretched = image.GetImageStretchedDPI();
button.Image = imageStretched;
list.Add(imageStretched);
}
private static Image GetImageStretchedDPI(this Image imageIn) {
Debug.Assert(imageIn != null);
var newWidth = imageIn.Width.MultipliedByDPIRatio();
var newHeight = imageIn.Height.MultipliedByDPIRatio();
var newBitmap = new Bitmap(newWidth, newHeight);
using (var g = Graphics.FromImage(newBitmap)) {
// According to this blog post http://blogs.msdn.com/b/visualstudio/archive/2014/03/19/improving-high-dpi-support-for-visual-studio-2013.aspx
// NearestNeighbor is more adapted for 200% and 200%+ DPI
var interpolationMode = InterpolationMode.HighQualityBicubic;
if (s_DPIRatio >= 2.0f) {
interpolationMode = InterpolationMode.NearestNeighbor;
}
g.InterpolationMode = interpolationMode;
g.DrawImage(imageIn, new Rectangle(0, 0, newWidth, newHeight));
}
imageIn.Dispose();
return newBitmap;
}
请注意,将返回创建的可枚举一次性位图。如果你不在乎在按钮上处理位图,你就不必在意处理拉伸的位图。
请注意我们处理原始按钮位图。
请注意我们自己的会员处理DPI:MultipliedByDPIRatio(this int)
,DPIRatioIsOne:bool
,s_DPIRatio
。你可以写自己的,棘手的一点是获得实际的DPI比率。为了收集DPI比率,我发现的最佳方式是this one。
请注意对博客文章Improving High-DPI support for Visual Studio 2013的引用,其中VS团队解释说,对于他们的图标样式,他们确定图像在200%,100%之间拉伸[最好用Bicubic算法实现,并且高于或等于使用朴素最近邻算法可以达到200%。提供的代码反映了这些选择。
编辑:在200%DPI下各种插值模式的屏幕截图下,恕我直言InterpolationMode.HighQualityBicubic
优于InterpolationMode.NearestNeighbor
。
答案 1 :(得分:3)
这是一个随时可用的助手类,它基于接受的答案,包括检索DPI比例,并增加了对PictureBox图像缩放的支持:
public static class HighDpiHelper
{
public static void AdjustControlImagesDpiScale(Control container)
{
var dpiScale = GetDpiScale(container).Value;
if (CloseToOne(dpiScale))
return;
AdjustControlImagesDpiScale(container.Controls, dpiScale);
}
private static void AdjustButtonImageDpiScale(ButtonBase button, float dpiScale)
{
var image = button.Image;
if (image == null)
return;
button.Image = ScaleImage(image, dpiScale);
}
private static void AdjustControlImagesDpiScale(Control.ControlCollection controls, float dpiScale)
{
foreach (Control control in controls)
{
var button = control as ButtonBase;
if (button != null)
AdjustButtonImageDpiScale(button, dpiScale);
else
{
var pictureBox = control as PictureBox;
if (pictureBox != null)
AdjustPictureBoxDpiScale(pictureBox, dpiScale);
}
AdjustControlImagesDpiScale(control.Controls, dpiScale);
}
}
private static void AdjustPictureBoxDpiScale(PictureBox pictureBox, float dpiScale)
{
var image = pictureBox.Image;
if (image == null)
return;
if (pictureBox.SizeMode == PictureBoxSizeMode.CenterImage)
pictureBox.Image = ScaleImage(pictureBox.Image, dpiScale);
}
private static bool CloseToOne(float dpiScale)
{
return Math.Abs(dpiScale - 1) < 0.001;
}
private static Lazy<float> GetDpiScale(Control control)
{
return new Lazy<float>(() =>
{
using (var graphics = control.CreateGraphics())
return graphics.DpiX / 96.0f;
});
}
private static Image ScaleImage(Image image, float dpiScale)
{
var newSize = ScaleSize(image.Size, dpiScale);
var newBitmap = new Bitmap(newSize.Width, newSize.Height);
using (var g = Graphics.FromImage(newBitmap))
{
// According to this blog post http://blogs.msdn.com/b/visualstudio/archive/2014/03/19/improving-high-dpi-support-for-visual-studio-2013.aspx
// NearestNeighbor is more adapted for 200% and 200%+ DPI
var interpolationMode = InterpolationMode.HighQualityBicubic;
if (dpiScale >= 2.0f)
interpolationMode = InterpolationMode.NearestNeighbor;
g.InterpolationMode = interpolationMode;
g.DrawImage(image, new Rectangle(new Point(), newSize));
}
return newBitmap;
}
private static Size ScaleSize(Size size, float scale)
{
return new Size((int)(size.Width * scale), (int)(size.Height * scale));
}
}