using Microsoft.Win32; using System; using System.IO; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using Ink_Canvas.Helpers; namespace Ink_Canvas { public partial class MainWindow : Window { #region Image private async void BtnImageInsert_Click(object sender, RoutedEventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "Image files (*.jpg; *.jpeg; *.png; *.bmp)|*.jpg;*.jpeg;*.png;*.bmp"; if (openFileDialog.ShowDialog() == true) { string filePath = openFileDialog.FileName; Image image = await CreateAndCompressImageAsync(filePath); if (image != null) { string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); image.Name = timestamp; CenterAndScaleElement(image); inkCanvas.Children.Add(image); timeMachine.CommitElementInsertHistory(image); } } } private async Task CreateAndCompressImageAsync(string filePath) { string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency"); if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } string fileExtension = Path.GetExtension(filePath); string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); string newFilePath = Path.Combine(savePath, timestamp + fileExtension); await Task.Run(() => File.Copy(filePath, newFilePath, true)); return await Dispatcher.InvokeAsync(() => { BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.UriSource = new Uri(newFilePath); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); int width = bitmapImage.PixelWidth; int height = bitmapImage.PixelHeight; Image image = new Image(); // 设置拉伸模式为Fill,支持任意比例缩放 image.Stretch = Stretch.Fill; if (isLoaded && Settings.Canvas.IsCompressPicturesUploaded && (width > 1920 || height > 1080)) { double scaleX = 1920.0 / width; double scaleY = 1080.0 / height; double scale = Math.Min(scaleX, scaleY); TransformedBitmap transformedBitmap = new TransformedBitmap(bitmapImage, new ScaleTransform(scale, scale)); image.Source = transformedBitmap; image.Width = transformedBitmap.PixelWidth; image.Height = transformedBitmap.PixelHeight; } else { image.Source = bitmapImage; image.Width = width; image.Height = height; } return image; }); } #endregion #region Media private async void BtnMediaInsert_Click(object sender, RoutedEventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "Media files (*.mp4; *.avi; *.wmv)|*.mp4;*.avi;*.wmv"; if (openFileDialog.ShowDialog() == true) { string filePath = openFileDialog.FileName; byte[] mediaBytes = await Task.Run(() => File.ReadAllBytes(filePath)); MediaElement mediaElement = await CreateMediaElementAsync(filePath); if (mediaElement != null) { CenterAndScaleElement(mediaElement); InkCanvas.SetLeft(mediaElement, 0); InkCanvas.SetTop(mediaElement, 0); inkCanvas.Children.Add(mediaElement); mediaElement.LoadedBehavior = MediaState.Manual; mediaElement.UnloadedBehavior = MediaState.Manual; mediaElement.Loaded += async (_, args) => { mediaElement.Play(); await Task.Delay(100); mediaElement.Pause(); }; timeMachine.CommitElementInsertHistory(mediaElement); } } } private async Task CreateMediaElementAsync(string filePath) { string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency"); if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } return await Dispatcher.InvokeAsync(() => { MediaElement mediaElement = new MediaElement(); mediaElement.Source = new Uri(filePath); string timestamp = "media_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); mediaElement.Name = timestamp; mediaElement.LoadedBehavior = MediaState.Manual; mediaElement.UnloadedBehavior = MediaState.Manual; mediaElement.Width = 256; mediaElement.Height = 256; string fileExtension = Path.GetExtension(filePath); string newFilePath = Path.Combine(savePath, mediaElement.Name + fileExtension); File.Copy(filePath, newFilePath, true); mediaElement.Source = new Uri(newFilePath); return mediaElement; }); } #endregion #region Image Operations /// /// 旋转图片 /// /// 要旋转的图片 /// 旋转角度(正数为顺时针,负数为逆时针) private void RotateImage(Image image, double angle) { if (image == null) return; try { // 获取当前的变换 var transformGroup = image.RenderTransform as TransformGroup ?? new TransformGroup(); // 查找现有的旋转变换 RotateTransform rotateTransform = null; foreach (Transform transform in transformGroup.Children) { if (transform is RotateTransform rt) { rotateTransform = rt; break; } } // 如果没有旋转变换,创建一个新的 if (rotateTransform == null) { rotateTransform = new RotateTransform(); transformGroup.Children.Add(rotateTransform); } // 设置旋转中心为图片中心 rotateTransform.CenterX = image.ActualWidth / 2; rotateTransform.CenterY = image.ActualHeight / 2; // 累加旋转角度 rotateTransform.Angle = (rotateTransform.Angle + angle) % 360; // 应用变换 image.RenderTransform = transformGroup; // 提交到时间机器以支持撤销 // 注意:旋转操作目前不支持撤销,因为需要更复杂的历史记录机制 } catch (Exception ex) { // 记录错误但不中断程序 System.Diagnostics.Debug.WriteLine($"旋转图片时发生错误: {ex.Message}"); } } /// /// 克隆图片 /// /// 要克隆的图片 private void CloneImage(Image image) { if (image == null) return; try { // 创建图片的副本 var clonedImage = new Image { Source = image.Source, Width = image.Width, Height = image.Height, Stretch = image.Stretch, RenderTransform = image.RenderTransform?.Clone() as Transform }; // 设置位置,稍微偏移以避免重叠 InkCanvas.SetLeft(clonedImage, InkCanvas.GetLeft(image) + 20); InkCanvas.SetTop(clonedImage, InkCanvas.GetTop(image) + 20); // 添加到画布 inkCanvas.Children.Add(clonedImage); // 提交到时间机器以支持撤销 timeMachine.CommitElementInsertHistory(clonedImage); } catch (Exception ex) { // 记录错误但不中断程序 System.Diagnostics.Debug.WriteLine($"克隆图片时发生错误: {ex.Message}"); } } /// /// 克隆图片到新页面 /// /// 要克隆的图片 private void CloneImageToNewBoard(Image image) { if (image == null) return; try { // 创建图片的副本 var clonedImage = new Image { Source = image.Source, Width = image.Width, Height = image.Height, Stretch = image.Stretch, RenderTransform = image.RenderTransform?.Clone() as Transform }; // 设置位置,稍微偏移以避免重叠 InkCanvas.SetLeft(clonedImage, InkCanvas.GetLeft(image) + 20); InkCanvas.SetTop(clonedImage, InkCanvas.GetTop(image) + 20); // 创建新页面 BtnWhiteBoardAdd_Click(null, null); // 添加到新页面的画布 inkCanvas.Children.Add(clonedImage); // 提交到时间机器以支持撤销 timeMachine.CommitElementInsertHistory(clonedImage); } catch (Exception ex) { // 记录错误但不中断程序 System.Diagnostics.Debug.WriteLine($"克隆图片到新页面时发生错误: {ex.Message}"); } } /// /// 缩放图片 /// /// 要缩放的图片 /// 缩放因子(大于1为放大,小于1为缩小) private void ScaleImage(Image image, double scaleFactor) { if (image == null) return; try { // 获取当前的变换 var transformGroup = image.RenderTransform as TransformGroup ?? new TransformGroup(); // 查找现有的缩放变换 ScaleTransform scaleTransform = null; foreach (Transform transform in transformGroup.Children) { if (transform is ScaleTransform st) { scaleTransform = st; break; } } // 如果没有缩放变换,创建一个新的 if (scaleTransform == null) { scaleTransform = new ScaleTransform(); transformGroup.Children.Add(scaleTransform); } // 设置缩放中心为图片中心 scaleTransform.CenterX = image.ActualWidth / 2; scaleTransform.CenterY = image.ActualHeight / 2; // 应用缩放因子 scaleTransform.ScaleX *= scaleFactor; scaleTransform.ScaleY *= scaleFactor; // 应用变换 image.RenderTransform = transformGroup; // 提交到时间机器以支持撤销 // 注意:缩放操作目前不支持撤销,因为需要更复杂的历史记录机制 } catch (Exception ex) { // 记录错误但不中断程序 System.Diagnostics.Debug.WriteLine($"缩放图片时发生错误: {ex.Message}"); } } /// /// 删除图片 /// /// 要删除的图片 private void DeleteImage(Image image) { if (image == null) return; try { // 从画布中移除图片 if (inkCanvas.Children.Contains(image)) { inkCanvas.Children.Remove(image); // 提交到时间机器以支持撤销 timeMachine.CommitElementRemoveHistory(image); } } catch (Exception ex) { // 记录错误但不中断程序 System.Diagnostics.Debug.WriteLine($"删除图片时发生错误: {ex.Message}"); } } #endregion private void CenterAndScaleElement(FrameworkElement element) { try { // 确保元素已加载且有有效尺寸 if (element == null || element.ActualWidth <= 0 || element.ActualHeight <= 0) { // 如果元素尺寸无效,等待加载完成后再处理 element.Loaded += (sender, e) => { Dispatcher.BeginInvoke(new Action(() => { CenterAndScaleElement(element); }), System.Windows.Threading.DispatcherPriority.Loaded); }; return; } // 获取画布的实际尺寸 double canvasWidth = inkCanvas.ActualWidth; double canvasHeight = inkCanvas.ActualHeight; // 如果画布尺寸为0,使用窗口尺寸作为备选 if (canvasWidth <= 0 || canvasHeight <= 0) { canvasWidth = this.ActualWidth; canvasHeight = this.ActualHeight; } // 如果仍然为0,使用屏幕尺寸 if (canvasWidth <= 0 || canvasHeight <= 0) { canvasWidth = SystemParameters.PrimaryScreenWidth; canvasHeight = SystemParameters.PrimaryScreenHeight; } // 计算最大允许尺寸(画布的70%) double maxWidth = canvasWidth * 0.7; double maxHeight = canvasHeight * 0.7; // 获取元素的当前尺寸 double elementWidth = element.ActualWidth; double elementHeight = element.ActualHeight; // 计算缩放比例 double scaleX = maxWidth / elementWidth; double scaleY = maxHeight / elementHeight; double scale = Math.Min(scaleX, scaleY); // 如果元素本身比最大尺寸小,不进行缩放 if (scale > 1.0) { scale = 1.0; } // 计算新的尺寸 double newWidth = elementWidth * scale; double newHeight = elementHeight * scale; // 设置元素尺寸 element.Width = newWidth; element.Height = newHeight; // 计算居中位置 double centerX = (canvasWidth - newWidth) / 2; double centerY = (canvasHeight - newHeight) / 2; // 确保位置不为负数 centerX = Math.Max(0, centerX); centerY = Math.Max(0, centerY); // 设置位置 InkCanvas.SetLeft(element, centerX); InkCanvas.SetTop(element, centerY); // 清除任何现有的RenderTransform element.RenderTransform = Transform.Identity; LogHelper.WriteLogToFile($"元素居中完成: 位置({centerX}, {centerY}), 尺寸({newWidth}x{newHeight})"); } catch (Exception ex) { LogHelper.WriteLogToFile($"元素居中失败: {ex.Message}", LogHelper.LogType.Error); } } } }