もくじ
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f
回転/拡大縮小/移動の関連記事
- 画面の要素を回転/拡大縮小/移動する(RenderTransform)
- 画面の要素を回転/拡大縮小/移動する(RenderTransformにMatrixTransform)
- フリックやピンチインアウトでControlを移動・拡大縮小する(ManipulationDeltaイベントとMatrixTransform)
- [xaml/C#] 枠の中で画像などを拡大縮小する(マウスと指で移動/拡大)
やること
ウインドウの中の、ある枠のなかで、写真などを拡大縮小したい。
窓の外のモノを動かしているイメージ。
窓枠の中ではそのコントロール(例ではTextBlock)が見えてるが、枠の外側にドラッグやフリックして移動させると、枠の外は見えなくなる感じにしたい。
やりかた
以前の記事(上の「回転/拡大縮小/移動の関連記事」のところを参照)でやった、コントロールの移動、拡大縮小を、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