SelectionCropper.cs
// 矩形選択
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Maywork.WPF.Helpers;
public static class SelectionCropper
{
#region Enable
public static readonly DependencyProperty EnableProperty =
DependencyProperty.RegisterAttached(
"Enable",
typeof(bool),
typeof(SelectionCropper),
new PropertyMetadata(false, OnEnableChanged));
public static void SetEnable(DependencyObject obj, bool value)
=> obj.SetValue(EnableProperty, value);
public static bool GetEnable(DependencyObject obj)
=> (bool)obj.GetValue(EnableProperty);
#endregion
#region CropCommand
public static readonly DependencyProperty CropCommandProperty =
DependencyProperty.RegisterAttached(
"CropCommand",
typeof(ICommand),
typeof(SelectionCropper),
new PropertyMetadata(null));
public static void SetCropCommand(DependencyObject obj, ICommand value)
=> obj.SetValue(CropCommandProperty, value);
public static ICommand? GetCropCommand(DependencyObject obj)
=> (ICommand?)obj.GetValue(CropCommandProperty);
#endregion
private static System.Windows.Shapes.Rectangle? _selectionRect;
private static Point _start;
private static bool _dragging;
private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not ScrollViewer sv)
return;
if ((bool)e.NewValue)
{
sv.Loaded += OnLoaded;
}
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
if (sender is not ScrollViewer sv)
return;
var canvas = FindChild<Canvas>(sv);
if (canvas == null)
return;
_selectionRect = new System.Windows.Shapes.Rectangle
{
Stroke = Brushes.Red,
StrokeThickness = 1,
Fill = new SolidColorBrush(Color.FromArgb(80, 255, 0, 0)),
Visibility = Visibility.Collapsed
};
canvas.Children.Add(_selectionRect);
canvas.MouseLeftButtonDown += (s, ev) =>
{
_start = ev.GetPosition(canvas);
_dragging = true;
Canvas.SetLeft(_selectionRect, _start.X);
Canvas.SetTop(_selectionRect, _start.Y);
_selectionRect.Width = 0;
_selectionRect.Height = 0;
_selectionRect.Visibility = Visibility.Visible;
canvas.CaptureMouse();
};
canvas.MouseMove += (s, ev) =>
{
if (!_dragging) return;
var pos = ev.GetPosition(canvas);
double x = Math.Min(pos.X, _start.X);
double y = Math.Min(pos.Y, _start.Y);
double w = Math.Abs(pos.X - _start.X);
double h = Math.Abs(pos.Y - _start.Y);
Canvas.SetLeft(_selectionRect, x);
Canvas.SetTop(_selectionRect, y);
_selectionRect.Width = w;
_selectionRect.Height = h;
};
canvas.MouseLeftButtonUp += (s, ev) =>
{
if (!_dragging) return;
_dragging = false;
canvas.ReleaseMouseCapture();
ExecuteCrop(sv, canvas);
};
}
public readonly record struct ImageSelectionRect(
int X,
int Y,
int Width,
int Height);
private static void ExecuteCrop(ScrollViewer sv, Canvas canvas)
{
if (_selectionRect == null)
return;
var image = FindChild<Image>(canvas);
if (image?.Source is not BitmapSource bmp)
return;
// TransformToVisualで座標変換だけする
GeneralTransform t = canvas.TransformToVisual(image);
double cx = Canvas.GetLeft(_selectionRect);
double cy = Canvas.GetTop(_selectionRect);
var p0 = t.Transform(new Point(cx, cy));
var p1 = t.Transform(new Point(cx + _selectionRect.Width,
cy + _selectionRect.Height));
int x = (int)Math.Round(Math.Min(p0.X, p1.X));
int y = (int)Math.Round(Math.Min(p0.Y, p1.Y));
int w = (int)Math.Round(Math.Abs(p1.X - p0.X));
int h = (int)Math.Round(Math.Abs(p1.Y - p0.Y));
if (w <= 0 || h <= 0)
return;
var selection = new ImageSelectionRect(x, y, w, h);
var cmd = GetCropCommand(sv);
if (cmd?.CanExecute(selection) == true)
cmd.Execute(selection);
_selectionRect.Visibility = Visibility.Collapsed;
}
private static T? FindChild<T>(DependencyObject parent) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t)
return t;
var result = FindChild<T>(child);
if (result != null)
return result;
}
return null;
}
}
/*
// 使い方
XAML
xmlns:h="clr-namespace:Maywork.WPF.Helpers"
<Grid>
<ScrollViewer
h:SelectionCropper.Enable="True"
h:SelectionCropper.CropCommand="{Binding CropCommand}" />
<Canvas>
<Image Source="{Binding CurrentImage}"/>
</Canvas>
</ScrollViewer>
</Grid>
public RelayCommand<ImageSelectionRect> CropCommand { get; }
ViewModel
public MainWindowViewModel()
{
CropCommand = new RelayCommand<ImageSelectionRect>(OnCrop);
}
private void OnCrop(ImageSelectionRect rect)
{
if (CurrentImage is not BitmapSource bmp)
return;
// rectで座標系の情報が渡されるので
// ここでクロップなどの処理を行う
Debug.Print($"X:{rect.X} Y:{rect.Y} W:{rect.Width} H:{rect.Height}");
}
*/
Download

コメント