WPFとは?
今回作成のプログラムとソースコード
ScrollViewerに画像を表示する
表示した画像を拡大縮小させる
マウス位置を中心に画像を拡大縮小させる

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開発にチャレンジしてみてください。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です