WPF Standard Control Demo App › Image

Image Graphics

Image displays a picture from an ImageSource, such as a bitmap file, and scales it to the space it is given.

The size for each Stretch and StretchDirection

Image derives from FrameworkElement, not Control, and is not focusable by default (Focusable is False). Stretch decides how the picture is scaled to the space; the default is Uniform, which keeps the proportions. In a 300 × 200 area, a 600 × 300 picture was 600 × 300 with None, 300 × 200 with Fill, 300 × 150 with Uniform, and 400 × 200 with UniformToFill.

StretchDirection decides whether the picture may be enlarged, reduced, or both; the default is Both. UpOnly kept the 600 × 300 picture at 600 × 300 in every mode, and DownOnly kept the 100 × 50 picture at 100 × 50. Use DownOnly for icons that should never be enlarged past their own size.

Stretch image UpOnly DownOnly Both
None 100 x 50 100.01 x 50.01 100.01 x 50.01 100.01 x 50.01
None 600 x 300 600.08 x 300.04 600.08 x 300.04 600.08 x 300.04
Fill 100 x 50 300 x 200 100.01 x 50.01 300 x 200
Fill 600 x 300 600.08 x 300.04 300 x 200 300 x 200
Uniform 100 x 50 300 x 150 100.01 x 50.01 300 x 150
Uniform 600 x 300 600.08 x 300.04 300 x 150 300 x 150
UniformToFill 100 x 50 400 x 200 100.01 x 50.01 400 x 200
UniformToFill 600 x 300 600.08 x 300.04 400 x 200 400 x 200

Displayed size for each Stretch and StretchDirection. Measured on .NET 10 / Windows 11.

Where UniformToFill and None cut the picture

The part outside the area is cut off, and it is cut from the right and bottom: with UniformToFill, the 400 × 200 picture started at the area’s top-left corner. With HorizontalAlignment and VerticalAlignment set to Center, it started at -50, so both sides were cut evenly. Center the Image when you fill equal cells, such as thumbnails, with UniformToFill; otherwise the crop keeps the top-left of the picture.

DPI and the decoded size

Without scaling, the size is in device-independent units, the pixels × 96 / DPI: 100 × 50 pixels at 72 DPI would be 133.33 × 66.67; the measured 72 DPI picture was 133.36 × 66.68. The same pixels are shown larger at a lower DPI, so check the file’s DPI when you use Stretch="None". The PNGs used here stored their resolution in a pHYs chunk as a whole number of pixels per metre, which makes the effective DPI slightly lower than the nominal one: the measured sizes match 2834 pixels per metre for 72 DPI (71.98 DPI) and 3779 for 96 DPI (95.99 DPI), which is why the 96 DPI picture came back as 100.01 wide.

Set DecodePixelWidth for thumbnails. With 100, a 600 × 300 picture was decoded to 100 × 50 pixels. It also changes the picture’s own size: without scaling it was shown 100 wide, not 600.

Binding a file path, and the file lock

A path string bound to Source is converted to an image: the Source became a BitmapFrameDecode. A path to a missing file, or to an SVG file, left Source null and the Image 0 × 0, without an exception. WPF decodes pictures with the Windows Imaging Component codecs installed, and the measuring machine had none for SVG. Convert SVG before showing it.

A file shown through a path is locked. While a PNG bound that way was shown, deleting the file failed with an IOException, and it still failed after Source was set to null; it succeeded only after a garbage collection. A BitmapImage with CacheOption set to OnLoad reads the file at once, and its file could be deleted while the image was shown. Load files that may change that way.

case measured
base class, Focusable; defaults Stretch, StretchDirection FrameworkElement, False; Uniform, Both
large image, UniformToFill / Both in 300 x 200: size; top-left; clip 400 x 200; 0, 0; 300 x 200
large image, UniformToFill / Both, aligned Center in 300 x 200: size; top-left; clip 400 x 200; -50, 0; 300 x 200
large image, None / UpOnly in 300 x 200: size; top-left; clip 600.08 x 300.04; 0, 0; 300 x 200
100 x 50 pixels at 72 DPI, Stretch None: size 133.36 x 66.68
path bound to Source, PNG file: Source type, size BitmapFrameDecode, 100.01 x 50.01
delete the file: while shown; Source = null; then GC locked (IOException); locked (IOException); deleted
path bound to Source, missing file: Source type, size null, 0 x 0
path bound to Source, SVG file: Source type, size null, 0 x 0
BitmapImage with CacheOption OnLoad, shown: delete the file deleted
600 x 300 with DecodePixelWidth 100: pixels; size in DIPs; Stretch None size 100 x 50; 100.01 x 50.01; 100.01 x 50.01
demo's start path img0.jpg: exists; pixels; DPI True; 3840 x 2400; 96

Type, cropping, DPI, paths, file locks, and decoding. Measured on .NET 10 / Windows 11.

Trying it in the demo app

The Image page of the demo app, with the control list on the left and the first section, Source / Stretch / StretchDirection

The Image page of the demo app binds Source to the text of a TextBox holding a file path, and sets Stretch and StretchDirection from two combo boxes. It starts with the path C:\Windows\Web\Wallpaper\Windows\img0.jpg, which existed on the measuring machine (Windows 11) as a picture of 3840 × 2400 pixels at 96 DPI. The combo boxes start at the first values, None and UpOnly, so the picture is shown at full size and only its top-left corner is visible. The “Show Code” link under the section displays its XAML. The following XAML is that section (ImageUsageControl.xaml), with the styles and the surrounding GroupBoxes left out and the namespace declarations added. In the demo app, the Image fills the result area of the window; the measurements on this page laid it out in an area of a fixed size (300 × 200) instead. markupextensions is the prefix for the demo app’s own markup extensions:

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:markupextensions="clr-namespace:WPFStandardControlDemoApp.Common.MarkupExtensions">
  <TextBox x:Name="SourceTextBox" Text="C:\Windows\Web\Wallpaper\Windows\img0.jpg" />

  <ComboBox x:Name="StretchComboBox"
            DisplayMemberPath="Name"
            ItemsSource="{markupextensions:EnumBindingSource EnumType=Stretch}"
            SelectedValuePath="Value"
            SelectedIndex="0" />
  <ComboBox x:Name="StretchDirectionComboBox"
            DisplayMemberPath="Name"
            ItemsSource="{markupextensions:EnumBindingSource EnumType=StretchDirection}"
            SelectedValuePath="Value"
            SelectedIndex="0" />

  <Grid Background="#F0F0F0">
    <Image x:Name="DemoImage"
           Source="{Binding Text, ElementName=SourceTextBox}"
           Stretch="{Binding SelectedValue, ElementName=StretchComboBox}"
           StretchDirection="{Binding SelectedValue, ElementName=StretchDirectionComboBox}" />
  </Grid>
</StackPanel>

Source code and how it was measured

Every behavior on this page was measured by running it on .NET 10 / Windows 11, using ImageDemoScene in this site’s screenshot tool. The pictures were PNG files written by the tool, of 100 × 50 and 600 × 300 pixels, laid out in a 300 × 200 area. The path was bound from a TextBox as in the demo app, and a lock was detected by trying to delete the file.

View Image source code on GitHub →