Als WPF-Entwickler, der hin und wieder mit einem Designer zusammen arbeitet, wird man früher oder später mit dem Problem konfrontiert, dass die zugelieferten Grafiken in der Anwendung größer dargestellt werden, als sie abgespeichert wurden.

Dies wird immer dann auftreten, wenn ein Control die Größe des darin enthaltenen Bildes annehmen soll, zum Beispiel ein Button.

Ursache hierfür ist ziemlich sicher, dass der Designer die Grafiken mit einem Apple-Mac erstellt hat. Dieser arbeitet standardmäßig mit 72dpi für die Grafiken, während Microsoft in Windows 96dpi verwendet. Wird das Bild in WPF dargestellt, wird es um den Faktor 96/72 = 1,33 größer dargestellt.
Eine relativ schlechte Lösung wäre, die Bild-Größen statisch im Code zu hinterlegen. Spätere Größenänderung des Bildes müssten dann jedes Mal zusätzlich auch im Code nachgearbeitet werden.

Da ich in meinem Projekten recht häufig mit solchen Konstellationen arbeite, habe ich ein kleines UserControl entwickelt, dass sich diesem Problem annimmt. Beim Öffnen des Bildes wird die Orginalgröße ausgelesen und dem Bild zugewiesen. Der DPI-Wert wird ignoriert.

Das UserControl arbeitet aktuell nur mit einem BitmapSource als Source des Bildes, zum Beispiel eine Pfad zu einem Bild.

Folgend der Code für das Control:

public class DPIAwareImage : Image
{
	static DPIAwareImage()
	{
		// listen to changes to stretch property, and set default value to fill
		StretchProperty.OverrideMetadata(typeof(DPIAwareImage), new FrameworkPropertyMetadata(Stretch.Fill, new PropertyChangedCallback(StretchPropertyChanged)));
		/// listen to changes to source property
		SourceProperty.OverrideMetadata(typeof(DPIAwareImage), new FrameworkPropertyMetadata(new PropertyChangedCallback(SourcePropertyChanged)));
	}

	// check if a Stretch other then Fill is set.
	private static void StretchPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
	{
		if (args.NewValue == null)
		{
			return;
		}

		if ((Stretch)args.NewValue != Stretch.Fill)
		{
			throw new NotSupportedException("DPIAwareImage only works with Stretch=Fill.");
		}
	}

	// check the new image source and set the origin width and height if available
	private static void SourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
	{
		var source = args.NewValue as BitmapSource;

		if (source == null)
		{
			return;
		}

		var image = sender as DPIAwareImage;

		image.Stretch = Stretch.Fill;
		image.Width = source.PixelWidth;
		image.Height = source.PixelHeight;
	}
}

In den Zeilen 38 und 39 wird die Orginalgröße der Datenquelle ausgelesen und dem Bild zugewiesen.

Das Control überwacht neben Änderungen der Source-Eigenschaft auch die Stretch-Eigenschaft. Alles außer „Fill“ würde für den Verwendungszweck nicht funktionieren, da sich der möglicherweise „falsche“ DPI-Inhalt an die festgelegte Größe skalieren muss.