WPF学習中・表示した画像を拡大・縮小・移動する。

C# コンピュータ
C#
Thumbというコントロールはドラッグで移動することが出来ます。
そのコントロールに画像を被せて、さらにScaleTransformで拡大・縮小することで、画像を拡大・縮小・移動するプログラムにしてみました。

プロジェクトの作成

PowerShellで実行。要dotnet.exe

mkdir MoveZoomImage
cd MoveZoomImage
dotnet new wpf
dotnet add package Microsoft.Xaml.Behaviors.WPF
dotnet add package ReactiveProperty.WPF
code .

ソースコード

ファイル名:MainWindow.xaml

<Window x:Class="MoveZoomImage.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:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
        xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
        xmlns:local="clr-namespace:MoveZoomImage"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Canvas
            Background="Gray"
            Height="450"
            Width="800">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseWheel">
                    <interactivity:EventToReactiveCommand Command="{Binding MouseWheelCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
            <Thumb
                Canvas.Left="{Binding ThumbLeft.Value}"
                Canvas.Top="{Binding ThumbTop.Value}">
                <Thumb.RenderTransform>
                    <ScaleTransform ScaleX="{Binding ZoomScale.Value}" ScaleY="{Binding ZoomScale.Value}"/>
                </Thumb.RenderTransform>
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="DragCompleted">
                        <interactivity:EventToReactiveCommand Command="{Binding DragCompletedCommand}" />
                    </i:EventTrigger>
                    <i:EventTrigger EventName="DragStarted">
                        <interactivity:EventToReactiveCommand Command="{Binding DragStartedCommand}" />
                    </i:EventTrigger>
                    <i:EventTrigger EventName="DragDelta">
                        <interactivity:EventToReactiveCommand Command="{Binding DragDeltaCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <Thumb.Template>
                    <ControlTemplate TargetType="Thumb">
                        <Image
                            Stretch="None"
                            Source="E:\csharp\wpf\MoveZoomImage\logo.png">
                        </Image>
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>
        </Canvas>
    </Grid>
</Window>

ファイル名:MainWindowViewModel.cs

using Reactive.Bindings;
using System.Reactive.Disposables;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Windows;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;
using System.Windows.Input;

namespace MoveZoomImage
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string name) =>
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        
        public ReactiveProperty<double> ThumbLeft { get; private set; } = new(0.0d);
        public ReactiveProperty<double> ThumbTop { get; private set; } = new(0.0d);
        public ReactiveProperty<double> ZoomScale { get; private set; } = new(1.0d);

        public ReactiveCommand<System.Windows.Controls.Primitives.DragCompletedEventArgs> DragCompletedCommand { get; } = new();
        public ReactiveCommand<System.Windows.Controls.Primitives.DragStartedEventArgs> DragStartedCommand { get; } = new();
        public ReactiveCommand<System.Windows.Controls.Primitives.DragDeltaEventArgs> DragDeltaCommand { get; } = new();
        public ReactiveCommand<MouseWheelEventArgs> MouseWheelCommand { get; } = new();

        public MainWindowViewModel()
        {
            PropertyChanged += (o, e) => {};

            DragCompletedCommand.Subscribe(e =>
            {
                // ドラッグ完了
                ThumbLeft.Value = ThumbLeft.Value + e.HorizontalChange;
                ThumbTop.Value = ThumbTop.Value + e.VerticalChange;
            });
            DragStartedCommand.Subscribe(e=>
            {
                // ドラッグ開始
            });
            DragDeltaCommand.Subscribe(e =>
            {
                // ドラッグ中
                //ThumbLeft.Value = ThumbLeft.Value + e.HorizontalChange;
                //ThumbTop.Value = ThumbTop.Value + e.VerticalChange;
            });
            MouseWheelCommand.Subscribe(e => 
            {
                // ホイール
                double x = ZoomScale.Value;

                if (e.Delta < 0)
                    x = x - 0.25d;
                else
                    x = x + 0.25d;
                
                if (x < 0.25d)
                    x = 0.25d;

                if (x > 8.0d)
                    x = 8.0d;
                
                ZoomScale.Value = x;
            });
        }
    }
}
C#でプログラミングしたのはViewModel部分だけで、内容的にはドラック中のマウスの移動量をThumの座標に連動する部分と、マウスホイールの量に合わせてScaleTransformの拡大縮小の割合を変化させるだけです。拡大縮小移動の機能のほとんどはXAMLで定義した内容になるわけで、似たようなことをWinFormで行おうとするとかなり苦労することに成ります。

実行

dotnet run

起動すると画像が表示されます。


画像をマウスでドラックすると画像が移動します。

マウスホイールを回すと画像が拡大縮小します。



GIF


追記:20240514
DragDeltaCommand内でThumbを移動していましたがDragCompletedCommandで移動するように変更しました。
等倍での移動は問題なさそうですが、拡大縮小した状態で移動距離がとても大きな数字になっており、Windowの外に移動するので画像が消えてしまいます。対策が思いつかないので、とりあえずDragCompletedCommandで一気に移動するようにしてみました。
移動の軌跡が見えなので見た目は悪いのですが、消えてしまうよりまだ良いかと思います。

コメント