我想在WPF应用程序中显示一些图像,不过Microsoft" smartly"将所有内容扩展到错误的比例!我想要的是以像素精度显示图像。即如果图像是50x50,我想在屏幕上显示50个像素和50个像素,无论图像/屏幕的DPI设置是什么。
我尝试过以下操作,但不起作用:
[assembly: System.Windows.Media.DisableDpiAwareness]
行
醇>
问题是,我离屏幕很远,所以在控制面板中我将文字大小设置得更大。但我可以看出图像尺寸不正确,因为当我在Photoshop中并排打开相同的图像时它会变得更大和模糊。
答案 0 :(得分:0)
Dwayne Need创建了一篇关于此问题的优秀博文,可以找到here。该代码也可在同一位置使用。引用他:
下面的代码介绍了一个名为Bitmap的新类。位图是Image的替代品,但它不显示任何图像源,而只显示位图源。这使我可以访问PixelWidth和PixelHeight属性来确定合适的大小。这个课程的重要方面是:
- 派生自UIElement而不是FrameworkElement,因为我不想要像MinWidth,MaxWidth甚至Width这样的东西。
- Bitmap.Source可以设置为任何BitmapSource。
- 测量时,会返回相应的测量单位以显示位图的PixelWidth和PixelHeight
- 渲染时,会将其绘制的图像偏移以与像素网格对齐
- 每当布局更新时,它会检查是否需要重新渲染以再次与像素网格对齐。
这是一个非常简单的课程,请查看详细信息的源代码。
我希望他也不介意我在这里分享代码:
示例窗口(在原始代码中可以找到更好的示例...):
<Window x:Class="ImageSnappingToPixels.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ImageSnappingToPixels"
Height="300" Width="600">
<Grid x:Name="root">
<StackPanel Orientation="Horizontal">
<local:Bitmap Source="picture1.png" />
<local:Bitmap Source="picture2.png" />
</StackPanel>
</Grid>
</Window>
这是他完整且完整的Bitmap类:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ImageSnappingToPixels
{
class Bitmap : UIElement
{
public Bitmap()
{
_sourceDownloaded = new EventHandler(OnSourceDownloaded);
_sourceFailed = new EventHandler<ExceptionEventArgs>(OnSourceFailed);
LayoutUpdated += new EventHandler(OnLayoutUpdated);
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
"Source",
typeof(BitmapSource),
typeof(Bitmap),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(Bitmap.OnSourceChanged)));
public BitmapSource Source
{
get
{
return (BitmapSource) GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
public event EventHandler<ExceptionEventArgs> BitmapFailed;
// Return our measure size to be the size needed to display the bitmap pixels.
protected override Size MeasureCore(Size availableSize)
{
Size measureSize = new Size();
BitmapSource bitmapSource = Source;
if(bitmapSource != null)
{
PresentationSource ps = PresentationSource.FromVisual(this);
if (ps != null)
{
Matrix fromDevice = ps.CompositionTarget.TransformFromDevice;
Vector pixelSize = new Vector(bitmapSource.PixelWidth, bitmapSource.PixelHeight);
Vector measureSizeV = fromDevice.Transform(pixelSize);
measureSize = new Size(measureSizeV.X, measureSizeV.Y);
}
}
return measureSize;
}
protected override void OnRender(DrawingContext dc)
{
BitmapSource bitmapSource = this.Source;
if (bitmapSource != null)
{
_pixelOffset = GetPixelOffset();
// Render the bitmap offset by the needed amount to align to pixels.
dc.DrawImage(bitmapSource, new Rect(_pixelOffset, DesiredSize));
}
}
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Bitmap bitmap = (Bitmap)d;
BitmapSource oldValue = (BitmapSource)e.OldValue;
BitmapSource newValue = (BitmapSource)e.NewValue;
if (((oldValue != null) && (bitmap._sourceDownloaded != null)) && (!oldValue.IsFrozen && (oldValue is BitmapSource)))
{
((BitmapSource)oldValue).DownloadCompleted -= bitmap._sourceDownloaded;
((BitmapSource)oldValue).DownloadFailed -= bitmap._sourceFailed;
// ((BitmapSource)newValue).DecodeFailed -= bitmap._sourceFailed; // 3.5
}
if (((newValue != null) && (newValue is BitmapSource)) && !newValue.IsFrozen)
{
((BitmapSource)newValue).DownloadCompleted += bitmap._sourceDownloaded;
((BitmapSource)newValue).DownloadFailed += bitmap._sourceFailed;
// ((BitmapSource)newValue).DecodeFailed += bitmap._sourceFailed; // 3.5
}
}
private void OnSourceDownloaded(object sender, EventArgs e)
{
InvalidateMeasure();
InvalidateVisual();
}
private void OnSourceFailed(object sender, ExceptionEventArgs e)
{
Source = null; // setting a local value seems scetchy...
BitmapFailed(this, e);
}
private void OnLayoutUpdated(object sender, EventArgs e)
{
// This event just means that layout happened somewhere. However, this is
// what we need since layout anywhere could affect our pixel positioning.
Point pixelOffset = GetPixelOffset();
if (!AreClose(pixelOffset, _pixelOffset))
{
InvalidateVisual();
}
}
// Gets the matrix that will convert a point from "above" the
// coordinate space of a visual into the the coordinate space
// "below" the visual.
private Matrix GetVisualTransform(Visual v)
{
if (v != null)
{
Matrix m = Matrix.Identity;
Transform transform = VisualTreeHelper.GetTransform(v);
if (transform != null)
{
Matrix cm = transform.Value;
m = Matrix.Multiply(m, cm);
}
Vector offset = VisualTreeHelper.GetOffset(v);
m.Translate(offset.X, offset.Y);
return m;
}
return Matrix.Identity;
}
private Point TryApplyVisualTransform(Point point, Visual v, bool inverse, bool throwOnError, out bool success)
{
success = true;
if (v != null)
{
Matrix visualTransform = GetVisualTransform(v);
if (inverse)
{
if (!throwOnError && !visualTransform.HasInverse)
{
success = false;
return new Point(0, 0);
}
visualTransform.Invert();
}
point = visualTransform.Transform(point);
}
return point;
}
private Point ApplyVisualTransform(Point point, Visual v, bool inverse)
{
bool success = true;
return TryApplyVisualTransform(point, v, inverse, true, out success);
}
private Point GetPixelOffset()
{
Point pixelOffset = new Point();
PresentationSource ps = PresentationSource.FromVisual(this);
if (ps != null)
{
Visual rootVisual = ps.RootVisual;
// Transform (0,0) from this element up to pixels.
pixelOffset = this.TransformToAncestor(rootVisual).Transform(pixelOffset);
pixelOffset = ApplyVisualTransform(pixelOffset, rootVisual, false);
pixelOffset = ps.CompositionTarget.TransformToDevice.Transform(pixelOffset);
// Round the origin to the nearest whole pixel.
pixelOffset.X = Math.Round(pixelOffset.X);
pixelOffset.Y = Math.Round(pixelOffset.Y);
// Transform the whole-pixel back to this element.
pixelOffset = ps.CompositionTarget.TransformFromDevice.Transform(pixelOffset);
pixelOffset = ApplyVisualTransform(pixelOffset, rootVisual, true);
pixelOffset = rootVisual.TransformToDescendant(this).Transform(pixelOffset);
}
return pixelOffset;
}
private bool AreClose(Point point1, Point point2)
{
return AreClose(point1.X, point2.X) && AreClose(point1.Y, point2.Y);
}
private bool AreClose(double value1, double value2)
{
if (value1 == value2)
{
return true;
}
double delta = value1 - value2;
return ((delta < 1.53E-06) && (delta > -1.53E-06));
}
private EventHandler _sourceDownloaded;
private EventHandler<ExceptionEventArgs> _sourceFailed;
private Point _pixelOffset;
}
}