Files
community/Ink Canvas/Windows/ScreenshotSelectorWindow.xaml.cs
T
tayasui rainnya! 1c8bdcf352 fix: 全选后点击添加到白板显示已取消 (#379)
* 调整截图按钮默认行为为选区/白板全屏

* 截图选择器新增添加到白板按钮并支持新建白板页插入

* 修复添加到白板不生效:改为剪贴板复制粘贴插入

* 修复添加到白板截图时序:先截到内存再进白板

* Fix screenshot insert flow honoring add-to-whiteboard option

* Refine screenshot selector freehand confirm and blank double-click full select

* Fix freehand whiteboard insert alpha transparency

* Fix screenshot insert notification text for whiteboard flow

* Adjust screenshot selection: right-click/double-click full-select and keep selector on single click

* Show unmasked freehand selection area like rectangle mode

* Remove freehand cutout mask rendering from screenshot selector

* Restore freehand selected-area unmask behavior in selector

* fix: always restore window visibility after area screenshot

* fix: allow add-to-whiteboard after full-screen selection
2026-02-22 14:14:10 +08:00

1506 lines
55 KiB
C#

using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using Brushes = System.Windows.Media.Brushes;
using Color = System.Windows.Media.Color;
using DrawingRectangle = System.Drawing.Rectangle;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using WpfCanvas = System.Windows.Controls.Canvas;
using WpfPoint = System.Windows.Point;
namespace Ink_Canvas
{
public partial class ScreenshotSelectorWindow : Window
{
private bool _isSelecting;
private bool _isFreehandMode;
private bool _isAdjusting;
private bool _isMoving;
private bool _isCameraMode;
private WpfPoint _startPoint;
private WpfPoint _currentPoint;
private WpfPoint _lastMousePosition;
private List<WpfPoint> _freehandPoints;
private Polyline _freehandPolyline;
private Rect _currentSelection;
private ControlPointType _activeControlPoint = ControlPointType.None;
private CameraService _cameraService;
private Bitmap _capturedCameraImage = null;
private DateTime _lastBlankClickTime = DateTime.MinValue;
private WpfPoint _lastBlankClickPosition;
private const int DoubleClickTimeThresholdMs = 300; // 双击判定时间阈值(常见范围 200~500ms)
private const double DoubleClickDistanceThresholdPx = 12; // 双击判定位置阈值(像素)
// 控制点类型枚举
private enum ControlPointType
{
None,
TopLeft, TopRight, BottomLeft, BottomRight,
Top, Bottom, Left, Right,
Move
}
public DrawingRectangle? SelectedArea { get; private set; }
public List<WpfPoint> SelectedPath { get; private set; }
public Bitmap CameraImage { get; private set; }
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource { get; private set; }
public bool ShouldAddToWhiteboard { get; private set; }
public ScreenshotSelectorWindow()
{
InitializeComponent();
// 设置窗口覆盖所有屏幕
SetupFullScreenOverlay();
// 初始化自由绘制模式
InitializeFreehandMode();
// 绑定控制点事件
BindControlPointEvents();
// 初始化按钮状态
InitializeButtonStates();
// 初始化摄像头服务
InitializeCameraService();
// 隐藏提示文字的定时器
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(5);
timer.Tick += (s, e) =>
{
HintTextBorder.Visibility = Visibility.Collapsed;
timer.Stop();
};
timer.Start();
}
private void InitializeFreehandMode()
{
_freehandPoints = new List<WpfPoint>();
_freehandPolyline = new Polyline
{
Stroke = Brushes.White,
StrokeThickness = 1,
Fill = Brushes.Transparent
};
SelectionCanvas.Children.Add(_freehandPolyline);
}
private void InitializeButtonStates()
{
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
}
private void InitializeCameraService()
{
try
{
// 从设置中加载摄像头配置
var cameraSettings = MainWindow.Settings.Camera;
_cameraService = new CameraService(
cameraSettings.RotationAngle,
cameraSettings.ResolutionWidth,
cameraSettings.ResolutionHeight);
_cameraService.FrameReceived += CameraService_FrameReceived;
_cameraService.ErrorOccurred += CameraService_ErrorOccurred;
// 初始化摄像头选择下拉框
RefreshCameraComboBox();
// 初始化旋转和分辨率显示
InitializeCameraControls();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化摄像头服务失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void InitializeCameraControls()
{
if (_cameraService != null)
{
// 更新旋转角度显示
UpdateRotationDisplay();
// 设置分辨率下拉框
var currentResolution = $"{_cameraService.ResolutionWidth}x{_cameraService.ResolutionHeight}";
foreach (ComboBoxItem item in ResolutionComboBox.Items)
{
if (item.Tag?.ToString() == $"{_cameraService.ResolutionWidth},{_cameraService.ResolutionHeight}")
{
ResolutionComboBox.SelectedItem = item;
break;
}
}
}
}
private void RefreshCameraComboBox()
{
try
{
CameraSelectionComboBox.Items.Clear();
if (_cameraService.HasAvailableCameras())
{
var cameraNames = _cameraService.GetCameraNames();
foreach (var name in cameraNames)
{
CameraSelectionComboBox.Items.Add(name);
}
if (cameraNames.Count > 0)
{
CameraSelectionComboBox.SelectedIndex = 0;
}
}
else
{
CameraSelectionComboBox.Items.Add("未找到摄像头设备");
CameraSelectionComboBox.SelectedIndex = 0;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新摄像头列表失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void CameraService_FrameReceived(object sender, Bitmap frame)
{
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
if (_isCameraMode && CameraPreviewImage != null && frame != null)
{
try
{
// 验证帧的有效性
if (frame.Width <= 0 || frame.Height <= 0)
return;
// 创建新的位图,避免Clone的问题
var clonedFrame = new Bitmap(frame.Width, frame.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(clonedFrame))
{
graphics.DrawImage(frame, 0, 0);
}
var bitmapSource = ConvertBitmapToBitmapSource(clonedFrame);
if (bitmapSource != null)
{
CameraPreviewImage.Source = bitmapSource;
CameraStatusText.Text = "摄像头已连接";
}
// 释放临时位图
clonedFrame.Dispose();
}
catch (Exception frameEx)
{
LogHelper.WriteLogToFile($"处理摄像头帧时出错: {frameEx.Message}", LogHelper.LogType.Error);
}
}
}));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理摄像头帧失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void CameraService_ErrorOccurred(object sender, string error)
{
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
CameraStatusText.Text = $"摄像头错误: {error}";
}));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理摄像头错误失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private System.Windows.Media.Imaging.BitmapSource ConvertBitmapToBitmapSource(Bitmap bitmap)
{
try
{
// 验证位图有效性
if (bitmap == null || bitmap.Width <= 0 || bitmap.Height <= 0)
return null;
// 使用更安全的方法转换位图
var bitmapData = bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
bitmap.PixelFormat);
try
{
// 根据像素格式选择合适的WPF像素格式
PixelFormat wpfPixelFormat;
switch (bitmap.PixelFormat)
{
case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
wpfPixelFormat = PixelFormats.Bgr24;
break;
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
wpfPixelFormat = PixelFormats.Bgra32;
break;
case System.Drawing.Imaging.PixelFormat.Format32bppRgb:
wpfPixelFormat = PixelFormats.Bgr32;
break;
default:
wpfPixelFormat = PixelFormats.Bgr24;
break;
}
var bitmapSource = System.Windows.Media.Imaging.BitmapSource.Create(
bitmapData.Width,
bitmapData.Height,
bitmap.HorizontalResolution,
bitmap.VerticalResolution,
wpfPixelFormat,
null,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride);
bitmapSource.Freeze();
return bitmapSource;
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"转换位图失败: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
private void BindControlPointEvents()
{
// 绑定所有控制点的鼠标事件
var controlPoints = new[]
{
TopLeftControl, TopRightControl, BottomLeftControl, BottomRightControl,
TopControl, BottomControl, LeftControl, RightControl
};
foreach (var control in controlPoints)
{
control.MouseLeftButtonDown += ControlPoint_MouseLeftButtonDown;
control.MouseLeftButtonUp += ControlPoint_MouseLeftButtonUp;
control.MouseMove += ControlPoint_MouseMove;
// 确保控制点能够接收鼠标事件
control.IsHitTestVisible = true;
control.Focusable = false;
// 设置控制点的Z-index,确保它们在最上层
WpfCanvas.SetZIndex(control, 1003);
}
}
private void SetupFullScreenOverlay()
{
// 获取所有屏幕的虚拟屏幕边界
var virtualScreen = SystemInformation.VirtualScreen;
// 转换为WPF坐标系统
var dpiScale = GetDpiScale();
Left = virtualScreen.Left / dpiScale;
Top = virtualScreen.Top / dpiScale;
Width = virtualScreen.Width / dpiScale;
Height = virtualScreen.Height / dpiScale;
}
private double GetDpiScale()
{
var source = PresentationSource.FromVisual(this);
if (source?.CompositionTarget != null)
{
return source.CompositionTarget.TransformToDevice.M11;
}
return 1.0; // 默认DPI
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
CancelSelection();
}
else if (e.Key == Key.Enter)
{
ConfirmSelection();
}
}
private void RectangleModeButton_Click(object sender, RoutedEventArgs e)
{
// 重置所有选择状态
ResetSelectionState();
_isFreehandMode = false;
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
HintText.Text = "拖拽鼠标选择矩形区域";
HintTextBorder.Visibility = Visibility.Visible;
}
private void FreehandModeButton_Click(object sender, RoutedEventArgs e)
{
// 重置所有选择状态
ResetSelectionState();
_isFreehandMode = true;
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
HintText.Text = "按住鼠标左键自由绘制,松开后可继续调整或重新绘制,确认后再截图";
HintTextBorder.Visibility = Visibility.Visible;
}
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
// 重置所有选择状态
ResetSelectionState();
// 设置全屏截图模式
_isFreehandMode = false;
_isCameraMode = false;
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
// 隐藏摄像头预览
CameraPreviewBorder.Visibility = Visibility.Collapsed;
// 直接执行全屏截图
PerformFullScreenCapture();
}
private void CameraModeButton_Click(object sender, RoutedEventArgs e)
{
try
{
// 重置所有选择状态
ResetSelectionState();
// 设置摄像头模式
_isFreehandMode = false;
_isCameraMode = true;
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
// 显示摄像头预览
CameraPreviewBorder.Visibility = Visibility.Visible;
HintText.Text = "摄像头预览模式,点击确认截图按钮进行截图";
HintTextBorder.Visibility = Visibility.Visible;
// 启动摄像头预览
if (_cameraService != null && _cameraService.HasAvailableCameras())
{
var selectedIndex = CameraSelectionComboBox.SelectedIndex;
if (selectedIndex >= 0 && selectedIndex < _cameraService.GetCameraNames().Count)
{
_cameraService.StartPreview(selectedIndex);
}
}
else
{
CameraStatusText.Text = "未找到摄像头设备";
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动摄像头模式失败: {ex.Message}", LogHelper.LogType.Error);
CameraStatusText.Text = $"启动摄像头失败: {ex.Message}";
}
}
private void CameraSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
if (_isCameraMode && _cameraService != null)
{
var selectedIndex = CameraSelectionComboBox.SelectedIndex;
if (selectedIndex >= 0 && selectedIndex < _cameraService.GetCameraNames().Count)
{
_cameraService.SwitchCamera(selectedIndex);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换摄像头失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void SwitchCameraButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (_cameraService != null && _cameraService.HasAvailableCameras())
{
var cameraNames = _cameraService.GetCameraNames();
if (cameraNames.Count > 1)
{
var currentIndex = CameraSelectionComboBox.SelectedIndex;
var nextIndex = (currentIndex + 1) % cameraNames.Count;
CameraSelectionComboBox.SelectedIndex = nextIndex;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换摄像头失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
ShouldAddToWhiteboard = false;
// 在自由绘制模式下,按当前自由选区执行确认
if (_isFreehandMode)
{
ConfirmFreehandSelection();
return;
}
// 在摄像头模式下,执行摄像头截图
if (_isCameraMode)
{
ConfirmCameraCapture();
return;
}
ConfirmSelection();
}
private void AddToWhiteboardButton_Click(object sender, RoutedEventArgs e)
{
ShouldAddToWhiteboard = true;
// 在自由绘制模式下,按当前自由选区执行确认
if (_isFreehandMode)
{
ConfirmFreehandSelection();
return;
}
if (_isCameraMode)
{
ConfirmCameraCapture();
return;
}
ConfirmSelection();
}
private void ConfirmCameraCapture()
{
try
{
if (_cameraService != null && _cameraService.IsCapturing)
{
// 直接获取BitmapSource,避免Bitmap传递问题
var bitmapSource = _cameraService.GetCurrentFrameAsBitmapSource();
if (bitmapSource != null)
{
// 保存BitmapSource而不是Bitmap
CameraBitmapSource = bitmapSource;
// 停止摄像头预览
_cameraService.StopPreview();
// 设置结果并关闭窗口
DialogResult = true;
Close();
}
else
{
CameraStatusText.Text = "无法获取摄像头画面";
}
}
else
{
CameraStatusText.Text = "摄像头未启动";
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"摄像头截图失败: {ex.Message}", LogHelper.LogType.Error);
CameraStatusText.Text = $"摄像头截图失败: {ex.Message}";
}
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
CancelSelection();
}
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 检查是否点击了UI元素,如果是则不处理选择
var hitElement = e.Source as FrameworkElement;
if (hitElement != null && (
hitElement is Ellipse ||
hitElement is System.Windows.Controls.Button ||
hitElement is Border ||
hitElement is TextBlock ||
hitElement is StackPanel ||
hitElement is Separator ||
hitElement.Name == "SizeInfoBorder" ||
hitElement.Name == "HintText" ||
hitElement.Name == "AdjustModeHint" ||
hitElement.Name == "SelectionRectangle"))
{
return;
}
// 如果正在调整,忽略新的选择
if (_isAdjusting) return;
// 空白区域双击可快速全选(时间阈值 300ms,位置阈值 12px)
if (!_isCameraMode)
{
var clickPoint = e.GetPosition(this);
var now = DateTime.Now;
if ((e.ClickCount >= 2 || (now - _lastBlankClickTime).TotalMilliseconds <= DoubleClickTimeThresholdMs) &&
Math.Abs(clickPoint.X - _lastBlankClickPosition.X) <= DoubleClickDistanceThresholdPx &&
Math.Abs(clickPoint.Y - _lastBlankClickPosition.Y) <= DoubleClickDistanceThresholdPx)
{
SelectFullScreenArea();
_lastBlankClickTime = DateTime.MinValue;
e.Handled = true;
return;
}
_lastBlankClickTime = now;
_lastBlankClickPosition = clickPoint;
}
// 如果正在选择,先重置状态
if (_isSelecting)
{
_isSelecting = false;
ReleaseMouseCapture();
}
// 开始新的选择
_isSelecting = true;
_startPoint = e.GetPosition(this);
_currentPoint = _startPoint;
// 隐藏提示文字
HintTextBorder.Visibility = Visibility.Collapsed;
if (_isFreehandMode)
{
// 自由绘制模式:开始绘制
_freehandPoints.Clear();
_freehandPolyline.Points.Clear();
SelectedArea = null;
SelectedPath = null;
_freehandPoints.Add(_startPoint);
_freehandPolyline.Points.Add(_startPoint);
// 确保自由绘制路径可见
_freehandPolyline.Visibility = Visibility.Visible;
UpdateFreehandSelectionMask(_freehandPolyline.Points);
}
else
{
// 矩形模式
SelectionRectangle.Visibility = Visibility.Visible;
SizeInfoBorder.Visibility = Visibility.Visible;
}
// 捕获鼠标
CaptureMouse();
if (!_isFreehandMode)
{
UpdateSelection();
}
}
private void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (_isCameraMode)
{
return;
}
// 点击到工具栏等UI元素时不触发全选
var hitElement = e.Source as FrameworkElement;
if (hitElement != null && (
hitElement is Ellipse ||
hitElement is System.Windows.Controls.Button ||
hitElement is Border ||
hitElement is TextBlock ||
hitElement is StackPanel ||
hitElement is Separator ||
hitElement.Name == "SizeInfoBorder" ||
hitElement.Name == "HintText" ||
hitElement.Name == "AdjustModeHint"))
{
return;
}
if (_isSelecting)
{
_isSelecting = false;
if (IsMouseCaptured)
{
ReleaseMouseCapture();
}
}
SelectFullScreenArea();
e.Handled = true;
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
// 如果正在调整模式且正在移动,不处理窗口级别的鼠标移动
if (_isAdjusting && _isMoving)
{
return;
}
if (_isSelecting)
{
_currentPoint = e.GetPosition(this);
if (_isFreehandMode)
{
// 自由绘制模式:添加点到路径
_freehandPoints.Add(_currentPoint);
_freehandPolyline.Points.Add(_currentPoint);
// 确保自由绘制路径可见
_freehandPolyline.Visibility = Visibility.Visible;
UpdateFreehandSelectionMask(_freehandPolyline.Points);
}
else
{
// 矩形模式
UpdateSelection();
}
}
}
private void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// 如果正在调整模式且正在移动,不处理窗口级别的鼠标释放
if (_isAdjusting && _isMoving)
{
return;
}
if (_isSelecting)
{
_isSelecting = false;
ReleaseMouseCapture();
if (_isFreehandMode)
{
// 自由绘制模式:松开后先保留选区,等待用户点击“确认截图/添加到白板”
if (_freehandPoints.Count > 1)
{
// 创建路径副本并闭合
var pathPoints = new List<WpfPoint>(_freehandPoints);
if (pathPoints.Count > 0)
{
pathPoints.Add(_startPoint);
}
var optimizedPath = OptimizePath(pathPoints);
var bounds = CalculatePathBounds(optimizedPath);
if (bounds.Width > 5 && bounds.Height > 5)
{
SelectedPath = optimizedPath;
var dpiScale = GetDpiScale();
var virtualScreen = SystemInformation.VirtualScreen;
int screenX = (int)((bounds.X * dpiScale) + virtualScreen.Left);
int screenY = (int)((bounds.Y * dpiScale) + virtualScreen.Top);
int screenWidth = (int)(bounds.Width * dpiScale);
int screenHeight = (int)(bounds.Height * dpiScale);
SelectedArea = new DrawingRectangle(screenX, screenY, screenWidth, screenHeight);
UpdateFreehandSelectionMask(optimizedPath);
HintText.Text = "自由选区已完成,可点击确认截图/添加到白板;重新拖动可重画";
HintTextBorder.Visibility = Visibility.Visible;
return;
}
}
// 自由绘制无效则清理本次路径,允许继续重绘
_freehandPoints.Clear();
_freehandPolyline.Points.Clear();
_freehandPolyline.Visibility = Visibility.Collapsed;
TransparentSelectionMask.Visibility = Visibility.Collapsed;
OverlayRectangle.Visibility = Visibility.Visible;
SelectedArea = null;
SelectedPath = null;
HintText.Text = "自由选区过小,请重新绘制";
HintTextBorder.Visibility = Visibility.Visible;
return;
}
else
{
// 矩形模式:进入调整模式
var rect = GetSelectionRectangle();
if (rect.Width > 5 && rect.Height > 5) // 最小尺寸检查
{
_currentSelection = rect;
_isAdjusting = true;
ShowControlPoints();
HintText.Text = "拖拽控制点调整选择区域,或拖拽边框移动位置";
HintTextBorder.Visibility = Visibility.Visible;
}
else
{
SelectedArea = null;
SelectionRectangle.Visibility = Visibility.Collapsed;
SizeInfoBorder.Visibility = Visibility.Collapsed;
HintText.Text = "请拖拽选择区域,右键或双击可全选";
HintTextBorder.Visibility = Visibility.Visible;
return;
}
}
}
}
private void ControlPoint_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!_isAdjusting) return;
_isMoving = true;
_lastMousePosition = e.GetPosition(this);
// 确定当前控制点类型
var ellipse = sender as Ellipse;
if (ellipse == TopLeftControl) _activeControlPoint = ControlPointType.TopLeft;
else if (ellipse == TopRightControl) _activeControlPoint = ControlPointType.TopRight;
else if (ellipse == BottomLeftControl) _activeControlPoint = ControlPointType.BottomLeft;
else if (ellipse == BottomRightControl) _activeControlPoint = ControlPointType.BottomRight;
else if (ellipse == TopControl) _activeControlPoint = ControlPointType.Top;
else if (ellipse == BottomControl) _activeControlPoint = ControlPointType.Bottom;
else if (ellipse == LeftControl) _activeControlPoint = ControlPointType.Left;
else if (ellipse == RightControl) _activeControlPoint = ControlPointType.Right;
// 捕获鼠标到控制点本身,而不是整个窗口
ellipse?.CaptureMouse();
e.Handled = true;
}
private void ControlPoint_MouseMove(object sender, MouseEventArgs e)
{
if (!_isAdjusting || !_isMoving || _activeControlPoint == ControlPointType.None) return;
try
{
var currentPosition = e.GetPosition(this);
var delta = currentPosition - _lastMousePosition;
// 根据控制点类型调整选择区域
AdjustSelection(delta);
_lastMousePosition = currentPosition;
e.Handled = true;
}
catch (Exception)
{
// 如果出现异常,停止移动
_isMoving = false;
_activeControlPoint = ControlPointType.None;
var ellipse = sender as Ellipse;
ellipse?.ReleaseMouseCapture();
}
}
private void ControlPoint_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isMoving)
{
_isMoving = false;
_activeControlPoint = ControlPointType.None;
var ellipse = sender as Ellipse;
ellipse?.ReleaseMouseCapture();
e.Handled = true;
}
}
private void AdjustSelection(Vector delta)
{
var newRect = _currentSelection;
switch (_activeControlPoint)
{
case ControlPointType.TopLeft:
newRect.X += delta.X;
newRect.Y += delta.Y;
newRect.Width -= delta.X;
newRect.Height -= delta.Y;
break;
case ControlPointType.TopRight:
newRect.Y += delta.Y;
newRect.Width += delta.X;
newRect.Height -= delta.Y;
break;
case ControlPointType.BottomLeft:
newRect.X += delta.X;
newRect.Width -= delta.X;
newRect.Height += delta.Y;
break;
case ControlPointType.BottomRight:
newRect.Width += delta.X;
newRect.Height += delta.Y;
break;
case ControlPointType.Top:
newRect.Y += delta.Y;
newRect.Height -= delta.Y;
break;
case ControlPointType.Bottom:
newRect.Height += delta.Y;
break;
case ControlPointType.Left:
newRect.X += delta.X;
newRect.Width -= delta.X;
break;
case ControlPointType.Right:
newRect.Width += delta.X;
break;
}
// 确保最小尺寸
if (newRect.Width >= 10 && newRect.Height >= 10)
{
_currentSelection = newRect;
UpdateSelectionDisplay();
}
}
private void ShowControlPoints()
{
// 确保选择矩形在调整模式下可见
SelectionRectangle.Visibility = Visibility.Visible;
// 设置选择矩形的Z-index,确保它能够接收鼠标事件
WpfCanvas.SetZIndex(SelectionRectangle, 1001);
// 确保选择矩形能够接收鼠标事件
SelectionRectangle.IsHitTestVisible = true;
ControlPointsCanvas.Visibility = Visibility.Visible;
UpdateControlPointsPosition();
}
private void UpdateControlPointsPosition()
{
var rect = _currentSelection;
// 更新角控制点位置
WpfCanvas.SetLeft(TopLeftControl, rect.Left - 4);
WpfCanvas.SetTop(TopLeftControl, rect.Top - 4);
WpfCanvas.SetLeft(TopRightControl, rect.Right - 4);
WpfCanvas.SetTop(TopRightControl, rect.Top - 4);
WpfCanvas.SetLeft(BottomLeftControl, rect.Left - 4);
WpfCanvas.SetTop(BottomLeftControl, rect.Bottom - 4);
WpfCanvas.SetLeft(BottomRightControl, rect.Right - 4);
WpfCanvas.SetTop(BottomRightControl, rect.Bottom - 4);
// 更新边控制点位置
WpfCanvas.SetLeft(TopControl, rect.Left + rect.Width / 2 - 4);
WpfCanvas.SetTop(TopControl, rect.Top - 4);
WpfCanvas.SetLeft(BottomControl, rect.Left + rect.Width / 2 - 4);
WpfCanvas.SetTop(BottomControl, rect.Bottom - 4);
WpfCanvas.SetLeft(LeftControl, rect.Left - 4);
WpfCanvas.SetTop(LeftControl, rect.Top + rect.Height / 2 - 4);
WpfCanvas.SetLeft(RightControl, rect.Right - 4);
WpfCanvas.SetTop(RightControl, rect.Top + rect.Height / 2 - 4);
}
private void UpdateSelection()
{
var rect = GetSelectionRectangle();
// 更新选择矩形
WpfCanvas.SetLeft(SelectionRectangle, rect.X);
WpfCanvas.SetTop(SelectionRectangle, rect.Y);
SelectionRectangle.Width = rect.Width;
SelectionRectangle.Height = rect.Height;
// 在选择过程中,禁用选择矩形的鼠标事件,避免干扰选择操作
if (_isSelecting)
{
SelectionRectangle.IsHitTestVisible = false;
}
// 更新透明选择区域遮罩
UpdateTransparentSelectionMask(rect);
// 更新尺寸信息
SizeInfoText.Text = $"{(int)rect.Width} x {(int)rect.Height}";
WpfCanvas.SetLeft(SizeInfoBorder, rect.X);
WpfCanvas.SetTop(SizeInfoBorder, rect.Y - 30);
// 确保尺寸信息不超出屏幕
if (WpfCanvas.GetTop(SizeInfoBorder) < 0)
{
WpfCanvas.SetTop(SizeInfoBorder, rect.Y + rect.Height + 5);
}
}
private void UpdateTransparentSelectionMask(Rect selectionRect)
{
SetSelectionMaskGeometry(new RectangleGeometry(selectionRect));
}
private void UpdateFreehandSelectionMask(IList<WpfPoint> points)
{
if (points == null || points.Count < 3)
{
return;
}
var figure = new PathFigure
{
StartPoint = points[0],
IsClosed = true,
IsFilled = true
};
var segments = new PolyLineSegment();
for (int i = 1; i < points.Count; i++)
{
segments.Points.Add(points[i]);
}
figure.Segments.Add(segments);
var geometry = new PathGeometry();
geometry.Figures.Add(figure);
SetSelectionMaskGeometry(geometry);
}
private void SetSelectionMaskGeometry(Geometry selectionGeometry)
{
try
{
if (!(TransparentSelectionMask.Clip is CombinedGeometry combinedGeometry))
{
throw new InvalidOperationException("TransparentSelectionMask.Clip 不是 CombinedGeometry");
}
combinedGeometry.Geometry2 = selectionGeometry;
// 显示透明遮罩,隐藏原始遮罩
TransparentSelectionMask.Visibility = Visibility.Visible;
OverlayRectangle.Visibility = Visibility.Collapsed;
}
catch (Exception)
{
// 如果几何体操作失败,回退到原始遮罩
TransparentSelectionMask.Visibility = Visibility.Collapsed;
OverlayRectangle.Visibility = Visibility.Visible;
}
}
private void UpdateSelectionDisplay()
{
var rect = _currentSelection;
// 更新选择矩形
WpfCanvas.SetLeft(SelectionRectangle, rect.X);
WpfCanvas.SetTop(SelectionRectangle, rect.Y);
SelectionRectangle.Width = rect.Width;
SelectionRectangle.Height = rect.Height;
// 确保选择矩形在调整模式下能够接收鼠标事件
if (_isAdjusting)
{
SelectionRectangle.IsHitTestVisible = true;
WpfCanvas.SetZIndex(SelectionRectangle, 1001);
}
// 更新透明选择区域遮罩
UpdateTransparentSelectionMask(rect);
// 更新控制点位置
UpdateControlPointsPosition();
// 更新尺寸信息
SizeInfoText.Text = $"{(int)rect.Width} x {(int)rect.Height}";
WpfCanvas.SetLeft(SizeInfoBorder, rect.X);
WpfCanvas.SetTop(SizeInfoBorder, rect.Y - 30);
// 确保尺寸信息不超出屏幕
if (WpfCanvas.GetTop(SizeInfoBorder) < 0)
{
WpfCanvas.SetTop(SizeInfoBorder, rect.Y + rect.Height + 5);
}
}
private Rect GetSelectionRectangle()
{
double x = Math.Min(_startPoint.X, _currentPoint.X);
double y = Math.Min(_startPoint.Y, _currentPoint.Y);
double width = Math.Abs(_currentPoint.X - _startPoint.X);
double height = Math.Abs(_currentPoint.Y - _startPoint.Y);
return new Rect(x, y, width, height);
}
private void ConfirmSelection()
{
// 在自由绘制模式下,走自由选区确认
if (_isFreehandMode)
{
ConfirmFreehandSelection();
return;
}
if (_isAdjusting)
{
// 转换为屏幕坐标,考虑DPI缩放
var dpiScale = GetDpiScale();
var virtualScreen = SystemInformation.VirtualScreen;
// 计算实际屏幕坐标
int screenX = (int)((_currentSelection.X * dpiScale) + virtualScreen.Left);
int screenY = (int)((_currentSelection.Y * dpiScale) + virtualScreen.Top);
int screenWidth = (int)(_currentSelection.Width * dpiScale);
int screenHeight = (int)(_currentSelection.Height * dpiScale);
SelectedArea = new DrawingRectangle(screenX, screenY, screenWidth, screenHeight);
}
if (SelectedArea.HasValue)
{
DialogResult = true;
}
else
{
HintText.Text = "请先选择截图区域";
HintTextBorder.Visibility = Visibility.Visible;
return;
}
Close();
}
private void ConfirmFreehandSelection()
{
if (!SelectedArea.HasValue || SelectedPath == null || SelectedPath.Count < 3)
{
HintText.Text = "请先拖动鼠标绘制自由选区";
HintTextBorder.Visibility = Visibility.Visible;
return;
}
DialogResult = true;
Close();
}
private void SelectFullScreenArea()
{
var virtualScreen = SystemInformation.VirtualScreen;
_currentSelection = new Rect(0, 0, ActualWidth, ActualHeight);
SelectionRectangle.Visibility = Visibility.Visible;
SelectionRectangle.IsHitTestVisible = false;
ControlPointsCanvas.Visibility = Visibility.Collapsed;
SizeInfoBorder.Visibility = Visibility.Visible;
_isAdjusting = false;
_isSelecting = false;
_isFreehandMode = false;
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128));
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128));
UpdateSelectionDisplay();
SelectedPath = null;
SelectedArea = new DrawingRectangle(virtualScreen.X, virtualScreen.Y, virtualScreen.Width, virtualScreen.Height);
HintText.Text = "已全选屏幕,点击确认截图或添加到白板";
HintTextBorder.Visibility = Visibility.Visible;
}
private void CancelSelection()
{
// 停止摄像头预览
if (_cameraService != null)
{
_cameraService.StopPreview();
}
SelectedArea = null;
SelectedPath = null;
CameraImage = null;
ShouldAddToWhiteboard = false;
DialogResult = false;
Close();
}
private Rect CalculatePathBounds(List<WpfPoint> points)
{
if (points == null || points.Count == 0)
return new Rect();
double minX = points[0].X;
double minY = points[0].Y;
double maxX = points[0].X;
double maxY = points[0].Y;
foreach (var point in points)
{
minX = Math.Min(minX, point.X);
minY = Math.Min(minY, point.Y);
maxX = Math.Max(maxX, point.X);
maxY = Math.Max(maxY, point.Y);
}
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
private List<WpfPoint> OptimizePath(List<WpfPoint> points)
{
if (points == null || points.Count < 3)
return points;
var optimized = new List<WpfPoint>();
optimized.Add(points[0]);
for (int i = 1; i < points.Count - 1; i++)
{
var prev = points[i - 1];
var current = points[i];
var next = points[i + 1];
// 计算当前点到前后两点连线的距离
var distance = DistanceToLine(current, prev, next);
// 进一步降低阈值,保留更多点,确保路径质量
if (distance > 0.1) // 从0.5降低到0.1
{
optimized.Add(current);
}
}
optimized.Add(points[points.Count - 1]);
return optimized;
}
private double DistanceToLine(WpfPoint point, WpfPoint lineStart, WpfPoint lineEnd)
{
var A = point.X - lineStart.X;
var B = point.Y - lineStart.Y;
var C = lineEnd.X - lineStart.X;
var D = lineEnd.Y - lineStart.Y;
var dot = A * C + B * D;
var lenSq = C * C + D * D;
if (lenSq == 0) return Math.Sqrt(A * A + B * B);
var param = dot / lenSq;
double xx, yy;
if (param < 0)
{
xx = lineStart.X;
yy = lineStart.Y;
}
else if (param > 1)
{
xx = lineEnd.X;
yy = lineEnd.Y;
}
else
{
xx = lineStart.X + param * C;
yy = lineStart.Y + param * D;
}
var dx = point.X - xx;
var dy = point.Y - yy;
return Math.Sqrt(dx * dx + dy * dy);
}
private void SelectionRectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!_isAdjusting) return;
_isMoving = true;
_activeControlPoint = ControlPointType.Move;
_lastMousePosition = e.GetPosition(this);
// 捕获鼠标到选择矩形
SelectionRectangle.CaptureMouse();
e.Handled = true;
}
private void SelectionRectangle_MouseMove(object sender, MouseEventArgs e)
{
if (!_isAdjusting || !_isMoving || _activeControlPoint != ControlPointType.Move) return;
try
{
var currentPosition = e.GetPosition(this);
var delta = currentPosition - _lastMousePosition;
// 移动整个选择区域
var newRect = _currentSelection;
newRect.X += delta.X;
newRect.Y += delta.Y;
// 确保选择区域不会移出屏幕边界
var screenBounds = new Rect(0, 0, ActualWidth, ActualHeight);
if (newRect.Left < 0) newRect.X = 0;
if (newRect.Top < 0) newRect.Y = 0;
if (newRect.Right > screenBounds.Right) newRect.X = screenBounds.Right - newRect.Width;
if (newRect.Bottom > screenBounds.Bottom) newRect.Y = screenBounds.Bottom - newRect.Height;
_currentSelection = newRect;
UpdateSelectionDisplay();
_lastMousePosition = currentPosition;
e.Handled = true;
}
catch (Exception)
{
// 如果出现异常,停止移动
_isMoving = false;
_activeControlPoint = ControlPointType.None;
SelectionRectangle.ReleaseMouseCapture();
}
}
private void SelectionRectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isMoving && _activeControlPoint == ControlPointType.Move)
{
_isMoving = false;
_activeControlPoint = ControlPointType.None;
SelectionRectangle.ReleaseMouseCapture();
e.Handled = true;
}
}
private void PerformFullScreenCapture()
{
try
{
// 获取虚拟屏幕边界
var virtualScreen = SystemInformation.VirtualScreen;
// 设置全屏截图区域
SelectedArea = new DrawingRectangle(virtualScreen.X, virtualScreen.Y, virtualScreen.Width, virtualScreen.Height);
SelectedPath = null; // 全屏截图不需要路径
// 直接关闭窗口并返回结果
DialogResult = true;
Close();
}
catch (Exception ex)
{
// 如果全屏截图失败,记录错误并关闭窗口
System.Diagnostics.Debug.WriteLine($"全屏截图失败: {ex.Message}");
DialogResult = false;
Close();
}
}
private void ResetSelectionState()
{
// 重置所有选择相关的状态
_isSelecting = false;
_isAdjusting = false;
_isMoving = false;
_isCameraMode = false;
_activeControlPoint = ControlPointType.None;
// 停止摄像头预览
if (_cameraService != null)
{
_cameraService.StopPreview();
}
// 清除自由绘制的内容
_freehandPoints.Clear();
_freehandPolyline.Points.Clear();
_freehandPolyline.Visibility = Visibility.Collapsed;
// 清除矩形选择的内容
SelectionRectangle.Visibility = Visibility.Collapsed;
SelectionRectangle.IsHitTestVisible = false;
ControlPointsCanvas.Visibility = Visibility.Collapsed;
SizeInfoBorder.Visibility = Visibility.Collapsed;
SelectionPath.Visibility = Visibility.Collapsed;
HintTextBorder.Visibility = Visibility.Collapsed;
// 隐藏摄像头预览
CameraPreviewBorder.Visibility = Visibility.Collapsed;
// 重置遮罩
TransparentSelectionMask.Visibility = Visibility.Collapsed;
OverlayRectangle.Visibility = Visibility.Visible;
// 释放鼠标捕获
if (IsMouseCaptured)
{
ReleaseMouseCapture();
}
// 释放选择矩形的鼠标捕获
if (SelectionRectangle.IsMouseCaptured)
{
SelectionRectangle.ReleaseMouseCapture();
}
// 重置选择区域
_currentSelection = new Rect();
SelectedArea = null;
SelectedPath = null;
CameraImage = null;
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
}
#region
private void RotateLeftButton_Click(object sender, RoutedEventArgs e)
{
if (_cameraService != null)
{
_cameraService.RotationAngle = (_cameraService.RotationAngle - 1 + 4) % 4;
UpdateRotationDisplay();
SaveCameraSettings();
}
}
private void RotateRightButton_Click(object sender, RoutedEventArgs e)
{
if (_cameraService != null)
{
_cameraService.RotationAngle = (_cameraService.RotationAngle + 1) % 4;
UpdateRotationDisplay();
SaveCameraSettings();
}
}
private void ResolutionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_cameraService != null && ResolutionComboBox.SelectedItem is ComboBoxItem selectedItem)
{
var resolution = selectedItem.Tag?.ToString();
if (!string.IsNullOrEmpty(resolution))
{
var parts = resolution.Split(',');
if (parts.Length == 2 &&
int.TryParse(parts[0], out int width) &&
int.TryParse(parts[1], out int height))
{
_cameraService.ResolutionWidth = width;
_cameraService.ResolutionHeight = height;
SaveCameraSettings();
}
}
}
}
private void UpdateRotationDisplay()
{
if (_cameraService != null)
{
var angle = _cameraService.RotationAngle * 90;
RotationAngleText.Text = $"{angle}°";
}
}
private void SaveCameraSettings()
{
if (_cameraService != null)
{
MainWindow.Settings.Camera.RotationAngle = _cameraService.RotationAngle;
MainWindow.Settings.Camera.ResolutionWidth = _cameraService.ResolutionWidth;
MainWindow.Settings.Camera.ResolutionHeight = _cameraService.ResolutionHeight;
MainWindow.SaveSettingsToFile();
}
}
#endregion
protected override void OnClosed(EventArgs e)
{
try
{
// 清理摄像头资源
if (_cameraService != null)
{
_cameraService.StopPreview();
_cameraService.Dispose();
_cameraService = null;
}
// 清理摄像头图像
_capturedCameraImage?.Dispose();
CameraImage?.Dispose();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理资源失败: {ex.Message}", LogHelper.LogType.Error);
}
finally
{
base.OnClosed(e);
}
}
}
}