using Ink_Canvas.Controls; using Ink_Canvas.Helpers; using Microsoft.Win32; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Threading; using SystemEvents = Microsoft.Win32.SystemEvents; namespace Ink_Canvas.Windows { /// /// PPT侧滑快捷面板 /// public partial class PPTQuickPanel : UserControl { // Windows Core Audio API [ComImport] [Guid("5CDF2C82-841E-4546-9722-0CF74078229A")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAudioEndpointVolume { int NotImpl1(); int NotImpl2(); int GetChannelCount(out int channelCount); int SetMasterVolumeLevel(float level, Guid eventContext); int SetMasterVolumeLevelScalar(float level, Guid eventContext); int GetMasterVolumeLevel(out float level); int GetMasterVolumeLevelScalar(out float level); int SetChannelVolumeLevel(uint channelNumber, float level, Guid eventContext); int SetChannelVolumeLevelScalar(uint channelNumber, float level, Guid eventContext); int GetChannelVolumeLevel(uint channelNumber, out float level); int GetChannelVolumeLevelScalar(uint channelNumber, out float level); int SetMute([MarshalAs(UnmanagedType.Bool)] bool mute, Guid eventContext); int GetMute(out bool mute); } [ComImport] [Guid("D666063F-1587-4E43-81F1-B948E807363F")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IMMDevice { int Activate(ref Guid iid, int clsCtx, IntPtr activationParams, [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer); int OpenPropertyStore(int stgmAccess, out IPropertyStore propertyStore); int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id); int GetState(out int state); } [ComImport] [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IMMDeviceEnumerator { int NotImpl1(); int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice device); } [ComImport] [Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IPropertyStore { int GetCount(out int propertyCount); int GetAt(int propertyIndex, out Guid propertyKey); int GetValue(ref Guid propertyKey, out object value); int SetValue(ref Guid propertyKey, ref object value); int Commit(); } private static class MMDeviceEnumeratorFactory { [ComImport] [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] private class MMDeviceEnumerator { } public static IMMDeviceEnumerator CreateInstance() { return new MMDeviceEnumerator() as IMMDeviceEnumerator; } } private const int DEVICE_STATE_ACTIVE = 1; private const int eRender = 0; private const int eConsole = 0; private IAudioEndpointVolume _audioEndpointVolume; private bool _isExpanded = false; private bool _isDragging = false; private Point _dragStartPoint; private double _collapsedOffset = 200; // 折叠时的偏移量(隐藏内容区域) private MainWindow _mainWindow; private Dictionary _pptImages = new Dictionary(); private Dictionary> _pptImagePaths = new Dictionary>(); public PPTQuickPanel() { InitializeComponent(); InitializeAudio(); ApplyTheme(); // 监听主题变化 SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; Loaded += PPTQuickPanel_Loaded; Unloaded += PPTQuickPanel_Unloaded; IsVisibleChanged += PPTQuickPanel_IsVisibleChanged; MagnifierWindow.Closed2 += OnMagnifierClosed; } private void OnMagnifierClosed(object sender, EventArgs e) { Dispatcher.BeginInvoke(new Action(SyncMagnifierButtonState)); } private void SyncMagnifierButtonState() { if (MagnifierToggleButton == null) return; MagnifierToggleButton.Content = MagnifierWindow.HasInstance ? "关闭放大镜" : "开启放大镜"; } private void PPTQuickPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if (Visibility == Visibility.Visible) { ApplyTheme(); SyncMagnifierButtonState(); } } private void PPTQuickPanel_Loaded(object sender, RoutedEventArgs e) { // 初始状态为折叠 PanelTransform.X = _collapsedOffset; UpdateArrowRotation(); // 获取MainWindow引用 _mainWindow = Application.Current.MainWindow as MainWindow; SubscribeToPPTEvents(); SubscribeToInkCanvasChildrenChanges(); Dispatcher.BeginInvoke(new Action(() => { UpdateVolumeDisplay(); }), DispatcherPriority.Loaded); } private void SubscribeToInkCanvasChildrenChanges() { try { if (_mainWindow == null) return; System.Windows.Controls.InkCanvas inkCanvas = null; var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (inkCanvasField != null) { inkCanvas = inkCanvasField.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) { var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); inkCanvas = inkCanvasProperty?.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas != null && inkCanvas.Children is INotifyCollectionChanged notifyCollection) { notifyCollection.CollectionChanged += InkCanvasChildren_CollectionChanged; } } catch (Exception ex) { LogHelper.WriteLogToFile($"订阅inkCanvas.Children变化事件失败: {ex.Message}", LogHelper.LogType.Error); } } private void InkCanvasChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { try { if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (var item in e.OldItems) { if (item is FrameworkElement fe && _pptImages.ContainsKey(fe)) { RemoveImageFromPPT(fe); } } } } catch (Exception ex) { LogHelper.WriteLogToFile($"处理inkCanvas.Children变化失败: {ex.Message}", LogHelper.LogType.Error); } } private void RemoveImageFromPPT(FrameworkElement element) { try { if (element == null) return; if (_pptImages.ContainsKey(element)) { int slideNumber = _pptImages[element]; _pptImages.Remove(element); if (_pptImagePaths.ContainsKey(slideNumber)) { string imagePath = element.Tag as string; if (!string.IsNullOrEmpty(imagePath) && _pptImagePaths[slideNumber].Contains(imagePath)) { _pptImagePaths[slideNumber].Remove(imagePath); if (_pptImagePaths[slideNumber].Count == 0) { _pptImagePaths.Remove(slideNumber); DeletePPTImagePathsFile(slideNumber); } else { SavePPTImagePaths(slideNumber); } LogHelper.WriteLogToFile($"已从PPT页面{slideNumber}移除图片: {imagePath}"); } } } } catch (Exception ex) { LogHelper.WriteLogToFile($"从PPT关联数据中移除图片失败: {ex.Message}", LogHelper.LogType.Error); } } private void DeletePPTImagePathsFile(int slideIndex) { try { if (slideIndex <= 0) return; var folderPath = GetPresentationFolderPath(); if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath)) return; var jsonFilePath = Path.Combine(folderPath, slideIndex.ToString("0000") + ".images.json"); if (File.Exists(jsonFilePath)) { File.Delete(jsonFilePath); LogHelper.WriteLogToFile($"已删除第{slideIndex}页图片路径JSON文件: {jsonFilePath}"); } } catch (Exception ex) { LogHelper.WriteLogToFile($"删除PPT图片路径JSON文件失败: {ex.Message}", LogHelper.LogType.Error); } } private void SubscribeToPPTEvents() { try { if (_mainWindow == null) return; // 获取PPTManager var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); var pptManager = pptManagerProperty?.GetValue(_mainWindow); if (pptManager != null) { // 订阅PPT演示文稿打开事件 var presentationOpenEvent = pptManager.GetType().GetEvent("PresentationOpen"); if (presentationOpenEvent != null) { var openHandler = new Action(OnPPTPresentationOpen); presentationOpenEvent.AddEventHandler(pptManager, openHandler); } // 订阅PPT开始事件 var slideShowBeginEvent = pptManager.GetType().GetEvent("SlideShowBegin"); if (slideShowBeginEvent != null) { var beginHandler = new Action(OnPPTSlideShowBegin); slideShowBeginEvent.AddEventHandler(pptManager, beginHandler); } // 订阅PPT翻页事件 var slideShowNextSlideEvent = pptManager.GetType().GetEvent("SlideShowNextSlide"); if (slideShowNextSlideEvent != null) { var handler = new Action(OnPPTSlideChanged); slideShowNextSlideEvent.AddEventHandler(pptManager, handler); } // 订阅PPT结束事件 var slideShowEndEvent = pptManager.GetType().GetEvent("SlideShowEnd"); if (slideShowEndEvent != null) { var handler = new Action(OnPPTSlideShowEnd); slideShowEndEvent.AddEventHandler(pptManager, handler); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"订阅PPT事件失败: {ex.Message}", LogHelper.LogType.Error); } } private void OnPPTPresentationOpen(object presentation) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { // PPT打开时,加载所有图片路径 LoadAllPPTImagePaths(); } catch (Exception ex) { LogHelper.WriteLogToFile($"处理PPT打开事件失败: {ex.Message}", LogHelper.LogType.Error); } }), DispatcherPriority.Normal); } private void OnPPTSlideShowBegin(object window) { Application.Current.Dispatcher.BeginInvoke(new Action(async () => { try { // PPT开始时,加载所有图片路径并加载当前页面的图片 LoadAllPPTImagePaths(); if (_mainWindow == null) return; // 获取当前PPT页面编号 var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); var pptManager = pptManagerProperty?.GetValue(_mainWindow); if (pptManager != null) { var getCurrentSlideNumberMethod = pptManager.GetType().GetMethod("GetCurrentSlideNumber"); int currentSlide = 0; if (getCurrentSlideNumberMethod != null) { var result = getCurrentSlideNumberMethod.Invoke(pptManager, null); if (result != null) { currentSlide = (int)result; } } // 加载当前页面的图片 if (currentSlide > 0) { await LoadPPTImages(currentSlide); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"处理PPT开始事件失败: {ex.Message}", LogHelper.LogType.Error); } }), DispatcherPriority.Normal); } private void OnPPTSlideChanged(object window) { Application.Current.Dispatcher.BeginInvoke(new Action(async () => { try { if (_mainWindow == null) return; // 获取当前PPT页面编号 var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); var pptManager = pptManagerProperty?.GetValue(_mainWindow); if (pptManager != null) { var getCurrentSlideNumberMethod = pptManager.GetType().GetMethod("GetCurrentSlideNumber"); int currentSlide = 0; if (getCurrentSlideNumberMethod != null) { var result = getCurrentSlideNumberMethod.Invoke(pptManager, null); if (result != null) { currentSlide = (int)result; } } // 更新图片可见性 UpdatePPTImagesVisibility(currentSlide); // 加载当前页面的图片(如果还没有加载) if (currentSlide > 0 && (!_pptImagePaths.ContainsKey(currentSlide) || _pptImagePaths[currentSlide].Count == 0)) { // 先加载图片路径 var imagePaths = LoadPPTImagePaths(currentSlide); if (imagePaths != null && imagePaths.Count > 0) { _pptImagePaths[currentSlide] = imagePaths; // 加载图片 await LoadPPTImages(currentSlide); } } } } catch (Exception ex) { LogHelper.WriteLogToFile($"处理PPT翻页事件失败: {ex.Message}", LogHelper.LogType.Error); } }), DispatcherPriority.Normal); } private void OnPPTSlideShowEnd(object presentation) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { // PPT退出时隐藏所有图片 HideAllPPTImages(); } catch (Exception ex) { LogHelper.WriteLogToFile($"处理PPT结束事件失败: {ex.Message}", LogHelper.LogType.Error); } }), DispatcherPriority.Normal); } private void UpdatePPTImagesVisibility(int currentSlide) { try { foreach (var kvp in _pptImages) { var image = kvp.Key; var slideNumber = kvp.Value; // 如果图片在当前页面,显示;否则隐藏 image.Visibility = (slideNumber == currentSlide) ? Visibility.Visible : Visibility.Collapsed; } } catch (Exception ex) { LogHelper.WriteLogToFile($"更新PPT图片可见性失败: {ex.Message}", LogHelper.LogType.Error); } } private void HideAllPPTImages() { try { foreach (var image in _pptImages.Keys) { image.Visibility = Visibility.Collapsed; } } catch (Exception ex) { LogHelper.WriteLogToFile($"隐藏所有PPT图片失败: {ex.Message}", LogHelper.LogType.Error); } } private void PPTQuickPanel_Unloaded(object sender, RoutedEventArgs e) { SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged; UnsubscribeFromInkCanvasChildrenChanges(); } private void UnsubscribeFromInkCanvasChildrenChanges() { try { if (_mainWindow == null) return; System.Windows.Controls.InkCanvas inkCanvas = null; var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (inkCanvasField != null) { inkCanvas = inkCanvasField.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) { var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); inkCanvas = inkCanvasProperty?.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas != null && inkCanvas.Children is INotifyCollectionChanged notifyCollection) { notifyCollection.CollectionChanged -= InkCanvasChildren_CollectionChanged; } } catch (Exception ex) { LogHelper.WriteLogToFile($"取消订阅inkCanvas.Children变化事件失败: {ex.Message}", LogHelper.LogType.Error); } } private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { ApplyTheme(); }), DispatcherPriority.Normal); } #region 音频控制 private void InitializeAudio() { try { var deviceEnumerator = MMDeviceEnumeratorFactory.CreateInstance(); IMMDevice device; deviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, out device); Guid IID_IAudioEndpointVolume = new Guid("5CDF2C82-841E-4546-9722-0CF74078229A"); object interfacePointer; device.Activate(ref IID_IAudioEndpointVolume, 0, IntPtr.Zero, out interfacePointer); _audioEndpointVolume = interfacePointer as IAudioEndpointVolume; } catch (Exception ex) { LogHelper.WriteLogToFile($"初始化音频控制失败: {ex.Message}", LogHelper.LogType.Error); } } private float GetVolume() { if (_audioEndpointVolume == null) return 0.5f; try { float level; _audioEndpointVolume.GetMasterVolumeLevelScalar(out level); return level; } catch { return 0.5f; } } private void SetVolume(float volume) { if (_audioEndpointVolume == null) return; try { volume = Math.Max(0f, Math.Min(1f, volume)); _audioEndpointVolume.SetMasterVolumeLevelScalar(volume, Guid.Empty); } catch (Exception ex) { LogHelper.WriteLogToFile($"设置音量失败: {ex.Message}", LogHelper.LogType.Error); } } private bool GetMute() { if (_audioEndpointVolume == null) return false; try { bool mute; _audioEndpointVolume.GetMute(out mute); return mute; } catch { return false; } } private void SetMute(bool mute) { if (_audioEndpointVolume == null) return; try { _audioEndpointVolume.SetMute(mute, Guid.Empty); } catch (Exception ex) { LogHelper.WriteLogToFile($"设置静音失败: {ex.Message}", LogHelper.LogType.Error); } } private void UpdateVolumeDisplay() { if (_audioEndpointVolume == null) return; try { float volume = GetVolume(); bool isMuted = GetMute(); // 更新滑块值(不触发事件) VolumeSlider.ValueChanged -= VolumeSlider_ValueChanged; VolumeSlider.Value = volume * 100; VolumeSlider.ValueChanged += VolumeSlider_ValueChanged; // 更新文本显示 VolumeValueText.Text = $"{(int)(volume * 100)}%"; // 更新图标 UpdateVolumeIcon(isMuted, volume); } catch (Exception ex) { LogHelper.WriteLogToFile($"更新音量显示失败: {ex.Message}", LogHelper.LogType.Error); } } private void UpdateVolumeIcon(bool isMuted, float volume) { // 清除描边 VolumeIconPath.Stroke = null; VolumeIconPath.StrokeThickness = 0; VolumeIconPath2.Stroke = null; VolumeIconPath2.StrokeThickness = 0; // 默认隐藏第二个Path VolumeIconPath2.Visibility = Visibility.Collapsed; // 静音或音量为0%时显示静音图标 if (isMuted || volume <= 0f) { // 静音图标:扬声器 + X // 扬声器部分 var speakerGeometry = Geometry.Parse("M 7,1.00772 C 6.70313,1.00381 6.42188,1.13272 6.23048,1.35928 L 3,4.99991 H 2 C 0.906251,4.99991 0,5.84366 0,6.99991 V 8.99991 C 0,10.0898 0.910157,10.9999 2,10.9999 H 3 L 6.23048,14.6405 C 6.44141,14.8944 6.72266,15.0038 7,14.9999 V 1.00772 Z"); // X部分 var xGeometry = Geometry.Parse("M 10,5.00012 C 9.73441,5.00012 9.4805,5.10559 9.293,5.29309 C 8.90237,5.68372 8.90237,6.31653 9.293,6.70715 L 10.586,8.00013 L 9.293,9.2931 C 8.90237,9.68372 8.90237,10.3165 9.293,10.7072 C 9.68362,11.0978 10.3164,11.0978 10.7071,10.7072 L 12,9.41419 L 13.293,10.7072 C 13.6836,11.0978 14.3164,11.0978 14.7071,10.7072 C 15.0977,10.3165 15.0977,9.68372 14.7071,9.2931 L 13.4141,8.00013 L 14.7071,6.70715 C 15.0977,6.31653 15.0977,5.68372 14.7071,5.29309 C 14.5196,5.10559 14.2657,5.00012 14,5.00012 C 13.7344,5.00012 13.4805,5.10559 13.293,5.29309 L 12,6.58606 L 10.7071,5.29309 C 10.5196,5.10559 10.2657,5.00012 10,5.00012 Z"); var group = new GeometryGroup(); group.Children.Add(speakerGeometry); group.Children.Add(xGeometry); VolumeIconPath.Data = group; VolumeIconPath2.Visibility = Visibility.Collapsed; } else if (volume >= 0.5f) { // 音量>=50%:扬声器 + 两条声波线 // 扬声器部分 var speakerGeometry = Geometry.Parse("M 7,0 C 6.70313,0 6.42188,0.125086 6.23048,0.35165 L 3,3.99228 H 2 C 0.906251,3.99228 0,4.83603 0,5.99228 V 7.99228 C 0,9.08213 0.910157,9.99228 2,9.99228 H 3 L 6.23048,13.6329 C 6.44141,13.8868 6.72266,13.9962 7,13.9923 V 0 Z"); // 第一条声波线 var wave1Geometry = Geometry.Parse("M 13.461,0.961025 C 13.2695,0.957119 13.0742,1.01571 12.9024,1.12899 C 12.4453,1.44149 12.3242,2.06259 12.6328,2.51962 C 14.457,5.22666 14.457,8.75791 12.6328,11.4649 C 12.3242,11.922 12.4453,12.5431 12.9024,12.8556 C 13.3594,13.1642 13.9805,13.0431 14.293,12.586 C 15.4297,10.8946 16,8.94541 16,6.99228 C 16,5.03915 15.4297,3.08993 14.293,1.39853 C 14.0977,1.11337 13.7813,0.961025 13.461,0.961025 Z"); // 第二条声波线 var wave2Geometry = Geometry.Parse("M 10.0391,2.98056 C 9.81642,2.97275 9.58595,3.03915 9.39454,3.18368 C 9.13282,3.3829 9.00001,3.68368 9.00001,3.98837 V 4.04697 C 9.01173,4.23837 9.07423,4.42197 9.19923,4.58212 C 10.2734,6.01181 10.2734,7.97275 9.19923,9.39853 C 9.07423,9.5626 9.01173,9.74619 9.00001,9.93369 V 9.99619 C 9.00001,10.3009 9.13282,10.6017 9.39454,10.8009 C 9.83595,11.1329 10.4609,11.0431 10.793,10.6017 C 11.5977,9.53525 12,8.26572 12,6.99228 C 12,5.71884 11.5977,4.44931 10.793,3.379 C 10.6094,3.1329 10.3281,2.99618 10.0391,2.98056 Z"); var group = new GeometryGroup(); group.Children.Add(speakerGeometry); group.Children.Add(wave1Geometry); group.Children.Add(wave2Geometry); VolumeIconPath.Data = group; VolumeIconPath2.Visibility = Visibility.Collapsed; } else { // 音量<50%:扬声器 + 一条实线声波线 // 扬声器部分 var speakerGeometry = Geometry.Parse("M 7,1.00759 C 6.70313,1.00369 6.42188,1.13259 6.23048,1.35916 L 3,4.99979 H 2 C 0.906251,4.99979 0,5.84354 0,6.99979 V 8.99979 C 0,10.0896 0.910157,10.9998 2,10.9998 H 3 L 6.23048,14.6404 C 6.44141,14.8943 6.72266,15.0037 7,14.9998 V 1.00759 Z"); // 声波线(实线,不透明度100%) var wave1Geometry = Geometry.Parse("M 10.0391,3.98807 C 9.81642,3.98025 9.58595,4.04666 9.39454,4.19119 C 9.13282,4.39041 9.00001,4.69119 9.00001,4.99588 V 5.06229 C 9.01173,5.24979 9.07813,5.43338 9.19923,5.58963 C 10.2734,7.01932 10.2734,8.98026 9.19923,10.406 C 9.07813,10.5662 9.01173,10.7498 9.00001,10.9373 V 11.0037 C 9.00001,11.3084 9.13282,11.6092 9.39454,11.8084 C 9.83595,12.1404 10.4609,12.0506 10.793,11.6092 C 11.5977,10.5428 12,9.27323 12,7.99979 C 12,6.72635 11.5977,5.45682 10.793,4.3865 C 10.6094,4.14041 10.3281,4.00369 10.0391,3.98807 Z"); // 主Path:扬声器 + 声波线 var group = new GeometryGroup(); group.Children.Add(speakerGeometry); group.Children.Add(wave1Geometry); VolumeIconPath.Data = group; // 隐藏第二个Path VolumeIconPath2.Visibility = Visibility.Collapsed; } } #endregion #region 展开/折叠动画 private void ExpandPanel() { if (_isExpanded) return; _isExpanded = true; UpdateArrowRotation(); var animation = (Storyboard)Resources["ExpandAnimation"]; var doubleAnimation = animation.Children[0] as DoubleAnimation; doubleAnimation.From = PanelTransform.X; doubleAnimation.To = 0; animation.Begin(); } private void CollapsePanel() { if (!_isExpanded) return; _isExpanded = false; UpdateArrowRotation(); var animation = (Storyboard)Resources["CollapseAnimation"]; var doubleAnimation = animation.Children[0] as DoubleAnimation; doubleAnimation.From = PanelTransform.X; doubleAnimation.To = _collapsedOffset; animation.Begin(); } private void UpdateArrowRotation() { if (_isExpanded) { // 展开时箭头指向左(折叠) ArrowRotateTransform.Angle = 0; } else { // 折叠时箭头指向右(展开) ArrowRotateTransform.Angle = 180; } } #endregion #region 箭头按钮事件 private void ArrowButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { e.Handled = true; if (_isExpanded) { CollapsePanel(); } else { ExpandPanel(); } } private void ArrowButton_MouseEnter(object sender, MouseEventArgs e) { ArrowButtonBackgroundBrush.Color = Color.FromArgb(220, 55, 55, 55); } private void ArrowButton_MouseLeave(object sender, MouseEventArgs e) { ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 31, 31, 31); } #endregion #region 拖动手势 private static bool IsWithinSlider(object source) { var d = source as DependencyObject; while (d != null) { if (d is Slider) return true; d = (d is System.Windows.Media.Visual || d is System.Windows.Media.Media3D.Visual3D) ? System.Windows.Media.VisualTreeHelper.GetParent(d) : LogicalTreeHelper.GetParent(d); } return false; } private void ContentBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (IsWithinSlider(e.OriginalSource)) return; _isDragging = true; _dragStartPoint = e.GetPosition(MainCanvas); ContentBorder.CaptureMouse(); e.Handled = true; } private void ContentBorder_MouseMove(object sender, MouseEventArgs e) { if (!_isDragging) return; Point currentPoint = e.GetPosition(MainCanvas); double deltaX = currentPoint.X - _dragStartPoint.X; // 计算新位置 double newX = PanelTransform.X + deltaX; // 限制拖动范围 newX = Math.Max(0, Math.Min(_collapsedOffset, newX)); PanelTransform.X = newX; _dragStartPoint = currentPoint; } private void ContentBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (!_isDragging) return; _isDragging = false; ContentBorder.ReleaseMouseCapture(); // 判断是否超过一半 if (PanelTransform.X < _collapsedOffset / 2) { ExpandPanel(); } else { CollapsePanel(); } } private void ContentBorder_TouchDown(object sender, TouchEventArgs e) { if (IsWithinSlider(e.OriginalSource)) return; _isDragging = true; _dragStartPoint = e.GetTouchPoint(MainCanvas).Position; e.Handled = true; } private void ContentBorder_TouchMove(object sender, TouchEventArgs e) { if (!_isDragging) return; Point currentPoint = e.GetTouchPoint(MainCanvas).Position; double deltaX = currentPoint.X - _dragStartPoint.X; double newX = PanelTransform.X + deltaX; newX = Math.Max(0, Math.Min(_collapsedOffset, newX)); PanelTransform.X = newX; _dragStartPoint = currentPoint; } private void ContentBorder_TouchUp(object sender, TouchEventArgs e) { if (!_isDragging) return; _isDragging = false; if (PanelTransform.X < _collapsedOffset / 2) { ExpandPanel(); } else { CollapsePanel(); } } #endregion #region 音量控制事件 private void VolumeMuteButton_Click(object sender, RoutedEventArgs e) { try { bool currentMute = GetMute(); float volume = GetVolume(); SetMute(!currentMute); UpdateVolumeIcon(!currentMute, volume); } catch (Exception ex) { LogHelper.WriteLogToFile($"切换静音失败: {ex.Message}", LogHelper.LogType.Error); } } private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (!IsLoaded) return; try { float volume = (float)(e.NewValue / 100.0); SetVolume(volume); // 更新文本显示 VolumeValueText.Text = $"{(int)e.NewValue}%"; // 如果音量大于0,取消静音 if (e.NewValue > 0) { bool isMuted = GetMute(); if (isMuted) { SetMute(false); } } // 更新图标(根据当前音量和静音状态) bool currentMute = GetMute(); UpdateVolumeIcon(currentMute, volume); } catch (Exception ex) { LogHelper.WriteLogToFile($"音量滑块值改变处理失败: {ex.Message}", LogHelper.LogType.Error); } } private void VolumeSlider_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { // 确保精度为1% double value = VolumeSlider.Value; VolumeSlider.Value = Math.Round(value); } private void VolumeSlider_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { // 触摸操作完成时,确保精度为1% double value = VolumeSlider.Value; VolumeSlider.Value = Math.Round(value); } #endregion #region 图片插入 private async void InsertImageSelectFileButton_Click(object sender, RoutedEventArgs e) { if (_mainWindow == null) return; try { var dialog = new OpenFileDialog { Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf" }; if (dialog.ShowDialog() == true) { string filePath = dialog.FileName; var createImageMethod = _mainWindow.GetType().GetMethod("CreateAndCompressImageAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (createImageMethod != null) { var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { filePath }) as System.Threading.Tasks.Task; if (imageTask != null) { var inserted = await imageTask; if (inserted != null) { inserted.Tag = filePath; await InsertImageToMainWindow(inserted, filePath); } } } else { LogHelper.WriteLogToFile("无法找到CreateAndCompressImageAsync方法", LogHelper.LogType.Warning); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"插入图片失败: {ex.Message}", LogHelper.LogType.Error); } } private async void InsertImageScreenshotButton_Click(object sender, RoutedEventArgs e) { if (_mainWindow == null) return; try { var captureScreenshotMethod = _mainWindow.GetType().GetMethod("CaptureScreenshotAndInsert", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (captureScreenshotMethod != null) { var task = captureScreenshotMethod.Invoke(_mainWindow, null) as System.Threading.Tasks.Task; if (task != null) { await task; await SaveScreenshotToPPT(); } } else { LogHelper.WriteLogToFile("无法找到CaptureScreenshotAndInsert方法", LogHelper.LogType.Warning); } } catch (Exception ex) { LogHelper.WriteLogToFile($"截图插入失败: {ex.Message}", LogHelper.LogType.Error); } } private async System.Threading.Tasks.Task SaveScreenshotToPPT() { try { if (_mainWindow == null) return; System.Windows.Controls.InkCanvas inkCanvas = null; var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (inkCanvasField != null) { inkCanvas = inkCanvasField.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) { var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); inkCanvas = inkCanvasProperty?.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) return; System.Windows.Controls.Image lastScreenshot = null; foreach (System.Windows.Controls.Image img in inkCanvas.Children.OfType()) { if (img.Name != null && (img.Name.StartsWith("screenshot_") || img.Name.StartsWith("camera_")) && img.Tag == null) { lastScreenshot = img; } } if (lastScreenshot == null) return; string screenshotFilePath = await SaveScreenshotToFile(lastScreenshot); if (string.IsNullOrEmpty(screenshotFilePath)) { LogHelper.WriteLogToFile("保存截图文件失败", LogHelper.LogType.Warning); return; } lastScreenshot.Tag = screenshotFilePath; await ManageScreenshotInPPT(lastScreenshot, screenshotFilePath); } catch (Exception ex) { LogHelper.WriteLogToFile($"保存截图到PPT失败: {ex.Message}", LogHelper.LogType.Error); } } private async System.Threading.Tasks.Task SaveScreenshotToFile(System.Windows.Controls.Image image) { try { if (image?.Source == null) return null; string savePath = null; var settingsProperty = _mainWindow.GetType().GetProperty("Settings"); if (settingsProperty != null) { var settings = settingsProperty.GetValue(_mainWindow); if (settings != null) { var autoSavedStrokesLocationProperty = settings.GetType().GetProperty("Automation"); if (autoSavedStrokesLocationProperty != null) { var automation = autoSavedStrokesLocationProperty.GetValue(settings); if (automation != null) { var locationProperty = automation.GetType().GetProperty("AutoSavedStrokesLocation"); if (locationProperty != null) { var location = locationProperty.GetValue(automation) as string; if (!string.IsNullOrEmpty(location)) { savePath = Path.Combine(location, "File Dependency"); } } } } } } if (string.IsNullOrEmpty(savePath)) { LogHelper.WriteLogToFile("无法获取AutoSavedStrokesLocation", LogHelper.LogType.Warning); return null; } if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } string timestamp = image.Name; string filePath = Path.Combine(savePath, timestamp + ".png"); await Application.Current.Dispatcher.InvokeAsync(() => { if (image.Source is BitmapSource bitmapSource) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); using (var fileStream = new FileStream(filePath, FileMode.Create)) { encoder.Save(fileStream); } } }); return filePath; } catch (Exception ex) { LogHelper.WriteLogToFile($"保存截图到文件失败: {ex.Message}", LogHelper.LogType.Error); return null; } } private async System.Threading.Tasks.Task ManageScreenshotInPPT(System.Windows.Controls.Image image, string filePath) { if (_mainWindow == null || image == null || string.IsNullOrEmpty(filePath)) return; await Application.Current.Dispatcher.InvokeAsync(() => { try { int currentSlideNumber = 0; try { var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); var pptManager = pptManagerProperty?.GetValue(_mainWindow); if (pptManager != null) { var isInSlideShowProperty = pptManager.GetType().GetProperty("IsInSlideShow"); bool isInSlideShow = false; if (isInSlideShowProperty != null) { var result = isInSlideShowProperty.GetValue(pptManager); if (result != null) { isInSlideShow = (bool)result; } } if (isInSlideShow) { var getCurrentSlideNumberMethod = pptManager.GetType().GetMethod("GetCurrentSlideNumber"); if (getCurrentSlideNumberMethod != null) { var result = getCurrentSlideNumberMethod.Invoke(pptManager, null); if (result != null) { currentSlideNumber = (int)result; } } } } } catch (Exception ex) { LogHelper.WriteLogToFile($"获取当前PPT页面编号失败: {ex.Message}", LogHelper.LogType.Warning); } if (currentSlideNumber > 0 && !string.IsNullOrEmpty(filePath)) { _pptImages[image] = currentSlideNumber; if (!_pptImagePaths.ContainsKey(currentSlideNumber)) { _pptImagePaths[currentSlideNumber] = new List(); } _pptImagePaths[currentSlideNumber].Add(filePath); SavePPTImagePaths(currentSlideNumber); LogHelper.WriteLogToFile($"截图已关联到PPT页面{currentSlideNumber}: {filePath}"); } else if (currentSlideNumber > 0) { _pptImages[image] = currentSlideNumber; } } catch (Exception ex) { LogHelper.WriteLogToFile($"管理截图在PPT中的关联失败: {ex.Message}", LogHelper.LogType.Error); } }, DispatcherPriority.Normal); } private async System.Threading.Tasks.Task InsertImageToMainWindow(FrameworkElement element, string originalFilePath = null, bool saveToJson = true) { if (_mainWindow == null || element == null) return; // 确保在UI线程上执行 await Application.Current.Dispatcher.InvokeAsync(() => { try { // 生成唯一名称 string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); element.Name = timestamp; // 设置图片属性 element.IsHitTestVisible = true; element.Focusable = false; System.Windows.Controls.InkCanvas inkCanvas = null; var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (inkCanvasField != null) { inkCanvas = inkCanvasField.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) { var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (inkCanvasProperty != null) { inkCanvas = inkCanvasProperty.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } } if (inkCanvas == null) { LogHelper.WriteLogToFile("无法获取inkCanvas", LogHelper.LogType.Error); return; } // 初始化InkCanvas选择设置 var initializeInkCanvasSelectionSettingsMethod = _mainWindow.GetType().GetMethod("InitializeInkCanvasSelectionSettings", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); initializeInkCanvasSelectionSettingsMethod?.Invoke(_mainWindow, null); // 获取当前PPT页面编号(如果在PPT模式下) int currentSlideNumber = 0; try { var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); var pptManager = pptManagerProperty?.GetValue(_mainWindow); if (pptManager != null) { var isInSlideShowProperty = pptManager.GetType().GetProperty("IsInSlideShow"); bool isInSlideShow = false; if (isInSlideShowProperty != null) { var result = isInSlideShowProperty.GetValue(pptManager); if (result != null) { isInSlideShow = (bool)result; } } if (isInSlideShow) { var getCurrentSlideNumberMethod = pptManager.GetType().GetMethod("GetCurrentSlideNumber"); if (getCurrentSlideNumberMethod != null) { var result = getCurrentSlideNumberMethod.Invoke(pptManager, null); if (result != null) { currentSlideNumber = (int)result; } } } } } catch (Exception ex) { LogHelper.WriteLogToFile($"获取当前PPT页面编号失败: {ex.Message}", LogHelper.LogType.Warning); } // 如果在PPT模式下,记录图片和页面编号的关联,并保存图片路径 if (currentSlideNumber > 0 && !string.IsNullOrEmpty(originalFilePath) && saveToJson) { _pptImages[element] = currentSlideNumber; // 添加到页面图片路径列表 if (!_pptImagePaths.ContainsKey(currentSlideNumber)) { _pptImagePaths[currentSlideNumber] = new List(); } _pptImagePaths[currentSlideNumber].Add(originalFilePath); // 保存图片路径到JSON文件 SavePPTImagePaths(currentSlideNumber); } else if (currentSlideNumber > 0) { // 即使不保存到JSON,也要记录图片和页面编号的关联(用于翻页显示/隐藏) _pptImages[element] = currentSlideNumber; } // 先添加到画布(与MainWindow的实现保持一致) inkCanvas.Children.Add(element); // 等待图片加载完成后再进行后续处理 element.Loaded += (s, args) => { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { // 初始化TransformGroup var initializeTransformMethod = _mainWindow.GetType().GetMethod("InitializeElementTransform", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); initializeTransformMethod?.Invoke(_mainWindow, new object[] { element }); // 居中缩放 var centerMethod = _mainWindow.GetType().GetMethod("CenterAndScaleElement", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); centerMethod?.Invoke(_mainWindow, new object[] { element }); // 绑定事件处理器 var bindEventsMethod = _mainWindow.GetType().GetMethod("BindElementEvents", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); bindEventsMethod?.Invoke(_mainWindow, new object[] { element }); LogHelper.WriteLogToFile($"图片插入完成: {element.Name}, PPT页面: {currentSlideNumber}"); } catch (Exception ex) { LogHelper.WriteLogToFile($"图片加载后处理失败: {ex.Message}", LogHelper.LogType.Error); } }), DispatcherPriority.Loaded); }; // 提交历史记录 var timeMachineProperty = _mainWindow.GetType().GetProperty("timeMachine", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); var timeMachine = timeMachineProperty?.GetValue(_mainWindow); if (timeMachine != null) { var commitMethod = timeMachine.GetType().GetMethod("CommitElementInsertHistory"); commitMethod?.Invoke(timeMachine, new object[] { element }); } // 切换到选择模式 var setModeMethod = _mainWindow.GetType().GetMethod("SetCurrentToolMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); setModeMethod?.Invoke(_mainWindow, new object[] { System.Windows.Controls.InkCanvasEditingMode.Select }); var updateModeMethod = _mainWindow.GetType().GetMethod("UpdateCurrentToolMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); updateModeMethod?.Invoke(_mainWindow, new object[] { "select" }); var hidePanelsMethod = _mainWindow.GetType().GetMethod("HideSubPanels", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); hidePanelsMethod?.Invoke(_mainWindow, new object[] { "select" }); } catch (Exception ex) { LogHelper.WriteLogToFile($"插入图片到MainWindow失败: {ex.Message}", LogHelper.LogType.Error); } }, DispatcherPriority.Normal); } #endregion #region PPT图片保存和加载 /// /// 获取PPT演示文稿的文件夹路径 /// private string GetPresentationFolderPath() { try { if (_mainWindow == null) return null; // 获取PPTInkManager var singlePPTInkManagerField = _mainWindow.GetType().GetField("_singlePPTInkManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); var singlePPTInkManager = singlePPTInkManagerField?.GetValue(_mainWindow); if (singlePPTInkManager != null) { // 使用反射获取AutoSaveLocation var autoSaveLocationProperty = singlePPTInkManager.GetType().GetProperty("AutoSaveLocation"); var autoSaveLocation = autoSaveLocationProperty?.GetValue(singlePPTInkManager) as string; if (!string.IsNullOrEmpty(autoSaveLocation)) { // 获取PPTManager以获取当前演示文稿信息 var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); var pptManager = pptManagerProperty?.GetValue(_mainWindow); if (pptManager != null) { // 尝试获取当前演示文稿,使用重试机制 Microsoft.Office.Interop.PowerPoint.Presentation presentation = null; // 首先尝试使用 CurrentPresentation 属性(可能是缓存的) try { var currentPresentationProperty = pptManager.GetType().GetProperty("CurrentPresentation", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); presentation = currentPresentationProperty?.GetValue(pptManager) as Microsoft.Office.Interop.PowerPoint.Presentation; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } // 如果 CurrentPresentation 不可用,尝试 GetCurrentActivePresentation if (presentation == null) { try { var getCurrentActivePresentationMethod = pptManager.GetType().GetMethod("GetCurrentActivePresentation"); presentation = getCurrentActivePresentationMethod?.Invoke(pptManager, null) as Microsoft.Office.Interop.PowerPoint.Presentation; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } if (presentation != null) { try { // 立即检查COM对象是否仍然有效 if (!System.Runtime.InteropServices.Marshal.IsComObject(presentation)) { return null; } // 尝试访问对象以验证其有效性 try { var _ = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(presentation); System.Runtime.InteropServices.Marshal.Release(_); } catch (System.Runtime.InteropServices.InvalidComObjectException) { // COM对象已失效,静默返回 return null; } // 立即生成演示文稿ID(在对象失效前) string presentationId = GeneratePresentationId(presentation); if (string.IsNullOrEmpty(presentationId) || presentationId.StartsWith("unknown_") || presentationId.StartsWith("invalid_") || presentationId.StartsWith("com_error_")) { // 生成ID失败,返回null而不是抛出异常 return null; } return Path.Combine(autoSaveLocation, "Auto Saved - Presentations", presentationId); } catch (System.Runtime.InteropServices.InvalidComObjectException) { // COM对象已失效,静默返回(不记录错误日志) return null; } catch (System.Runtime.InteropServices.COMException) { // COM异常,对象可能已失效,静默返回(不记录错误日志) return null; } catch (Exception ex) { // 其他异常,只记录非COM相关异常 if (!(ex is System.Runtime.InteropServices.InvalidComObjectException) && !(ex is System.Runtime.InteropServices.COMException)) { LogHelper.WriteLogToFile($"获取PPT文件夹路径时发生非COM异常: {ex.Message}", LogHelper.LogType.Warning); } return null; } } } } } } catch (System.Runtime.InteropServices.InvalidComObjectException) { // COM对象已失效,静默返回(不记录错误日志) return null; } catch (System.Runtime.InteropServices.COMException) { // COM异常,对象可能已失效,静默返回(不记录错误日志) return null; } catch (Exception ex) { // 只记录非COM相关异常 if (!(ex is System.Runtime.InteropServices.InvalidComObjectException) && !(ex is System.Runtime.InteropServices.COMException)) { LogHelper.WriteLogToFile($"获取PPT文件夹路径失败: {ex.Message}", LogHelper.LogType.Error); } } return null; } /// /// 生成演示文稿ID(与PPTInkManager一致) /// private string GeneratePresentationId(Microsoft.Office.Interop.PowerPoint.Presentation presentation) { if (presentation == null) { return $"unknown_{DateTime.Now.Ticks}"; } try { // 检查COM对象是否仍然有效 if (!System.Runtime.InteropServices.Marshal.IsComObject(presentation)) { return $"unknown_{DateTime.Now.Ticks}"; } // 验证COM对象有效性 try { var _ = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(presentation); System.Runtime.InteropServices.Marshal.Release(_); } catch (System.Runtime.InteropServices.InvalidComObjectException) { // COM对象已失效 return $"unknown_{DateTime.Now.Ticks}"; } // 逐个访问属性,每个都进行异常处理 string presentationName = null; string presentationPath = null; int slidesCount = 0; try { presentationName = presentation.Name; } catch (System.Runtime.InteropServices.InvalidComObjectException) { return $"unknown_{DateTime.Now.Ticks}"; } catch (System.Runtime.InteropServices.COMException) { return $"unknown_{DateTime.Now.Ticks}"; } try { presentationPath = presentation.FullName; } catch (System.Runtime.InteropServices.InvalidComObjectException) { return $"unknown_{DateTime.Now.Ticks}"; } catch (System.Runtime.InteropServices.COMException) { return $"unknown_{DateTime.Now.Ticks}"; } try { slidesCount = presentation.Slides.Count; } catch (System.Runtime.InteropServices.InvalidComObjectException) { return $"unknown_{DateTime.Now.Ticks}"; } catch (System.Runtime.InteropServices.COMException) { return $"unknown_{DateTime.Now.Ticks}"; } var fileHash = GetFileHash(presentationPath); return $"{presentationName}_{slidesCount}_{fileHash}"; } catch (System.Runtime.InteropServices.InvalidComObjectException) { // COM对象已失效 return $"unknown_{DateTime.Now.Ticks}"; } catch (System.Runtime.InteropServices.COMException) { // COM异常,对象可能已失效 return $"unknown_{DateTime.Now.Ticks}"; } catch (Exception ex) { LogHelper.WriteLogToFile($"生成演示文稿ID失败: {ex}", LogHelper.LogType.Error); return $"unknown_{DateTime.Now.Ticks}"; } } /// /// 计算文件哈希值 /// private string GetFileHash(string filePath) { try { if (string.IsNullOrEmpty(filePath)) return "unknown"; using (var md5 = System.Security.Cryptography.MD5.Create()) { byte[] hashBytes = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(filePath)); return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8); } } catch (Exception ex) { LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error); return "error"; } } /// /// 保存指定页面的图片路径到JSON文件 /// private void SavePPTImagePaths(int slideIndex) { try { if (slideIndex <= 0 || !_pptImagePaths.ContainsKey(slideIndex)) return; var folderPath = GetPresentationFolderPath(); if (string.IsNullOrEmpty(folderPath)) return; if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } var jsonFilePath = Path.Combine(folderPath, slideIndex.ToString("0000") + ".images.json"); var imagePaths = _pptImagePaths[slideIndex]; string json = JsonConvert.SerializeObject(imagePaths, Formatting.Indented); File.WriteAllText(jsonFilePath, json); LogHelper.WriteLogToFile($"已保存第{slideIndex}页图片路径到JSON: {jsonFilePath}"); } catch (Exception ex) { LogHelper.WriteLogToFile($"保存PPT图片路径失败: {ex.Message}", LogHelper.LogType.Error); } } /// /// 从JSON文件加载指定页面的图片路径 /// private List LoadPPTImagePaths(int slideIndex) { try { if (slideIndex <= 0) return new List(); var folderPath = GetPresentationFolderPath(); if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath)) return new List(); var jsonFilePath = Path.Combine(folderPath, slideIndex.ToString("0000") + ".images.json"); if (!File.Exists(jsonFilePath)) return new List(); string json = File.ReadAllText(jsonFilePath); var imagePaths = JsonConvert.DeserializeObject>(json); return imagePaths ?? new List(); } catch (Exception ex) { LogHelper.WriteLogToFile($"加载PPT图片路径失败: {ex.Message}", LogHelper.LogType.Error); return new List(); } } /// /// 加载指定页面的图片 /// private async System.Threading.Tasks.Task LoadPPTImages(int slideIndex) { try { if (_mainWindow == null || slideIndex <= 0) return; var imagePaths = LoadPPTImagePaths(slideIndex); if (imagePaths == null || imagePaths.Count == 0) return; // 获取inkCanvas,检查是否已有图片 System.Windows.Controls.InkCanvas inkCanvas = null; var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (inkCanvasField != null) { inkCanvas = inkCanvasField.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) { var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); inkCanvas = inkCanvasProperty?.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas; } if (inkCanvas == null) return; // 检查已存在的图片路径(通过Tag) var existingImagePaths = new HashSet(); foreach (var existingImage in inkCanvas.Children.OfType()) { if (existingImage.Tag is string tagPath && !string.IsNullOrEmpty(tagPath)) existingImagePaths.Add(tagPath); } foreach (var existingPdf in inkCanvas.Children.OfType()) { if (existingPdf.Tag is string tagPath && !string.IsNullOrEmpty(tagPath)) existingImagePaths.Add(tagPath); } // 使用反射调用MainWindow的CreateAndCompressImageAsync方法 var createImageMethod = _mainWindow.GetType().GetMethod("CreateAndCompressImageAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (createImageMethod == null) { LogHelper.WriteLogToFile("无法找到CreateAndCompressImageAsync方法", LogHelper.LogType.Warning); return; } foreach (var imagePath in imagePaths) { try { // 如果图片已存在,跳过 if (existingImagePaths.Contains(imagePath)) { continue; } if (!File.Exists(imagePath)) { LogHelper.WriteLogToFile($"图片文件不存在: {imagePath}", LogHelper.LogType.Warning); continue; } var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { imagePath }) as System.Threading.Tasks.Task; if (imageTask != null) { var inserted = await imageTask; if (inserted != null) { // 保存原始文件路径到Tag inserted.Tag = imagePath; // 插入图片(不保存路径,因为已经存在) await InsertImageToMainWindow(inserted, imagePath, false); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"加载图片失败: {imagePath}, 错误: {ex.Message}", LogHelper.LogType.Error); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"加载PPT图片失败: {ex.Message}", LogHelper.LogType.Error); } } /// /// 加载所有页面的图片路径(在PPT打开时调用) /// public void LoadAllPPTImagePaths() { try { if (_mainWindow == null) return; var folderPath = GetPresentationFolderPath(); if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath)) return; // 清空现有数据 _pptImagePaths.Clear(); // 查找所有图片JSON文件 var jsonFiles = Directory.GetFiles(folderPath, "*.images.json"); foreach (var jsonFile in jsonFiles) { try { var fileName = Path.GetFileNameWithoutExtension(jsonFile); fileName = fileName.Replace(".images", ""); // 移除.images后缀 if (int.TryParse(fileName, out int slideIndex) && slideIndex > 0) { var imagePaths = LoadPPTImagePaths(slideIndex); if (imagePaths != null && imagePaths.Count > 0) { _pptImagePaths[slideIndex] = imagePaths; } } } catch (Exception ex) { LogHelper.WriteLogToFile($"加载图片路径文件失败: {jsonFile}, 错误: {ex.Message}", LogHelper.LogType.Error); } } LogHelper.WriteLogToFile($"已加载{_pptImagePaths.Count}个页面的图片路径"); } catch (Exception ex) { LogHelper.WriteLogToFile($"加载所有PPT图片路径失败: {ex.Message}", LogHelper.LogType.Error); } } #endregion #region 主题适配 private void ApplyTheme() { try { if (MainWindow.Settings != null) { ApplyTheme(MainWindow.Settings); } } catch (Exception ex) { LogHelper.WriteLogToFile($"应用PPT快捷面板主题失败: {ex.Message}", LogHelper.LogType.Error); } } private void ApplyTheme(Settings settings) { try { // Game Bar 风格:始终使用深色半透明外壳,不随系统主题翻转 ArrowButtonBackgroundBrush.Color = Color.FromArgb(204, 31, 31, 31); ContentBackgroundBrush.Color = Color.FromArgb(204, 31, 31, 31); // #CC1F1F1F ArrowPathFillBrush.Color = Colors.White; VolumeIconFillBrush.Color = Colors.White; VolumeIconFillBrush2.Color = Colors.White; VolumeValueForegroundBrush.Color = Color.FromArgb(230, 255, 255, 255); MagnifierTitleForegroundBrush.Color = Colors.White; MagnifierDescForegroundBrush.Color = Color.FromArgb(200, 255, 255, 255); Separator1BackgroundBrush.Color = Color.FromArgb(60, 255, 255, 255); } catch (Exception ex) { LogHelper.WriteLogToFile($"应用PPT快捷面板主题失败: {ex.Message}", LogHelper.LogType.Error); } } #endregion #region 聚焦放大镜 private void MagnifierToggleButton_Click(object sender, RoutedEventArgs e) { try { if (MagnifierWindow.HasInstance) { MagnifierWindow.HideInstance(); MagnifierToggleButton.Content = "开启放大镜"; } else { MagnifierWindow.Show((float)MagnifierZoomSlider.Value); if (MagnifierWindow.HasInstance) { MagnifierToggleButton.Content = "关闭放大镜"; } } } catch (Exception ex) { LogHelper.WriteLogToFile($"切换聚焦放大镜失败: {ex.Message}", LogHelper.LogType.Error); } } private void MagnifierZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (MagnifierZoomValueText != null) { MagnifierZoomValueText.Text = $"{e.NewValue:0.0}x"; } if (MagnifierWindow.HasInstance) { MagnifierWindow.SetZoom((float)e.NewValue); } } #endregion #region 公开方法 /// /// 设置面板的可见性(仅在PPT模式下显示) /// public void UpdateVisibility(bool isInPPTMode) { Visibility = isInPPTMode ? Visibility.Visible : Visibility.Collapsed; if (!isInPPTMode && MagnifierWindow.HasInstance) { MagnifierWindow.HideInstance(); if (MagnifierToggleButton != null) MagnifierToggleButton.Content = "开启放大镜"; } } #endregion } }