3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

0. はじめに

Freeradicalの中の人、yamarahです。
普段は、Autodesk InventorのAddIn作成に関する記事を書いています。
WPFで画像をタッチ操作で画像を拡大(ピンチアウト)/縮小(ピンチイン)/移動(スワイプ)したいよね、というお話です。

1. コード

かなり試行錯誤しましたが、結論のコードだけを示します。
Borderで包んでEventはそちらで受けるのと、Border基準の位置をVisualTreeHelper.GetOffset(targetElement)を使って補正するのがミソです。

MainWindow.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Border Name="ImageBorder" ClipToBounds="True"
                IsManipulationEnabled="True"
                ManipulationDelta="ImageBorder_ManipulationDelta"
                MouseWheel="ImageBorder_MouseWheel"
                MouseLeftButtonDown="ImageBorder_MouseLeftButtonDown"
                MouseLeftButtonUp="ImageBorder_MouseLeftButtonUp"
                MouseMove="ImageBorder_MouseMove">
            <Image Name="SampleImage" Source="サンプル画像.JPG"/>
        </Border>
    </Grid>
</Window>

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

namespace WpfApp1;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    readonly double scaleMinimumLimit = 1;
    readonly double scaleMaximumLimit = 2;

    private void ImageBorder_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
    {
        Image target = SampleImage;

        var matrix = target.RenderTransform.Value;
        var delta = e.DeltaManipulation;
        var offsetFromParent = VisualTreeHelper.GetOffset(target);

        var offsetX = delta.Translation.X;
        var offsetY = delta.Translation.Y;
        matrix.Translate(offsetX, offsetY);

        var estimatedResultScale = Math.Clamp(delta.Scale.X * matrix.M22, scaleMinimumLimit, scaleMaximumLimit);
        var scale = estimatedResultScale / matrix.M22;
        var centerX = e.ManipulationOrigin.X - offsetFromParent.X;
        var centerY = e.ManipulationOrigin.Y - offsetFromParent.Y;
        matrix.ScaleAt(scale, scale, centerX, centerY);

        target.RenderTransform = new MatrixTransform(matrix);
    }


    // マウス操作も必要な場合は、以下をどうぞ。

    readonly double mouseWheelScaleFactor = 1.2;
    private Point latestMousePosition;

    private void ImageBorder_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        var target = SampleImage;
        var parent = (UIElement)target.Parent;

        var matrix = target.RenderTransform.Value;
        var offsetFromParent = VisualTreeHelper.GetOffset(target);

        var centerX = e.GetPosition(parent).X - offsetFromParent.X;
        var centerY = e.GetPosition(parent).Y - offsetFromParent.Y;

        var estimatedResultScale = Math.Clamp(
            (e.Delta > 0 ? mouseWheelScaleFactor : 1 / mouseWheelScaleFactor) * matrix.M22,
            scaleMinimumLimit,
            scaleMaximumLimit);
        var scale = estimatedResultScale / matrix.M22;
        matrix.ScaleAt(scale, scale, centerX, centerY);

        SampleImage.RenderTransform = new MatrixTransform(matrix);
    }

    private void ImageBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var border = (UIElement)sender;

        latestMousePosition = e.GetPosition(border);
        border.CaptureMouse();
    }

    private void ImageBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        var border = (UIElement)sender;

        border.ReleaseMouseCapture();
    }

    private void ImageBorder_MouseMove(object sender, MouseEventArgs e)
    {
        var target = SampleImage;
        var parent = (UIElement)target.Parent;

        if (parent.IsMouseCaptured)
        {
            var matrix = target.RenderTransform.Value;

            var currentMousePosition = e.GetPosition(parent);
            Vector offset = currentMousePosition - latestMousePosition;
            matrix.Translate(offset.X, offset.Y);
            SampleImage.RenderTransform = new MatrixTransform(matrix);
            latestMousePosition = currentMousePosition;
        }
    }
}

2. 参考文献

[xaml/WPF] フリックやピンチインアウトでControlを移動・拡大縮小する(ManipulationDeltaイベントとMatrixTransform)
https://qiita.com/tera1707/items/dff76c2b870fe5c63e9d

3
2
0

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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?