
WPFとは?
WPF(Windows Presentation Foundation)とは、マイクロソフトが開発したデスクトップ クライアント アプリケーションを作るフレームワークで、WindowsFormとは違い、ユーザインターフェースとロジックを分離して書くことが出来ます。画面の設計はHTMLのように画面定義が行えるXAMLを用いて開発を行います。
今回作成したプログラム
画像をドラッグ&ドロップし、画像を表示します。

表示した画像サイズがウィンドウのサイズより大きい場合、スクロールバーが活性化されます。

Ctrlキーを押しながら、マウスのホイールを回転させることで、画像を拡大することができます。

ソースコード(xaml)
<Window x:Class="WpfEditorSample.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfEditorSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer Name="scrollViewer"
Grid.Row="0" Margin="10,10,10,0"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
Background="Azure">
<Canvas Name="canvas" Margin="0,0,0,0"
PreviewMouseWheel="canvas_PreviewMouseWheel"
Background="AliceBlue" >
<Canvas.RenderTransform>
<MatrixTransform x:Name="matrixTransform" />
</Canvas.RenderTransform>
<Image Name="image" AllowDrop="True"
Source="pack://application:,,,/Resources/drag_and_drop.png"
Drop="image_Drop"
SizeChanged="image_SizeChanged"/>
</Canvas>
</ScrollViewer>
<!-- 下部表示領域 -->
<StackPanel Grid.Row="1" Margin="10,5,10,5"
Orientation="Horizontal">
<TextBlock Text="倍率"/>
<TextBlock Text="{Binding Value, ElementName=rate}"/>
<TextBlock Text="%"/>
<Slider Name="rate"
Width="200"
IsSnapToTickEnabled="True"
Minimum="25"
Maximum="400"
Ticks="25,50,75,100,125,150,200,300,400"
Value="100"
Visibility="Hidden"/>
</StackPanel>
</Grid>
</Window>
ソースコード(cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfEditorSample
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// ドラッグ&ドロップされた画像を表示する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void image_Drop(object sender, DragEventArgs e)
{
string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
// TODO ファイルの拡張子での制御
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.UriSource = new Uri(files[0], UriKind.RelativeOrAbsolute);
imageSource.EndInit();
image.Source = imageSource;
}
/// <summary>
/// 画像変更時のCanvasサイズの変更
/// Scrollviewerのスクロールを出す用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void image_SizeChanged(object sender, SizeChangedEventArgs e)
{
canvas.Height = e.NewSize.Height;
canvas.Width = e.NewSize.Width;
}
private void canvas_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) <= 0)
{
return;
}
// 後続のイベントを実行しないための処理
e.Handled = true;
// マウスホイールのイベントを受け取り、スライダーをずらす
var index = rate.Ticks.IndexOf(rate.Value);
if (0 < e.Delta)
{
index += 1;
}
else
{
index -= 1;
}
if (index < 0)
{
return;
}else if(rate.Ticks.Count <= index)
{
return;
}
Double scale = rate.Ticks[index] / rate.Value;
rate.Value = rate.Ticks[index];
// canvasサイズの変更
canvas.Height *= scale;
canvas.Width *= scale;
// canvasの拡大縮小
Matrix m0 = new Matrix();
m0.Scale(rate.Value * 0.01, rate.Value * 0.01);//元のサイズとの比
matrixTransform.Matrix = m0;
// scrollViewerのスクロールバーの位置をマウス位置を中心とする。
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);
}
}
}
ScrollViewerに画像を描画する
ScrollViewerに画像を描画します。今回は、ScrollViewerの子要素にCanvas、さらにその子要素にImageを表示します。Canvasを使用せず、ScrollViewerに直接画像を配置することも可能ですが、画像をただ表示するだけでなく、拡大縮小や画像への加工(線を引いたり)を行う際には、Canvasが非常に便利です。

<ScrollViewer Name="scrollViewer"
Grid.Row="0" Margin="10,10,10,0"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
Background="Azure">
<Canvas Name="canvas" Margin="0,0,0,0"
PreviewMouseWheel="canvas_PreviewMouseWheel"
Background="AliceBlue" >
<Canvas.RenderTransform>
<MatrixTransform x:Name="matrixTransform" />
</Canvas.RenderTransform>
<Image Name="image" AllowDrop="True"
Source="pack://application:,,,/Resources/drag_and_drop.png"
Drop="image_Drop"
SizeChanged="image_SizeChanged"/>
</Canvas>
</ScrollViewer>
各タグには、Nameをつけることが出来ます。Nameをつけることで、XXXX.xaml.cs側で簡単に操作することが可能です。
例えば、今回、ドラッグ&ドロップした際に画像を表示する機能を実装しております。
Imageタグの属性に、Drop=”image_Drop” があります。これは、ファイルをドロップした際に、Image_Drop関数が実行されます。
表示した画像を拡大縮小する
今回はCtrlを押したままマウスのホイールを回した時に、拡大縮小する機能を作成しております。これは、2段階に分けて説明します。
まずは、画像の拡大縮小はCanvas自体を拡大縮小しています。
Canvasの拡大縮小を行うためには、Canvasのxamlに次のタグをセットします。
<Canvas.RenderTransform>
<MatrixTransform x:Name="matrixTransform" />
</Canvas.RenderTransform>
xcml.cs側では、次のように記述することで、画像を拡大縮小できます。
// canvasの拡大縮小
Double scale = 2.0; //2倍にする場合
Matrix m0 = new Matrix();
m0.Scale(scale, scale);//元のサイズとの比
matrixTransform.Matrix = m0;
次に、この処理をマウスのイベントと連動させます。
Canvasタグの属性に以下のイベントをセットします。previewで始まるイベントは、子要素のイベントより先に発火するため、子要素にコントロールが重なっている場合でも、イベントを付けることが出来ます。
PreviewMouseWheel="canvas_PreviewMouseWheel"
また、ただCanvasをmatrixTransformで拡大縮小させても、ScrollViewerには反映されません。そこで、canvasのサイズを変更してあげる必要があります。
Double scale = rate.Ticks[index] / rate.Value; // 新しい倍率 / 元の倍率
// canvasサイズの変更
canvas.Height *= scale;
canvas.Width *= scale;
マウス位置を中心に画像を拡大縮小させる
マウス位置を中心に画像を拡大縮小させるためには、スクロールバーの位置を適切に補正する必要があります。
scrollViewer.ScrollToHorizontalOffsetやscrollViewer.ScrollToVerticalOffsetを使用し、スクロールバーの位置を指定します。
// scrollViewerのスクロールバーの位置をマウス位置を中心とする。
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);
終わりに
WPFはまだネット上に情報が多くなく、まだまだWindowsFormを使っている人が多い印象ですが、WPFの書き方に慣れることで、画面開発が非常に円滑に進むと感じています。ぜひWPFでのGUI開発にチャレンジしてみてください。