WPF 標準コントロールデモアプリ › Image

Image Graphics

Image は、ビットマップのファイルなどの ImageSource から画像を表示し、与えられた領域に合わせて拡大・縮小する要素です。

Stretch と StretchDirection ごとの大きさ

Image は Control ではなく FrameworkElement から派生し、既定ではフォーカスを受け取りません(Focusable が False)。Stretch は領域に合わせた拡大・縮小の仕方で、既定値は縦横比を保つ Uniform です。300 × 200 の領域で、600 × 300 の画像は None で 600 × 300、Fill で 300 × 200、Uniform で 300 × 150、UniformToFill で 400 × 200 でした。

StretchDirection は、拡大だけ・縮小だけ・両方のどれを許すかで、既定値は Both です。UpOnly では、600 × 300 の画像はどの Stretch でも 600 × 300 のままで、DownOnly では 100 × 50 の画像が 100 × 50 のままでした。原寸より大きくしたくないアイコンには DownOnly を使います。

Stretch 画像 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

Stretch と StretchDirection ごとの表示の大きさ。.NET 10 / Windows 11 で計測。

UniformToFill と None で切れる場所

領域からはみ出した部分は切れ、切れるのは右と下です。UniformToFill の 400 × 200 の画像は、領域の左上から始まっていました。HorizontalAlignment と VerticalAlignment を Center にすると -50 から始まり、左右が均等に切れました。サムネイルのように同じ大きさの枠を UniformToFill で埋めるときは、Image を中央に揃えます。そうしないと、切り取りで画像の左上が残ります。

DPI とデコードの大きさ

拡大・縮小しないときの大きさはデバイス非依存の単位で、ピクセル数 × 96 / DPI です。72 DPI の 100 × 50 ピクセルは計算では 133.33 × 66.67 ですが、測った 72 DPI の画像は 133.36 × 66.68 でした。同じピクセル数でも DPI が低いほど大きく表示されるので、Stretch="None" ではファイルの DPI を確かめます。ここで使った PNG は解像度を pHYs チャンクに 1 メートルあたりのピクセル数の整数で保存しているため、実際に効く DPI は公称値よりわずかに低くなります。測った大きさは、72 DPI で 2834 ピクセル/メートル(71.98 DPI)、96 DPI で 3779 ピクセル/メートル(95.99 DPI)と一致し、そのため 96 DPI の画像でも幅は 100.01 になりました。

サムネイルには DecodePixelWidth を設定します。100 にすると、600 × 300 の画像は 100 × 50 ピクセルにデコードされました。画像自体の大きさも変わり、拡大・縮小しないときの幅は 600 ではなく 100 でした。

ファイルのパスのバインドと、ファイルのロック

Source にバインドしたパスの文字は画像に変換され、Source は BitmapFrameDecode になりました。存在しないファイルや SVG ファイルのパスでは、Source は null、Image は 0 × 0 のままで、例外は起きませんでした。WPF はインストールされている Windows Imaging Component のコーデックで画像をデコードし、計測したマシンには SVG のコーデックがありませんでした。SVG は変換してから表示します。

パスで表示したファイルはロックされます。そうしてバインドした PNG を表示している間、ファイルの削除は IOException で失敗し、Source を null にした後も失敗しました。削除できたのはガベージコレクションの後でした。CacheOption を OnLoad にした BitmapImage はファイルをすぐに読み込み、表示している間でもファイルを削除できました。変わる可能性のあるファイルは、この方法で読み込みます。

条件 計測値
基底クラス、Focusable、Stretch と StretchDirection の既定値 FrameworkElement, False; Uniform, Both
大きい画像、UniformToFill / Both、300 x 200 の中: 大きさ、左上の位置、クリップ 400 x 200、0, 0、300 x 200
大きい画像、UniformToFill / Both、Center に配置、300 x 200 の中: 大きさ、左上の位置、クリップ 400 x 200、-50, 0、300 x 200
大きい画像、None / UpOnly、300 x 200 の中: 大きさ、左上の位置、クリップ 600.08 x 300.04、0, 0、300 x 200
72 DPI の 100 x 50 ピクセル、Stretch None: 大きさ 133.36 x 66.68
Source にパスをバインド、PNG ファイル: Source の型、大きさ BitmapFrameDecode, 100.01 x 50.01
ファイルを削除: 表示中 / Source = null の後 / GC の後 ロックされている(IOException) / ロックされている(IOException) / 削除できる
Source にパスをバインド、存在しないファイル: Source の型、大きさ null, 0 x 0
Source にパスをバインド、SVG ファイル: Source の型、大きさ null, 0 x 0
CacheOption OnLoad の BitmapImage を表示中: ファイルを削除 削除できる
600 x 300 を DecodePixelWidth 100 で: ピクセル数、DIP での大きさ、Stretch None の大きさ 100 x 50; 100.01 x 50.01; 100.01 x 50.01
デモの最初のパス img0.jpg: 存在するか、ピクセル数、DPI True; 3840 x 2400; 96

型、切り取り、DPI、パス、ファイルのロック、デコード。.NET 10 / Windows 11 で計測。

デモアプリで試す

デモアプリの Image のページ。左にコントロールの一覧、右に最初の節の Source / Stretch / StretchDirection

デモアプリの Image のページは、Source をファイルのパスを入れた TextBox の文字にバインドし、Stretch と StretchDirection を 2 つのコンボボックスで選びます。パス C:\Windows\Web\Wallpaper\Windows\img0.jpg で始まり、計測したマシン(Windows 11)には、3840 × 2400 ピクセル・96 DPI の画像としてありました。コンボボックスは先頭の値の None と UpOnly で始まるので、画像は原寸で表示され、見えるのは左上の部分だけです。欄の下の「Show Code」リンクで、その欄の XAML を表示できます。次の XAML は、その欄(ImageUsageControl.xaml)から、スタイルと周囲の GroupBox を省き、名前空間の宣言を加えたものです。デモアプリでは Image はウィンドウの結果の領域いっぱいに置かれますが、このページの計測では大きさを固定した領域(300 × 200)に配置しました。markupextensions はデモアプリ独自のマークアップ拡張の接頭辞です。

<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>

関連するコントロールと記事

ソースコードと計測の方法

このページの挙動の記述は、すべて .NET 10 / Windows 11 で実際に動かして確かめたものです。計測には、このサイトのスクリーンショット生成ツールの ImageDemoScene を使いました。画像はツールが書き出した 100 × 50 ピクセルと 600 × 300 ピクセルの PNG ファイルで、300 × 200 の領域に配置しました。パスはデモアプリと同じく TextBox からバインドし、ロックはファイルの削除を試して調べました。

GitHub で Image のソースコードを見る →