8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[xaml/C#] 枠の中で画像などを拡大縮小する(マウスと指で移動/拡大)

Last updated at Posted at 2020-01-09

もくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f

回転/拡大縮小/移動の関連記事

やること

ウインドウの中の、ある枠のなかで、写真などを拡大縮小したい。
窓の外のモノを動かしているイメージ。

窓枠の中ではそのコントロール(例ではTextBlock)が見えてるが、枠の外側にドラッグやフリックして移動させると、枠の外は見えなくなる感じにしたい。

image.pngimage.pngimage.png
こんな感じ。

やりかた

以前の記事(上の「回転/拡大縮小/移動の関連記事」のところを参照)でやった、コントロールの移動、拡大縮小を、ScrlollViewerの中でやればOK。

※今回は、指で移動/拡大縮小と同時に、マウスで移動/拡大縮小ができるようにした。

サンプル

mainWindow.xaml
<Window x:Class="WpfApp39.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:WpfApp39"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="1*"/>
                <RowDefinition Height="3*"/>
                <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>

            <ScrollViewer Name="MyScrollViewer" Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"
                          IsManipulationEnabled="True"
                          ManipulationDelta="Grid_ManipulationDelta"
                          PreviewMouseLeftButtonDown="MyGrid_MouseLeftButtonDown"
                          MouseLeftButtonUp="MyGrid_MouseLeftButtonUp"
                          MouseMove="MyGrid_MouseMove"
                          MouseLeave="MyGrid_MouseLeave"
                          PreviewMouseWheel="MyGrid_MouseWheel">
                
                <!-- このテキストBlockを回転させる -->
                <!-- ここに、位置の基準点を指定するHorizontalAlignmentとかRenderTransformOrigin、-->
                <!-- 大きさを指定するWidthやHeightを指定すると、拡大するときの中心点がずれてしまう-->
                <TextBlock x:Name="MyTarget" Text="あ" FontSize="160" Background="Pink" />
            </ScrollViewer>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApp39
{
    public partial class MainWindow : Window
    {

        /// マウス押下中フラグ
        bool isMouseLeftButtonDown = false;

        /// マウスを押下した点を保存
        Point MouseDonwStartPoint = new Point(0, 0);

        /// マウスの現在地
        Point MouseCurrentPoint = new Point(0, 0);

        public MainWindow()
        {
            InitializeComponent();

            Debug.WriteLine($"大きさ:({MyTarget.ActualWidth},{MyTarget.ActualHeight})");
        }

        /// 【指】
        /// 移動時のイベント
        private void Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            var delta = e.DeltaManipulation;
            Matrix matrix = (MyTarget.RenderTransform as MatrixTransform).Matrix;
            matrix.Translate(delta.Translation.X, delta.Translation.Y);

            var scaleDelta = delta.Scale.X;
            var orgX = e.ManipulationOrigin.X;
            var orgY = e.ManipulationOrigin.Y;
            matrix.ScaleAt(scaleDelta, scaleDelta, orgX, orgY);
            MyTarget.RenderTransform = new MatrixTransform(matrix);
        }

        /// 【マウス】
        /// マウス押下
        private void MyGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // クリックした位置を保存
            // 位置の基準にするControlはなんでもいいが、MouseMoveのほうの基準Controlと合わせること。
            MouseDonwStartPoint = e.GetPosition(MyScrollViewer);

            isMouseLeftButtonDown = true;
        }

        /// 【マウス】
        /// マウス離す
        private void MyGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isMouseLeftButtonDown = false;
        }

        /// 【マウス】
        /// マウスがコントロールの上から外れた
        private void MyGrid_MouseLeave(object sender, MouseEventArgs e)
        {
            isMouseLeftButtonDown = false;
        }

        /// 【マウス】
        /// マウスがコントロールの上を移動
        private void MyGrid_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMouseLeftButtonDown == false) return;

            // マウスの現在位置座標を取得(ScrollViewerからの相対位置)
            // ここは、位置の基準にするControl(GetPositionの引数)はScrollViewrでもthis(Window自体)でもなんでもいい。
            // Start時とマウス移動時の差分がわかりさえすればよし。
            MouseCurrentPoint = e.GetPosition(MyScrollViewer);

            // 移動開始点と現在位置の差から、MouseMoveイベント1回分の移動量を算出
            double offsetX = MouseCurrentPoint.X - MouseDonwStartPoint.X;
            double offsetY = MouseCurrentPoint.Y - MouseDonwStartPoint.Y;

            // 動かす対象の図形からMatrixオブジェクトを取得
            // このMatrixオブジェクトを用いて図形を描画上移動させる
            Matrix matrix = ((MatrixTransform)MyTarget.RenderTransform).Matrix;

            // TranslateメソッドにX方向とY方向の移動量を渡し、移動後の状態を計算
            matrix.Translate(offsetX, offsetY);

            // 移動後の状態を計算したMatrixオブジェクトを描画に反映する
            MyTarget.RenderTransform = new MatrixTransform(matrix);

            // 移動開始点を現在位置で更新する
            // (今回の現在位置が次回のMouseMoveイベントハンドラで使われる移動開始点となる)
            MouseDonwStartPoint = MouseCurrentPoint;
        }

        /// 【マウス】
        /// ホイールくるくる
        private void MyGrid_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            var scale = 1.0;
            Matrix matrix = ((MatrixTransform)MyTarget.RenderTransform).Matrix;

            // ScaleAt()の拡大中心点(引数3,4個目)に渡すための座標をとるときの基準Controlは、拡大縮小をしたいものの一つ上のControlにすること。
            // ここでは拡大縮小するGridを包んでいるScrollViewerを基準にした。
            MouseCurrentPoint = e.GetPosition(MyScrollViewer);

            // ホイール上に回す→拡大 / 下に回す→縮小
            if (e.Delta > 0) scale = 1.25;
            else scale = 1 / 1.25;

            Debug.WriteLine($"倍率:{scale} 中心点:{MouseCurrentPoint} 大きさ:({MyTarget.ActualWidth},{MyTarget.ActualHeight})");

            // 拡大実施
            matrix.ScaleAt(scale, scale, MouseCurrentPoint.X, MouseCurrentPoint.Y);
            MyTarget.RenderTransform = new MatrixTransform(matrix);
        }
    }
}

備考

上のサンプルコードには書いてないが、拡大縮小をした後の現在の拡大率がいくつか知りたい場合はmatrix.M11の値を見ればわかる。(githubのコード参照)

コード

参考

アフィン行列について
ここを見て、倍率がmatrix.M11から取れると知った
http://math-edu-lab.sakura.ne.jp/embed/afin.html

8
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?