diff --git a/Ink Canvas/MainWindow_cs/MW_BoardControls.cs b/Ink Canvas/MainWindow_cs/MW_BoardControls.cs index 3d3a0e98..0fde1b60 100644 --- a/Ink Canvas/MainWindow_cs/MW_BoardControls.cs +++ b/Ink Canvas/MainWindow_cs/MW_BoardControls.cs @@ -24,14 +24,91 @@ namespace Ink_Canvas { // 保存每页白板图片信息 private void SaveStrokes(bool isBackupMain = false) { + // 确保画布上的所有UI元素都被保存到时间机器历史记录中 + var currentHistory = timeMachine.ExportTimeMachineHistory(); + var elementsInHistory = new HashSet(); + + // 收集已经在历史记录中的元素 + if (currentHistory != null) { + foreach (var h in currentHistory) { + if (h.CommitType == TimeMachineHistoryType.ElementInsert && + h.InsertedElement != null && + !h.StrokeHasBeenCleared) { + elementsInHistory.Add(h.InsertedElement); + } + } + } + + // 检查画布上的所有UI元素,确保它们都在历史记录中 + var missingElements = 0; + foreach (UIElement child in inkCanvas.Children) { + if (child is Image || child is MediaElement) { + if (!elementsInHistory.Contains(child)) { + timeMachine.CommitElementInsertHistory(child); + missingElements++; + LogHelper.WriteLogToFile($"SaveStrokes: 补充保存遗漏的UI元素 {child.GetType().Name}", LogHelper.LogType.Trace); + } + } + } + + if (missingElements > 0) { + LogHelper.WriteLogToFile($"SaveStrokes: 总共补充保存了 {missingElements} 个遗漏的UI元素", LogHelper.LogType.Trace); + } + + // 确保画布上的所有墨迹都被保存 + if (inkCanvas.Strokes.Count > 0) { + // 检查是否有墨迹没有在时间机器历史记录中 + var strokesInHistory = new HashSet(); + if (currentHistory != null) { + foreach (var h in currentHistory) { + if (h.CommitType == TimeMachineHistoryType.UserInput && + h.CurrentStroke != null && + !h.StrokeHasBeenCleared) { + foreach (Stroke stroke in h.CurrentStroke) { + strokesInHistory.Add(stroke); + } + } + } + } + + // 收集没有在历史记录中的墨迹 + var missingStrokes = new StrokeCollection(); + foreach (Stroke stroke in inkCanvas.Strokes) { + if (!strokesInHistory.Contains(stroke)) { + missingStrokes.Add(stroke); + } + } + + if (missingStrokes.Count > 0) { + timeMachine.CommitStrokeUserInputHistory(missingStrokes); + LogHelper.WriteLogToFile($"SaveStrokes: 补充保存了 {missingStrokes.Count} 个遗漏的墨迹", LogHelper.LogType.Trace); + } + } + if (isBackupMain) { var timeMachineHistory = timeMachine.ExportTimeMachineHistory(); TimeMachineHistories[0] = timeMachineHistory; timeMachine.ClearStrokeHistory(); + + // 调试信息 + var elementCount = timeMachineHistory?.Count(h => h.CommitType == TimeMachineHistoryType.ElementInsert && !h.StrokeHasBeenCleared) ?? 0; + var strokeHistoryCount = timeMachineHistory?.Count(h => h.CommitType == TimeMachineHistoryType.UserInput && !h.StrokeHasBeenCleared) ?? 0; + var currentCanvasElements = inkCanvas.Children.Count; + var currentCanvasStrokes = inkCanvas.Strokes.Count; + var selectedElement = selectedUIElement?.GetType().Name ?? "无"; + LogHelper.WriteLogToFile($"SaveStrokes(备份主页面): 保存了 {elementCount} 个UI元素, {strokeHistoryCount} 个墨迹历史, 当前画布有 {currentCanvasElements} 个元素, {currentCanvasStrokes} 个墨迹, 选中元素: {selectedElement}", LogHelper.LogType.Trace); } else { var timeMachineHistory = timeMachine.ExportTimeMachineHistory(); TimeMachineHistories[CurrentWhiteboardIndex] = timeMachineHistory; timeMachine.ClearStrokeHistory(); + + // 调试信息 + var elementCount = timeMachineHistory?.Count(h => h.CommitType == TimeMachineHistoryType.ElementInsert && !h.StrokeHasBeenCleared) ?? 0; + var strokeHistoryCount = timeMachineHistory?.Count(h => h.CommitType == TimeMachineHistoryType.UserInput && !h.StrokeHasBeenCleared) ?? 0; + var currentCanvasElements = inkCanvas.Children.Count; + var currentCanvasStrokes = inkCanvas.Strokes.Count; + var selectedElement = selectedUIElement?.GetType().Name ?? "无"; + LogHelper.WriteLogToFile($"SaveStrokes(页面{CurrentWhiteboardIndex}): 保存了 {elementCount} 个UI元素, {strokeHistoryCount} 个墨迹历史, 当前画布有 {currentCanvasElements} 个元素, {currentCanvasStrokes} 个墨迹, 选中元素: {selectedElement}", LogHelper.LogType.Trace); } } @@ -54,17 +131,29 @@ namespace Ink_Canvas { try { var targetIndex = isBackupMain ? 0 : CurrentWhiteboardIndex; - // 先清空当前画布的所有内容(墨迹和图片) - // 这里必须清除图片,因为页面切换时需要完全重置画布状态 + // 先清空当前画布的墨迹 inkCanvas.Strokes.Clear(); + + // 保存当前的UI元素,稍后会被ApplyHistoryToCanvas正确处理 + var currentElements = new List(); + for (int i = inkCanvas.Children.Count - 1; i >= 0; i--) + { + currentElements.Add(inkCanvas.Children[i]); + } inkCanvas.Children.Clear(); + LogHelper.WriteLogToFile($"RestoreStrokes: 清空了 {currentElements.Count} 个当前UI元素", LogHelper.LogType.Trace); // 如果历史记录为空,直接返回(新页面或空页面) if (TimeMachineHistories[targetIndex] == null) { timeMachine.ClearStrokeHistory(); + LogHelper.WriteLogToFile($"RestoreStrokes({(isBackupMain ? "备份主页面" : $"页面{CurrentWhiteboardIndex}")}): 历史记录为空", LogHelper.LogType.Trace); return; } + var targetHistory = TimeMachineHistories[targetIndex]; + var elementCount = targetHistory?.Count(h => h.CommitType == TimeMachineHistoryType.ElementInsert && !h.StrokeHasBeenCleared) ?? 0; + LogHelper.WriteLogToFile($"RestoreStrokes({(isBackupMain ? "备份主页面" : $"页面{CurrentWhiteboardIndex}")}): 准备恢复 {elementCount} 个UI元素", LogHelper.LogType.Trace); + if (isBackupMain) { timeMachine.ImportTimeMachineHistory(TimeMachineHistories[0]); foreach (var item in TimeMachineHistories[0]) ApplyHistoryToCanvas(item); @@ -73,9 +162,19 @@ namespace Ink_Canvas { // 通过时间机器历史恢复所有内容(墨迹和图片) foreach (var item in TimeMachineHistories[CurrentWhiteboardIndex]) ApplyHistoryToCanvas(item); } + + // 恢复后检查实际的UI元素数量 + var actualElementCount = inkCanvas.Children.Count; + LogHelper.WriteLogToFile($"RestoreStrokes({(isBackupMain ? "备份主页面" : $"页面{CurrentWhiteboardIndex}")}): 实际恢复了 {actualElementCount} 个UI元素", LogHelper.LogType.Trace); + + // 确保选中状态被清除,因为我们切换了页面 + if (selectedUIElement != null) { + DeselectUIElement(); + LogHelper.WriteLogToFile($"RestoreStrokes: 清除了选中状态", LogHelper.LogType.Trace); + } } - catch { - // ignored + catch (Exception ex) { + LogHelper.WriteLogToFile($"RestoreStrokes失败: {ex.Message}", LogHelper.LogType.Error); } } diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs index df09130b..acdeeeef 100644 --- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs +++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs @@ -5,6 +5,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; +using Ink_Canvas.Helpers; using Microsoft.Win32; namespace Ink_Canvas @@ -28,17 +29,10 @@ namespace Ink_Canvas string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); image.Name = timestamp; - // 新缩放逻辑:最大宽高为画布一半,并居中 - double maxWidth = inkCanvas.ActualWidth / 2; - double maxHeight = inkCanvas.ActualHeight / 2; - double scaleX = maxWidth / image.Width; - double scaleY = maxHeight / image.Height; - double scale = Math.Min(1, Math.Min(scaleX, scaleY)); - image.Width = image.Width * scale; - image.Height = image.Height * scale; - InkCanvas.SetLeft(image, (inkCanvas.ActualWidth - image.Width) / 2); - InkCanvas.SetTop(image, (inkCanvas.ActualHeight - image.Height) / 2); + CenterAndScaleElement(image); + InkCanvas.SetLeft(image, 0); + InkCanvas.SetTop(image, 0); inkCanvas.Children.Add(image); // 添加鼠标事件处理,使图片可以被选择 @@ -46,6 +40,7 @@ namespace Ink_Canvas image.IsManipulationEnabled = true; timeMachine.CommitElementInsertHistory(image); + LogHelper.WriteLogToFile($"图片插入: {image.Name}, 当前画布UI元素数量: {inkCanvas.Children.Count}", LogHelper.LogType.Trace); } } } @@ -119,6 +114,8 @@ namespace Ink_Canvas if (mediaElement != null) { + CenterAndScaleElement(mediaElement); + InkCanvas.SetLeft(mediaElement, 0); InkCanvas.SetTop(mediaElement, 0); inkCanvas.Children.Add(mediaElement); @@ -256,5 +253,27 @@ namespace Ink_Canvas } #endregion + + private void CenterAndScaleElement(FrameworkElement element) + { + double maxWidth = SystemParameters.PrimaryScreenWidth / 2; + double maxHeight = SystemParameters.PrimaryScreenHeight / 2; + + double scaleX = maxWidth / element.Width; + double scaleY = maxHeight / element.Height; + double scale = Math.Min(scaleX, scaleY); + + TransformGroup transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform(scale, scale)); + + double canvasWidth = inkCanvas.ActualWidth; + double canvasHeight = inkCanvas.ActualHeight; + double centerX = (canvasWidth - element.Width * scale) / 2; + double centerY = (canvasHeight - element.Height * scale) / 2; + + transformGroup.Children.Add(new TranslateTransform(centerX, centerY)); + + element.RenderTransform = transformGroup; + } } } diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs index 6bd57502..8f27b792 100644 --- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs +++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs @@ -1789,11 +1789,11 @@ namespace Ink_Canvas { // 总是恢复备份墨迹,不管是否在PPT模式 // PPT墨迹和白板墨迹应该分别管理,不应该互相影响 - RestoreStrokes(); + RestoreStrokes(true); LogHelper.WriteLogToFile($"退出白板模式,恢复备份墨迹。当前模式:{(BtnPPTSlideShowEnd.Visibility == Visibility.Visible ? "PPT放映" : "桌面")}", LogHelper.LogType.Trace); - // 退出白板时清空图片 - inkCanvas.Children.Clear(); + // 注释掉:退出白板时不应该清空图片,因为RestoreStrokes()已经恢复了正确的状态 + // inkCanvas.Children.Clear(); if (BtnSwitchTheme.Content.ToString() == "浅色") { BtnSwitch.Content = "黑板"; @@ -1837,8 +1837,8 @@ namespace Ink_Canvas { RestoreStrokes(true); LogHelper.WriteLogToFile($"切换到桌面模式,恢复备份墨迹。当前模式:{(BtnPPTSlideShowEnd.Visibility == Visibility.Visible ? "PPT放映" : "桌面")}", LogHelper.LogType.Trace); - // 退出白板时清空图片 - inkCanvas.Children.Clear(); + // 注释掉:退出白板时不应该清空图片,因为RestoreStrokes()已经恢复了正确的状态 + // inkCanvas.Children.Clear(); if (BtnSwitchTheme.Content.ToString() == "浅色") { BtnSwitch.Content = "黑板"; @@ -2044,17 +2044,10 @@ namespace Ink_Canvas { string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); image.Name = timestamp; - // 新缩放逻辑:最大宽高为画布一半,并居中 - double maxWidth = inkCanvas.ActualWidth / 2; - double maxHeight = inkCanvas.ActualHeight / 2; - double scaleX = maxWidth / image.Width; - double scaleY = maxHeight / image.Height; - double scale = Math.Min(1, Math.Min(scaleX, scaleY)); - image.Width = image.Width * scale; - image.Height = image.Height * scale; - InkCanvas.SetLeft(image, (inkCanvas.ActualWidth - image.Width) / 2); - InkCanvas.SetTop(image, (inkCanvas.ActualHeight - image.Height) / 2); + CenterAndScaleElement(image); + InkCanvas.SetLeft(image, 0); + InkCanvas.SetTop(image, 0); inkCanvas.Children.Add(image); timeMachine.CommitElementInsertHistory(image); @@ -2062,6 +2055,5 @@ namespace Ink_Canvas { } } - } } diff --git a/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs b/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs index dc3c2c53..33cd8fdf 100644 --- a/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs +++ b/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs @@ -6,6 +6,7 @@ using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; +using Ink_Canvas.Helpers; using iNKORE.UI.WPF.Modern.Controls; using Point = System.Windows.Point; @@ -587,6 +588,7 @@ namespace Ink_Canvas { } selectedUIElement = element; + LogHelper.WriteLogToFile($"SelectUIElement: 设置选中元素为 {element?.GetType().Name ?? "null"}", LogHelper.LogType.Trace); if (element != null) { @@ -600,11 +602,13 @@ namespace Ink_Canvas { if (element is Image) { ShowImageToolbar(); + LogHelper.WriteLogToFile($"SelectUIElement: 显示图片工具栏", LogHelper.LogType.Trace); } else { // 对于其他UI元素,显示拖拽手柄 ShowResizeHandles(); + LogHelper.WriteLogToFile($"SelectUIElement: 显示拖拽手柄", LogHelper.LogType.Trace); } } } @@ -867,6 +871,8 @@ namespace Ink_Canvas { private void UIElement_MouseDown(object sender, MouseButtonEventArgs e) { + LogHelper.WriteLogToFile($"UIElement_MouseDown: 编辑模式={inkCanvas.EditingMode}, 元素类型={sender.GetType().Name}", LogHelper.LogType.Trace); + if (inkCanvas.EditingMode == InkCanvasEditingMode.Select) { var element = sender as UIElement; @@ -875,9 +881,14 @@ namespace Ink_Canvas { // 切换到选择模式并选择这个元素 inkCanvas.Select(new[] { element }); SelectUIElement(element); + LogHelper.WriteLogToFile($"UIElement_MouseDown: 选择了UI元素 {element.GetType().Name}", LogHelper.LogType.Trace); e.Handled = true; } } + else + { + LogHelper.WriteLogToFile($"UIElement_MouseDown: 编辑模式不是Select,无法选择UI元素", LogHelper.LogType.Trace); + } } diff --git a/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs b/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs index d5409d6a..6303b6ec 100644 --- a/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs +++ b/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs @@ -135,12 +135,51 @@ namespace Ink_Canvas { if (item.StrokeHasBeenCleared) { // Undo: 移除元素 - if (item.InsertedElement != null && targetCanvas.Children.Contains(item.InsertedElement)) + if (item.InsertedElement != null && targetCanvas.Children.Contains(item.InsertedElement)) { targetCanvas.Children.Remove(item.InsertedElement); + LogHelper.WriteLogToFile($"ApplyHistoryToCanvas: 移除UI元素 {item.InsertedElement.GetType().Name}", LogHelper.LogType.Trace); + } } else { // Redo: 添加元素 - if (item.InsertedElement != null && !targetCanvas.Children.Contains(item.InsertedElement)) + if (item.InsertedElement != null) { + // 确保元素不在画布上,如果在就先移除 + if (targetCanvas.Children.Contains(item.InsertedElement)) { + targetCanvas.Children.Remove(item.InsertedElement); + LogHelper.WriteLogToFile($"ApplyHistoryToCanvas: 先移除已存在的UI元素 {item.InsertedElement.GetType().Name}", LogHelper.LogType.Trace); + } + + // 确保元素没有其他父容器 + if (item.InsertedElement is FrameworkElement fe && fe.Parent != null) { + if (fe.Parent is Panel parentPanel) { + parentPanel.Children.Remove(fe); + LogHelper.WriteLogToFile($"ApplyHistoryToCanvas: 从其他父容器移除UI元素 {item.InsertedElement.GetType().Name}", LogHelper.LogType.Trace); + } + } + targetCanvas.Children.Add(item.InsertedElement); + LogHelper.WriteLogToFile($"ApplyHistoryToCanvas: 添加UI元素 {item.InsertedElement.GetType().Name}", LogHelper.LogType.Trace); + + // 重新绑定事件处理器(仅对主画布) + if (targetCanvas == inkCanvas) { + if (item.InsertedElement is Image img) { + img.MouseDown -= UIElement_MouseDown; + img.MouseDown += UIElement_MouseDown; + img.IsManipulationEnabled = true; + + // 重新应用CenterAndScaleElement变换 + CenterAndScaleElement(img); + LogHelper.WriteLogToFile($"ApplyHistoryToCanvas: 重新配置图片元素 {img.Name}", LogHelper.LogType.Trace); + } else if (item.InsertedElement is MediaElement media) { + media.MouseDown -= UIElement_MouseDown; + media.MouseDown += UIElement_MouseDown; + media.IsManipulationEnabled = true; + + // 重新应用CenterAndScaleElement变换 + CenterAndScaleElement(media); + LogHelper.WriteLogToFile($"ApplyHistoryToCanvas: 重新配置媒体元素 {media.Name}", LogHelper.LogType.Trace); + } + } + } } }