From d2abadd69b421a5569c3f30f054dd86ab18ef614 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Thu, 19 Feb 2026 18:24:55 +0800
Subject: [PATCH] =?UTF-8?q?add:=E5=B1=95=E5=8F=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow.xaml | 16 ++
Ink Canvas/MainWindow_cs/MW_BoardControls.cs | 12 +-
Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs | 177 ++++++++++++++++++
3 files changed, 204 insertions(+), 1 deletion(-)
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index 13c5e9fd..116ebe99 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -10558,6 +10558,22 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
+
+
+
_capturedPhotos = new List();
+ // 按页绑定:每一页对应一个“实时画面”元素与布局/设备信息
+ private readonly Dictionary _liveFrameImageByPage = new Dictionary();
+ private readonly HashSet _liveEnabledPages = new HashSet();
+ private readonly Dictionary _cameraIndexByPage = new Dictionary();
+ private readonly Dictionary _liveFrameLayoutByPage =
+ new Dictionary();
+
private DateTime _lastCaptureTime = DateTime.MinValue;
private const int VideoPresenterCaptureCooldownMs = 1000;
@@ -53,6 +63,12 @@ namespace Ink_Canvas
{
CheckBoxEnablePhotoCorrection.IsChecked = Settings?.Automation?.IsEnablePhotoCorrection ?? false;
}
+
+ // 同步“上屏”按钮状态(按页绑定)
+ if (BtnToggleVideoPresenterLiveOnCanvas != null)
+ {
+ BtnToggleVideoPresenterLiveOnCanvas.IsChecked = _liveEnabledPages.Contains(GetCurrentPageIndex());
+ }
}
private void BtnCloseVideoPresenter_Click(object sender, RoutedEventArgs e)
@@ -119,6 +135,9 @@ namespace Ink_Canvas
{
BtnCapturePhoto.IsEnabled = true;
}
+
+ // 实时上屏:刷新当前页的画面元素
+ TryUpdateLiveFrameOnCanvas(preview);
}));
}
catch
@@ -127,6 +146,68 @@ namespace Ink_Canvas
}
}
+ private int GetCurrentPageIndex()
+ {
+ return Math.Max(1, CurrentWhiteboardIndex);
+ }
+
+ private void TryUpdateLiveFrameOnCanvas(BitmapImage preview)
+ {
+ try
+ {
+ int page = GetCurrentPageIndex();
+ if (!_liveEnabledPages.Contains(page)) return;
+ if (inkCanvas == null) return;
+ if (!_liveFrameImageByPage.TryGetValue(page, out var img) || img == null) return;
+
+ if (!inkCanvas.Children.Contains(img))
+ {
+ inkCanvas.Children.Add(img);
+ }
+
+ img.Source = preview;
+ img.Visibility = Visibility.Visible;
+ }
+ catch { }
+ }
+
+ private System.Windows.Controls.Image EnsureLiveFrameElementForPage(int page)
+ {
+ if (_liveFrameImageByPage.TryGetValue(page, out var existing) && existing != null) return existing;
+
+ var img = new System.Windows.Controls.Image
+ {
+ Tag = VideoPresenterLiveFrameTag,
+ Stretch = System.Windows.Media.Stretch.Uniform,
+ Width = 520,
+ Visibility = Visibility.Visible,
+ Opacity = 1.0
+ };
+ _liveFrameImageByPage[page] = img;
+ return img;
+ }
+
+ private void ApplyLiveFrameLayoutForPage(int page, System.Windows.Controls.Image img)
+ {
+ if (img == null) return;
+
+ if (_liveFrameLayoutByPage.TryGetValue(page, out var layout))
+ {
+ if (!double.IsNaN(layout.width) && layout.width > 10) img.Width = layout.width;
+ InkCanvas.SetLeft(img, Math.Max(0, layout.left));
+ InkCanvas.SetTop(img, Math.Max(0, layout.top));
+ return;
+ }
+
+ // 默认位置:居中偏上
+ double x = (inkCanvas?.ActualWidth ?? 0) / 2 - img.Width / 2;
+ double y = (inkCanvas?.ActualHeight ?? 0) / 2 - 200;
+ if (double.IsNaN(x) || double.IsInfinity(x)) x = 100;
+ if (double.IsNaN(y) || double.IsInfinity(y)) y = 100;
+ InkCanvas.SetLeft(img, Math.Max(0, x));
+ InkCanvas.SetTop(img, Math.Max(0, y));
+ }
+
private void RefreshVideoPresenterDeviceList()
{
if (_cameraService == null) return;
@@ -184,6 +265,7 @@ namespace Ink_Canvas
try
{
EnsureCameraService();
+ _cameraIndexByPage[GetCurrentPageIndex()] = cameraIndex;
if (_cameraService.StartPreview(cameraIndex))
{
if (BtnCapturePhoto != null) BtnCapturePhoto.IsEnabled = true;
@@ -195,6 +277,101 @@ namespace Ink_Canvas
}
}
+ private void BtnToggleVideoPresenterLiveOnCanvas_Checked(object sender, RoutedEventArgs e)
+ {
+ int page = GetCurrentPageIndex();
+ _liveEnabledPages.Add(page);
+
+ var img = EnsureLiveFrameElementForPage(page);
+ ApplyLiveFrameLayoutForPage(page, img);
+
+ if (inkCanvas != null && !inkCanvas.Children.Contains(img))
+ {
+ inkCanvas.Children.Add(img);
+ }
+
+ // 立即用侧栏预览刷新一次
+ if (VideoPresenterPreviewImage?.Source is BitmapImage bi)
+ {
+ img.Source = bi;
+ }
+ }
+
+ private void BtnToggleVideoPresenterLiveOnCanvas_Unchecked(object sender, RoutedEventArgs e)
+ {
+ int page = GetCurrentPageIndex();
+ _liveEnabledPages.Remove(page);
+
+ if (_liveFrameImageByPage.TryGetValue(page, out var img) && img != null)
+ {
+ try
+ {
+ if (inkCanvas != null && inkCanvas.Children.Contains(img))
+ {
+ inkCanvas.Children.Remove(img);
+ }
+ }
+ catch { }
+ }
+ }
+
+ // 翻页前调用:保存当前页实时画面的位置/大小
+ private void VideoPresenter_BeforePageLeave()
+ {
+ try
+ {
+ int page = GetCurrentPageIndex();
+ if (!_liveFrameImageByPage.TryGetValue(page, out var img) || img == null) return;
+
+ double left = InkCanvas.GetLeft(img);
+ double top = InkCanvas.GetTop(img);
+ if (double.IsNaN(left)) left = 0;
+ if (double.IsNaN(top)) top = 0;
+
+ _liveFrameLayoutByPage[page] = (left, top, img.Width);
+ }
+ catch { }
+ }
+
+ // 翻页后调用:根据该页状态恢复实时画面,并同步设备选择
+ private void VideoPresenter_OnPageChanged()
+ {
+ try
+ {
+ int page = GetCurrentPageIndex();
+
+ // 同步“上屏”按钮状态
+ if (BtnToggleVideoPresenterLiveOnCanvas != null)
+ {
+ BtnToggleVideoPresenterLiveOnCanvas.IsChecked = _liveEnabledPages.Contains(page);
+ }
+
+ // 若该页上屏,恢复画面元素(RestoreStrokes 会清空 inkCanvas.Children)
+ if (_liveEnabledPages.Contains(page))
+ {
+ var img = EnsureLiveFrameElementForPage(page);
+ ApplyLiveFrameLayoutForPage(page, img);
+ if (inkCanvas != null && !inkCanvas.Children.Contains(img))
+ {
+ inkCanvas.Children.Add(img);
+ }
+
+ if (VideoPresenterPreviewImage?.Source is BitmapImage bi)
+ {
+ img.Source = bi;
+ }
+ }
+
+ // 按页摄像头索引:切页后自动切回该页的摄像头
+ if (_cameraIndexByPage.TryGetValue(page, out int idx))
+ {
+ EnsureCameraService();
+ _cameraService?.StartPreview(idx);
+ }
+ }
+ catch { }
+ }
+
private void BtnCapturePhoto_Click(object sender, RoutedEventArgs e)
{
try