WPF で画像を表示したい。
アプリケーションの Window に画像を表示する場面で活躍するのが Image コントロールです。
このコントロールを使用すると、画像ファイル(.jpg / .png / .bmp 等)を読み込むことができます。
WPF で画像を表示する方法をお探しの方は、この記事を参考にしてみてください。
オススメの参考書
C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。
WPFで画像を表示するには
WPFで画像を表示するいくつかの方法がありますが、簡単な方法として「Image」コントロールを使用する方法があります。
<Image …/>
このコントロールは画像を表示するだけでなく、画像のサイズやトリミング、回転などをXAML上で行うことができます。
対応している画像ファイルの形式は次の通りです。一般的に使用する画像ファイルはサポートしていますね。
- ビットマップ(*.bmp)
- ジェイペグ(*.jpg)
- ピング(*.png)
- ティフ(*.tiff)
- ジフ(*.gif)
- アイコン(*.icon)
- Microsoft Windows Media Photo
Imageコントロールの使い方
それでは Image コントロールを使って画像を表示する方法について紹介をします。
まずはアプリケーション上に表示する画像をプロジェクトへ追加しましょう。
準備
Visual Studio のプロジェクトに新しいフォルダを任意の名前(ここでは Images)で追加します。
次に追加した Images フォルダに画像ファイルを追加します。上述した Image コントロールがサポートしている画像ファイルの形式を選択しましょう。
最後に VS_logo.png のプロパティを開いて、ビルドアクションを「コンテンツ」として実行ファイルのフォルダの階層に出力されるように設定します。また出力ディレクトリにコピーを「新しい場合はコピーする」に設定します。
これで準備は完了です。
ちなみにファイル名から予測はつくと思いますが、ここで追加したファイルは Visual Studio のロゴマークです。
Image コントロールに上図のロゴマークが表示されれば成功です。
基本的な使い方
MainWindow の XAML 上で、Image コントロールの Source プロパティに画像ファイルのパスを指定します。
XAML コードは次の通りです。
<Window
x:Class="ImageSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ImageSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="250"
mc:Ignorable="d">
<Grid>
<Image Source="Images/VS_logo.png" />
</Grid>
</Window>
上記のコードを実行すると次のような結果になります。
XAMLの記述もシンプルで簡単です。
注意点として、この方法で表示した画像ファイルが Image コントロールで表示されている間はファイルがロックされます。そのため、ロック中の画像ファイルを削除しようとすると下図のようなメッセージ画面が表示され、削除することができません。
C#のコードで記述する方法
コードで Image.Source を設定する場合は BitmapImage クラスのインスタンスが必要です。
コードで記述する場合は、XAML のように画像のファイルパスを渡すだけでは Image コントロールに画像を表示することができません。
BitmapImage は ISupportInitialize インターフェイスを実装して複数のプロパティの初期化を行います。ISupportInitialize は、Beginlnit と EndInit のペアで使用します。BeginInit を呼び出して初期化が開始されたことをオブジェクトへ通知し、EndInit を呼び出して初期化が完了したことをオブジェクトへ通知します。 初期化の完了後は、プロパティの変更は無視されます。
このとこを踏まえて実装したコードが以下になります。
<Window
x:Class="ImageSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ImageSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="250"
mc:Ignorable="d">
<Grid>
<Image Name="image" />
</Grid>
</Window>
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
namespace ImageSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
BitmapImage bmpImage = new BitmapImage();
using (FileStream stream = File.OpenRead(@"Images/VS_Logo.png"))
{
bmpImage.BeginInit();
bmpImage.StreamSource = stream;
bmpImage.DecodePixelWidth = 500;
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.CreateOptions = BitmapCreateOptions.None;
bmpImage.EndInit();
bmpImage.Freeze();
}
image.Source = bmpImage;
}
}
}
上記のコードを実行すると次のような結果になります。
XAMLでは画像ファイルはロックされましたが、コードでは細かくプロパティの設定を行うことができるので、ファイルをロックしないようにすることができます。
上記で紹介しているコードは CacheOption プロパティを BitmapCacheOption.OnLoad にしているので読み込み先のファイルを占有しなくなります。
また、DecodePixelWidth プロパティは画像がデコードされる幅をピクセル単位で設定することができます。
bmpImage.DecodePixelWidth = 50
bmpImage.DecodePixelWidth = 500
小さい画像を表示する場合は、値を小さくすることでメモリ容量を節約することができます。値を小さくするということは左の画像のように解像度が荒くなります。
ドラッグ&ドロップされた画像を表示する方法
まずコントロールにドロップができるように AllowDrop プロパティを true に設定します。デフォルト値は false になっており、コントロールにドロップができないようになっています。
Image コントロールに Drop イベントを追加します。Drop イベントはデータがドロップ先にドロップされると発生します。
GetData メソッドを使用して DataObject から転送されたデータを抽出して、ファイルのパスを取得します。
ファイルの拡張子が Image コントロールで表示できる拡張子か確認した後に表示します。
実装したコードが以下になります。
<Window
x:Class="ImageSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ImageSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="250"
mc:Ignorable="d">
<Grid>
<Image Name="image"
AllowDrop="True"
Drop="image_Drop"
Source="Images/VS_Logo.png" />
</Grid>
</Window>
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
namespace ImageSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void image_Drop(object sender, DragEventArgs e)
{
var fileNames = e.Data.GetData(DataFormats.FileDrop) as string[];
if (fileNames is null) return; // nullなら返す
var fileName = fileNames[0]; // 画像ファイルのパスを取得
var ext = Path.GetExtension(fileName).ToLower(); // 拡張子の確認
// ファイルの拡張子が対応しているか確認する
if (ext != ".bmp" && ext != ".jpg" && ext != ".jpeg" && ext != ".png" && ext != ".tiff" && ext != ".gif" && ext != ".icon")
{
MessageBox.Show("画像ファイルを選択してください。", "お知らせ", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
BitmapImage bmpImage = new BitmapImage();
using (FileStream stream = File.OpenRead(fileName))
{
bmpImage.BeginInit();
bmpImage.StreamSource = stream;
bmpImage.DecodePixelWidth = 500;
bmpImage.CacheOption = BitmapCacheOption.OnLoad;
bmpImage.CreateOptions = BitmapCreateOptions.None;
bmpImage.EndInit();
}
image.Source = bmpImage;
}
}
}
上記のコードを実行すると次のような結果になります。
画像を拡大縮小する方法
ScrollViewer と Canvas と Image を組み合わせます。こうする事で画像を拡大した時に、画像サイズが Window より大きい場合はスクロールを表示できるようになります。
XAML の構成としては、ScrollViewer の子要素に Canvas、さらにその子要素に Image となるようにします。Canvasを使用せず、ScrollViewerに直接画像を配置することも可能ですが、画像をただ表示するだけでなく、拡大縮小や画像の加工(線を引いたり)を行う際には、Canvasを使うと非常に便利です。
画像の拡大縮小は描画時の画像の高さと幅に、拡大時は現在のサイズに1.2倍をかける、縮小する時は現在のサイズに0.8倍をかけることで拡大縮小を行います。
ただ画像を拡大縮小しても ScrolViewer には反映されないので、Image コントロールの SizeChanged イベントを使って canvas のサイズを画像サイズと同じにします。
マウス位置を中心に画像を拡大縮小させるためには、scrollViewer.ScrollToHorizontalOffsetやscrollViewer.ScrollToVerticalOffsetを使用して、スクロールバーの位置を調整します。
実装したコードが以下になります。
<Window
x:Class="ImageSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ImageSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="250"
mc:Ignorable="d">
<Grid>
<ScrollViewer
Name="scrollViewer"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible">
<Canvas
Name="canvas"
Margin="0"
PreviewMouseWheel="canvas_PreviewMouseWheel">
<Image
Name="image"
Width="{Binding Width, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SizeChanged="image_SizeChanged"
Source="Images/VS_Logo.png"
Stretch="UniformToFill" />
</Canvas>
</ScrollViewer>
</Grid>
</Window>
using System.Windows;
namespace ImageSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void image_SizeChanged(object sender, SizeChangedEventArgs e)
{
canvas.Height = e.NewSize.Height;
canvas.Width = e.NewSize.Width;
}
private void canvas_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
e.Handled = true; // 後続のイベントを実行しないための処理
double scale;
if (0 < e.Delta)
{
scale = 0.8; // 20%の倍率で縮小
}
else
{
scale = 1.2; // 20%の倍率で拡大
}
// 画像の拡大縮小
image.Height = image.ActualHeight * scale;
image.Width = image.ActualWidth * scale;
// マウス位置が中心になるようにスクロールバーの位置を調整
Point mousePoint = e.GetPosition(scrollViewer);
double x_barOffset = (scrollViewer.HorizontalOffset + mousePoint.X) * scale - mousePoint.X;
scrollViewer.ScrollToHorizontalOffset(x_barOffset);
double y_barOffset = (scrollViewer.VerticalOffset + mousePoint.Y) * scale - mousePoint.Y;
scrollViewer.ScrollToVerticalOffset(y_barOffset);
}
}
}
上記のコードを実行してマウスホイールを回転させると、次のような結果になります。
表示した画像をファイルとして保存する方法
Image コントロールに保存した画像を保存したい場合は、保存したいファイルの拡張子に合わせてエンコーダークラスを利用します。
各エンコーダークラスには Save メソッドがあり、このメソッドでイメージを指定した Stream にエンコードします。
実装したコードが以下になります。
<Window
x:Class="ImageSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ImageSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="265"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Image
Name="image"
Grid.Row="0"
DockPanel.Dock="Top"
Source="Images/VS_Logo.png" />
<Button
Name="btnSave"
Grid.Row="1"
Click="btnSave_Click"
Content="保存" />
</Grid>
</Window>
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
namespace ImageSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
var source = (BitmapSource)image.Source;
if (source is null) return;
// SaveFileDialogを表示
var sfd = new SaveFileDialog();
sfd.Title = "画像ファイルを保存する";
sfd.InitialDirectory = @"C:\";
sfd.FileName = @"sample.png";
sfd.Filter = "Image files (*.png, *.bmp, *.jpeg) | *.png; *.bmp; *.jpeg ";
if (false == sfd.ShowDialog()) return;
// BitmapSourceを保存する
using (Stream stream = new FileStream(sfd.FileName, FileMode.Create))
{
var ext = Path.GetExtension(sfd.FileName).ToLower();
if (ext == ".png")
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
}
else if (ext == ".bmp")
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
}
else if (ext == ".jpeg")
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
}
}
}
}
}
上記のコードを実行すると指定したフォルダに画像ファイルとして保存されます。
まとめ
この記事では WPF で画像を表示する Image コントロールの使い方について紹介しました。
Image コントロールは一般的に使用する画像ファイルはサポートしているので、画像を表示する場面では積極的に使用しても問題なさそうです。
- ビットマップ(*.bmp)
- ジェイペグ(*.jpg)
- ピング(*.png)
- ティフ(*.tiff)
- ジフ(*.gif)
- アイコン(*.icon)
- Microsoft Windows Media Photo
以上、最後まで読んで頂きありがとうございました。