677 lines
20 KiB
C#
677 lines
20 KiB
C#
using System;
|
|
using System.Drawing.Imaging;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading.Tasks;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Shapes;
|
|
using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.Media;
|
|
using Avalonia.Media.Imaging;
|
|
using Avalonia.Platform;
|
|
using Avalonia.Threading;
|
|
using DispenserCommon.Aop;
|
|
using DispenserCommon.Utils;
|
|
using DispenserHal.Camera.Factory;
|
|
using DispenserUI.Utils;
|
|
using Serilog;
|
|
using Bitmap = System.Drawing.Bitmap;
|
|
using Brushes = Avalonia.Media.Brushes;
|
|
using Image = Avalonia.Controls.Image;
|
|
using PixelFormat = Avalonia.Platform.PixelFormat;
|
|
using Point = Avalonia.Point;
|
|
using Rectangle = System.Drawing.Rectangle;
|
|
|
|
namespace DispenserUI.Views.Controls;
|
|
|
|
#pragma warning disable CA1416
|
|
[GlobalTry]
|
|
public partial class ImageViewer : UserControl
|
|
{
|
|
private readonly SlidingWindow _slidingWindow = new(100);
|
|
|
|
public static readonly StyledProperty<Bitmap> SourceProperty =
|
|
AvaloniaProperty.Register<ImageViewer, Bitmap>(nameof(Source));
|
|
|
|
public static readonly StyledProperty<bool> ShowToolBarProperty =
|
|
AvaloniaProperty.Register<ImageViewer, bool>(nameof(ShowToolBar), true);
|
|
|
|
public static readonly StyledProperty<bool> ShowingCrosshairProperty =
|
|
AvaloniaProperty.Register<ImageViewer, bool>(nameof(ShowingCrosshair));
|
|
|
|
public static readonly StyledProperty<double> TuningStepProperty =
|
|
AvaloniaProperty.Register<ImageViewer, double>(nameof(TuningStep), 1.0);
|
|
|
|
public static readonly StyledProperty<bool> ScaleRelativeCenterProperty =
|
|
AvaloniaProperty.Register<ImageViewer, bool>(nameof(ScaleRelativeCenter));
|
|
|
|
|
|
public static readonly StyledProperty<double> ScaleRatioProperty =
|
|
AvaloniaProperty.Register<ImageViewer, double>(nameof(ScaleRatio), 1);
|
|
|
|
|
|
public static readonly StyledProperty<Stretch> StretchProperty =
|
|
AvaloniaProperty.Register<ImageViewer, Stretch>(nameof(Stretch));
|
|
|
|
public static readonly StyledProperty<object> SlotContentProperty =
|
|
AvaloniaProperty.Register<ImageViewer, object>(nameof(SlotContent));
|
|
|
|
|
|
private bool _isDragging;
|
|
|
|
private bool _isDraggingToolBar;
|
|
|
|
private bool _isPressingSpace;
|
|
|
|
private Point _origin = new(0, 0);
|
|
|
|
private Point _startPoint;
|
|
|
|
private Point _crosshairCenter;
|
|
|
|
private Point _crosshairCenterPoint;
|
|
|
|
private Point _crosshairPixelCenter;
|
|
|
|
private bool _isDraggingCanvas;
|
|
|
|
public event EventHandler<Point> PixelChanged;
|
|
|
|
public event EventHandler<Point> CenterPixelChanged;
|
|
public event EventHandler<double> ScaleRatioChanged;
|
|
|
|
private readonly string _id;
|
|
|
|
private readonly WriteableBitmap _image;
|
|
|
|
public ImageViewer()
|
|
{
|
|
Focusable = true;
|
|
_id = Guid.NewGuid().ToString();
|
|
|
|
_image = new WriteableBitmap(new PixelSize(4096, 3000), new Vector(96, 96),
|
|
PixelFormat.Bgra8888, AlphaFormat.Premul);
|
|
|
|
InitializeComponent();
|
|
Viewer = this.FindControl<Image>("Viewer");
|
|
ImageContainer = this.FindControl<Grid>("ImageContainer");
|
|
ToolBar = this.FindControl<ShadowBox>("ToolBar");
|
|
ImageCanvas = this.FindControl<Canvas>("ImageCanvas");
|
|
SlotViewer = this.FindControl<ContentControl>("SlotViewer");
|
|
PixelX = this.FindControl<TextBlock>("PixelX");
|
|
PixelY = this.FindControl<TextBlock>("PixelY");
|
|
ImageWidth = this.FindControl<TextBlock>("ImageWidth");
|
|
ImageHeight = this.FindControl<TextBlock>("ImageHeight");
|
|
ScaleRatioText = this.FindControl<TextBlock>("ScaleRatioText");
|
|
|
|
Viewer!.PointerWheelChanged += OnPointerWheelChanged;
|
|
Viewer.PointerPressed += OnPointerPressed;
|
|
Viewer.PointerReleased += OnPointerReleased;
|
|
Viewer.PointerMoved += OnPointerMoved;
|
|
Viewer.DoubleTapped += OnDoubleTapped;
|
|
|
|
|
|
ToolBar!.PointerPressed += OnToolBarPointerPressed;
|
|
ToolBar.PointerReleased += OnToolBarPointerReleased;
|
|
ToolBar.PointerMoved += OnToolBarPointerMoved;
|
|
|
|
ImageCanvas!.PointerWheelChanged += (sender, args) => { args.Handled = false; };
|
|
|
|
|
|
AddHandler(KeyDownEvent, OnKeyDownEvent, RoutingStrategies.Tunnel);
|
|
AddHandler(KeyUpEvent, OnKeyUpEvent, RoutingStrategies.Tunnel);
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
this.GetObservable(ShowToolBarProperty).Subscribe(val =>
|
|
{
|
|
if (!val)
|
|
{
|
|
ToolBar!.IsVisible = false;
|
|
}
|
|
});
|
|
|
|
this.GetObservable(ShowingCrosshairProperty).Subscribe(ToggleCrosshair);
|
|
|
|
this.GetObservable(SourceProperty).Subscribe(source =>
|
|
{
|
|
if (source != null)
|
|
{
|
|
// 处理源图片变化
|
|
HandleSourceChanged(source).ConfigureAwait(false);
|
|
Viewer.Source = _image;
|
|
}
|
|
else
|
|
{
|
|
// 设置默认值
|
|
var blank = ImageHelper.LoadBitmap("Assets/UI/blank.png");
|
|
Viewer.Source = blank;
|
|
}
|
|
});
|
|
#pragma warning restore CA1416
|
|
|
|
this.GetObservable(ScaleRatioProperty)
|
|
.Subscribe(val => { ScaleRatioText!.Text = val.ToString("0.00"); });
|
|
|
|
InvalidateVisual();
|
|
},
|
|
DispatcherPriority.Loaded);
|
|
}
|
|
|
|
|
|
public Bitmap Source
|
|
{
|
|
get => GetValue(SourceProperty);
|
|
set => SetValue(SourceProperty, value);
|
|
}
|
|
|
|
public bool ShowToolBar
|
|
{
|
|
get => GetValue(ShowToolBarProperty);
|
|
set => SetValue(ShowToolBarProperty, value);
|
|
}
|
|
|
|
public bool ShowingCrosshair
|
|
{
|
|
get => GetValue(ShowingCrosshairProperty);
|
|
set => SetValue(ShowingCrosshairProperty, value);
|
|
}
|
|
|
|
public double TuningStep
|
|
{
|
|
get => GetValue(TuningStepProperty);
|
|
set => SetValue(TuningStepProperty, value);
|
|
}
|
|
|
|
public object SlotContent
|
|
{
|
|
get => GetValue(SlotContentProperty);
|
|
set => SetValue(SlotContentProperty, value);
|
|
}
|
|
|
|
public bool ScaleRelativeCenter
|
|
{
|
|
get => GetValue(ScaleRelativeCenterProperty);
|
|
set => SetValue(ScaleRelativeCenterProperty, value);
|
|
}
|
|
|
|
public double ScaleRatio
|
|
{
|
|
get => GetValue(ScaleRatioProperty);
|
|
set => SetValue(ScaleRatioProperty, value);
|
|
}
|
|
|
|
public Stretch Stretch
|
|
{
|
|
get => GetValue(StretchProperty);
|
|
set => SetValue(StretchProperty, value);
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
}
|
|
|
|
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
|
|
private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
|
|
|
|
/// <summary>
|
|
/// 处理源图片变化事件
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
private async Task HandleSourceChanged(Bitmap source)
|
|
{
|
|
var width = source.Width;
|
|
var height = source.Height;
|
|
|
|
await Task.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
var bitmapData = source.LockBits(new Rectangle(0, 0, width, height),
|
|
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
|
|
|
using (var frameBuffer = _image.Lock())
|
|
{
|
|
var sourcePtr = bitmapData.Scan0;
|
|
var destPtr = frameBuffer.Address;
|
|
var count = (uint)(bitmapData.Stride * source.Height);
|
|
CopyMemory(destPtr, sourcePtr, count);
|
|
}
|
|
|
|
source.UnlockBits(bitmapData);
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
ImageWidth.Text = width.ToString();
|
|
ImageHeight.Text = height.ToString();
|
|
Viewer.InvalidateVisual();
|
|
});
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e, "图像转换异常");
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
private void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
|
|
{
|
|
// 根据鼠标滚轮的Delta值来计算缩放比例的变化
|
|
var point = e.GetPosition(Viewer);
|
|
|
|
Scaling(point, e.Delta.Y > 0);
|
|
}
|
|
|
|
private void OnKeyUpEvent(object? sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Space)
|
|
{
|
|
_isPressingSpace = false;
|
|
}
|
|
|
|
// 将光标改为拖动光标
|
|
Viewer.Cursor = new Cursor(StandardCursorType.Hand);
|
|
}
|
|
|
|
|
|
private void OnKeyDownEvent(object? sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Space)
|
|
{
|
|
_isPressingSpace = true;
|
|
// 将光标改为拖动光标
|
|
Viewer.Cursor = new Cursor(StandardCursorType.DragMove);
|
|
}
|
|
else if (e.Key == Key.LeftAlt)
|
|
{
|
|
// 将光标改为十字光标
|
|
Viewer.Cursor = new Cursor(StandardCursorType.Cross);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 去缩放图片
|
|
/// </summary>
|
|
/// <param name="center"></param>
|
|
/// <param name="enlarge"></param>
|
|
/// <returns></returns>
|
|
private void Scaling(Point center, bool enlarge)
|
|
{
|
|
if (enlarge)
|
|
{
|
|
ScaleRatio *= 1.1;
|
|
}
|
|
else
|
|
{
|
|
ScaleRatio /= 1.1;
|
|
}
|
|
|
|
ScaleRatio = Math.Clamp(ScaleRatio, 0.5, 100);
|
|
|
|
if (ScaleRelativeCenter)
|
|
{
|
|
center = new Point(Viewer.Bounds.Width / 2, Viewer.Bounds.Height / 2);
|
|
}
|
|
|
|
if (ShowingCrosshair)
|
|
{
|
|
center = _crosshairCenter;
|
|
}
|
|
|
|
Viewer.RenderTransformOrigin = new RelativePoint(center, RelativeUnit.Absolute);
|
|
// 设置新的缩放比例
|
|
Viewer.RenderTransform = new ScaleTransform(ScaleRatio, ScaleRatio);
|
|
|
|
SlotViewer.RenderTransformOrigin = new RelativePoint(center, RelativeUnit.Absolute);
|
|
SlotViewer.RenderTransform = new ScaleTransform(ScaleRatio, ScaleRatio);
|
|
|
|
ScaleRatioChanged?.Invoke(this, ScaleRatio);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 鼠标按下事件
|
|
/// </summary>
|
|
private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
{
|
|
var properties = e.GetCurrentPoint(this).Properties;
|
|
if (properties.IsLeftButtonPressed)
|
|
{
|
|
_isDragging = true;
|
|
// 计算拖动起始点
|
|
_startPoint = e.GetPosition(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 鼠标释放事件
|
|
/// </summary>
|
|
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
|
{
|
|
_isDragging = false;
|
|
Viewer.Cursor = new Cursor(StandardCursorType.Hand);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 鼠标移动事件
|
|
/// </summary>
|
|
private void OnPointerMoved(object? sender, PointerEventArgs e)
|
|
{
|
|
GetRealPixelCoordinate(sender as Image, e.GetPosition(Viewer));
|
|
|
|
if (!_isDragging || !_slidingWindow.AllowValue(_id) || !_isPressingSpace) return;
|
|
|
|
var currentPoint = e.GetPosition(this);
|
|
// 计算两个点之间的差值
|
|
var deltaX = currentPoint.X - _startPoint.X;
|
|
var deltaY = currentPoint.Y - _startPoint.Y;
|
|
|
|
_origin = new Point(_origin.X + deltaX, _origin.Y + deltaY);
|
|
|
|
// 这里的移动需要包含了保持原有的缩放比例,然后再进行移动,否则移动之后图像会进行缩放
|
|
var transformGroup = new TransformGroup();
|
|
transformGroup.Children.Add(new ScaleTransform(ScaleRatio, ScaleRatio));
|
|
transformGroup.Children.Add(new TranslateTransform(_origin.X, _origin.Y));
|
|
|
|
Viewer.RenderTransform = transformGroup;
|
|
SlotViewer.RenderTransform = transformGroup;
|
|
ImageCanvas.RenderTransform = new TranslateTransform(_origin.X, _origin.Y);
|
|
|
|
_startPoint = currentPoint;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通过双击图片是还原图像大小和位置
|
|
/// </summary>
|
|
private void OnDoubleTapped(object? sender, TappedEventArgs e)
|
|
{
|
|
ResetSize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置大小
|
|
/// </summary>
|
|
public void ResetSize()
|
|
{
|
|
ScaleRatioChanged?.Invoke(this, 1);
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
Viewer.Stretch = Stretch.Uniform;
|
|
ScaleRatio = 1;
|
|
Viewer.RenderTransform = new ScaleTransform(ScaleRatio, ScaleRatio);
|
|
SlotViewer.RenderTransform = new ScaleTransform(ScaleRatio, ScaleRatio);
|
|
|
|
|
|
if (!ShowingCrosshair) return;
|
|
|
|
ImageCanvas.RenderTransform = new TranslateTransform(0, 0);
|
|
_crosshairCenter = new Point(ImageCanvas.Bounds.Width / 2, ImageCanvas.Bounds.Height / 2);
|
|
_crosshairPixelCenter = new Point(Viewer.Bounds.Width / 2, Viewer.Bounds.Height / 2);
|
|
|
|
// 渲染十字线
|
|
RenderCrosshair();
|
|
});
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 图像缩小
|
|
/// </summary>
|
|
public void ImageShrink()
|
|
{
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
var center = new Point(Viewer.Bounds.Width / 2, Viewer.Bounds.Height / 2);
|
|
Scaling(center, false);
|
|
});
|
|
}
|
|
|
|
public Control GetViewer()
|
|
{
|
|
return Viewer;
|
|
}
|
|
|
|
public Control GetContainer()
|
|
{
|
|
return ImageContainer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 图像放大
|
|
/// </summary>
|
|
public void ImageEnlarge()
|
|
{
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
var center = new Point(Viewer.Bounds.Width / 2, Viewer.Bounds.Height / 2);
|
|
Scaling(center, true);
|
|
});
|
|
}
|
|
|
|
private void OnShowCrosshairChanged(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is ToggleSwitch ts)
|
|
{
|
|
var @checked = ts.IsChecked.GetValueOrDefault();
|
|
ShowingCrosshair = @checked;
|
|
ToggleCrosshair(@checked);
|
|
}
|
|
}
|
|
|
|
|
|
private void OnScaleRelativeCenterChanged(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is ToggleSwitch ts)
|
|
{
|
|
var @checked = ts.IsChecked.GetValueOrDefault();
|
|
ScaleRelativeCenter = @checked;
|
|
}
|
|
}
|
|
|
|
private void ToggleCrosshair(bool show)
|
|
{
|
|
if (show)
|
|
{
|
|
ImageCanvas.PointerPressed += OnCrosshairPointerPressed;
|
|
ImageCanvas.PointerReleased += OnCrosshairPointerReleased;
|
|
ImageCanvas.PointerMoved += OnCrosshairPointerMoved;
|
|
|
|
_crosshairCenter = new Point(Viewer.Bounds.Width / 2, Viewer.Bounds.Height / 2);
|
|
_crosshairPixelCenter = new Point(Viewer.Bounds.Width / 2, Viewer.Bounds.Height / 2);
|
|
_crosshairCenterPoint = _crosshairCenter;
|
|
|
|
// 渲染十字线
|
|
RenderCrosshair();
|
|
}
|
|
else
|
|
{
|
|
ImageCanvas.Children.Clear();
|
|
ImageCanvas.PointerPressed -= OnCrosshairPointerPressed;
|
|
ImageCanvas.PointerReleased -= OnCrosshairPointerReleased;
|
|
ImageCanvas.PointerMoved -= OnCrosshairPointerMoved;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 步长变更
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void OnStepChanged(object? sender, NumericUpDownValueChangedEventArgs e)
|
|
{
|
|
if (sender is NumericUpDown step)
|
|
{
|
|
TuningStep = (double)step.Value.GetValueOrDefault();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 拖拽十字线
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void OnCrosshairPointerMoved(object? sender, PointerEventArgs e)
|
|
{
|
|
if (_isDraggingCanvas)
|
|
{
|
|
_crosshairCenter = e.GetPosition(ImageCanvas);
|
|
|
|
_crosshairCenterPoint = e.GetPosition(Viewer);
|
|
|
|
// 移动十字线的时候触发事件
|
|
CenterPixelChanged?.Invoke(this, _crosshairCenterPoint);
|
|
GetRealPixelCoordinate(Viewer, _crosshairCenterPoint);
|
|
RenderCrosshair();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 拖拽十字线事件
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void OnCrosshairPointerReleased(object? sender, PointerReleasedEventArgs e)
|
|
{
|
|
ImageCanvas.Cursor = new Cursor(StandardCursorType.Arrow);
|
|
_isDraggingCanvas = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 拖拽十字线事件
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void OnCrosshairPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
{
|
|
var properties = e.GetCurrentPoint(ImageCanvas).Properties;
|
|
if (properties.IsLeftButtonPressed)
|
|
{
|
|
ImageCanvas.Cursor = new Cursor(StandardCursorType.Hand);
|
|
_isDraggingCanvas = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 渲染十字线
|
|
/// </summary>
|
|
private void RenderCrosshair()
|
|
{
|
|
ImageCanvas.Children.Clear();
|
|
var width = 4096;
|
|
var height = 3000;
|
|
|
|
var centerX = _crosshairCenter.X;
|
|
var centerY = _crosshairCenter.Y;
|
|
|
|
var line1 = new Line
|
|
{
|
|
StartPoint = new Point(0, centerY),
|
|
EndPoint = new Point(width, centerY),
|
|
Stroke = Brushes.Orange,
|
|
StrokeThickness = 5
|
|
};
|
|
|
|
|
|
var line2 = new Line
|
|
{
|
|
StartPoint = new Point(centerX, 0),
|
|
EndPoint = new Point(centerX, height),
|
|
Stroke = Brushes.Orange,
|
|
StrokeThickness = 5
|
|
};
|
|
|
|
var pixelText = new TextBlock
|
|
{
|
|
Text = $"({_crosshairPixelCenter.X:0.###},{_crosshairPixelCenter.Y:0.###})",
|
|
Foreground = Brushes.Orange,
|
|
FontSize = 60,
|
|
Margin = new Thickness(centerX + 40, centerY + 40, 0, 0)
|
|
};
|
|
|
|
// 中间画一个空白的圆,用来实现扩大拖拽鼠标点击区域
|
|
var circle = new Ellipse
|
|
{
|
|
Width = 100,
|
|
Height = 100,
|
|
Fill = Brushes.Transparent,
|
|
Stroke = Brushes.Transparent,
|
|
StrokeThickness = 0,
|
|
Cursor = new Cursor(StandardCursorType.Hand),
|
|
Margin = new Thickness(centerX - 50, centerY - 50, 0, 0)
|
|
};
|
|
|
|
ImageCanvas.Children.Add(circle);
|
|
ImageCanvas.Children.Add(line1);
|
|
ImageCanvas.Children.Add(line2);
|
|
ImageCanvas.Children.Add(pixelText);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取图像的真实像素坐标
|
|
/// </summary>
|
|
/// <param name="image"></param>
|
|
/// <param name="point"></param>
|
|
private void GetRealPixelCoordinate(Image? image, Point point)
|
|
{
|
|
if (image!.Source is not Avalonia.Media.Imaging.Bitmap) return;
|
|
|
|
PixelX.Text = point.X.ToString("F0");
|
|
PixelY.Text = point.Y.ToString("F0");
|
|
|
|
_crosshairPixelCenter = point;
|
|
|
|
PixelChanged?.Invoke(this, point);
|
|
}
|
|
|
|
|
|
private void OnToolBarPointerMoved(object? sender, PointerEventArgs e)
|
|
{
|
|
if (_isDraggingToolBar)
|
|
{
|
|
var currentPoint = e.GetPosition(this);
|
|
// 计算两个点之间的差值
|
|
var deltaX = currentPoint.X - _startPoint.X;
|
|
var deltaY = currentPoint.Y - _startPoint.Y;
|
|
|
|
_origin = new Point(_origin.X + deltaX, _origin.Y + deltaY);
|
|
|
|
ToolBar.RenderTransform = new TranslateTransform(_origin.X, _origin.Y);
|
|
_startPoint = currentPoint;
|
|
}
|
|
}
|
|
|
|
private void OnToolBarPointerReleased(object? sender, PointerReleasedEventArgs e)
|
|
{
|
|
ToolBar.Cursor = new Cursor(StandardCursorType.Arrow);
|
|
_isDraggingToolBar = false;
|
|
}
|
|
|
|
private void OnToolBarPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
{
|
|
var properties = e.GetCurrentPoint(this).Properties;
|
|
if (properties.IsLeftButtonPressed)
|
|
{
|
|
ToolBar.Cursor = new Cursor(StandardCursorType.Hand);
|
|
_isDraggingToolBar = true;
|
|
// 计算拖动起始点
|
|
_startPoint = e.GetPosition(this);
|
|
}
|
|
}
|
|
|
|
|
|
private void TriggerTakePhoto()
|
|
{
|
|
var camera = CameraManager.GetCamera()!;
|
|
|
|
camera.TokePhoto();
|
|
|
|
if (camera.CapturingVideo)
|
|
{
|
|
camera.StartCaptureVideo();
|
|
}
|
|
}
|
|
} |