add:pdf插入
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace Ink_Canvas.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// 画布上的多页 PDF:仅显示当前页;翻页与页码由主窗口 PDF 侧栏控制(无 XAML 文件)。
|
||||
/// </summary>
|
||||
public class PdfEmbeddedView : UserControl
|
||||
{
|
||||
private readonly Image _pageImage;
|
||||
|
||||
private string _pdfPath;
|
||||
private uint _pageCount;
|
||||
private uint _currentIndex;
|
||||
private bool _compressLargePictures;
|
||||
private bool _isPagingBusy;
|
||||
private bool _layoutSizeLocked;
|
||||
|
||||
/// <summary>页码或可翻页状态变化(用于更新侧栏)。</summary>
|
||||
public event EventHandler PageNavigationStateChanged;
|
||||
|
||||
public PdfEmbeddedView()
|
||||
{
|
||||
MinWidth = 80;
|
||||
MinHeight = 60;
|
||||
|
||||
var grid = new Grid { ClipToBounds = true };
|
||||
_pageImage = new Image
|
||||
{
|
||||
Stretch = Stretch.Uniform,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
grid.Children.Add(_pageImage);
|
||||
Content = grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化并显示第一页;由 MainWindow 在 UI 线程创建后调用。
|
||||
/// </summary>
|
||||
public async Task InitializeAsync(string pdfFilePath, uint pageCount, bool compressLargePictures)
|
||||
{
|
||||
_pdfPath = pdfFilePath ?? throw new ArgumentNullException(nameof(pdfFilePath));
|
||||
_pageCount = pageCount;
|
||||
_compressLargePictures = compressLargePictures;
|
||||
_currentIndex = 0;
|
||||
|
||||
await ShowPageAsync(_currentIndex);
|
||||
}
|
||||
|
||||
public string PdfPath => _pdfPath;
|
||||
|
||||
public uint PageCount => _pageCount;
|
||||
|
||||
public uint CurrentPageIndex => _currentIndex;
|
||||
|
||||
public string PageLabelText => _pageCount == 0 ? "" : $"{_currentIndex + 1} / {_pageCount}";
|
||||
|
||||
public bool CanGoPrevious => !_isPagingBusy && _pageCount > 1 && _currentIndex > 0;
|
||||
|
||||
public bool CanGoNext => !_isPagingBusy && _pageCount > 1 && _currentIndex < _pageCount - 1;
|
||||
|
||||
public async Task GoToPreviousPageAsync()
|
||||
{
|
||||
await GoRelativeAsync(-1);
|
||||
}
|
||||
|
||||
public async Task GoToNextPageAsync()
|
||||
{
|
||||
await GoRelativeAsync(1);
|
||||
}
|
||||
|
||||
private void NotifyPageNavigationStateChanged()
|
||||
{
|
||||
PageNavigationStateChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async Task GoRelativeAsync(int delta)
|
||||
{
|
||||
if (_isPagingBusy || _pageCount <= 1)
|
||||
return;
|
||||
int next = (int)_currentIndex + delta;
|
||||
if (next < 0 || next >= _pageCount)
|
||||
return;
|
||||
_currentIndex = (uint)next;
|
||||
await ShowPageAsync(_currentIndex);
|
||||
}
|
||||
|
||||
private async Task ShowPageAsync(uint pageIndex)
|
||||
{
|
||||
_isPagingBusy = true;
|
||||
NotifyPageNavigationStateChanged();
|
||||
try
|
||||
{
|
||||
BitmapSource raw = await PdfWinRtHelper.RenderPageToBitmapSourceAsync(_pdfPath, pageIndex);
|
||||
if (raw == null)
|
||||
return;
|
||||
|
||||
BitmapSource display = ApplyCompressionIfNeeded(raw);
|
||||
_pageImage.Source = display;
|
||||
if (!_layoutSizeLocked)
|
||||
{
|
||||
Width = display.PixelWidth;
|
||||
Height = display.PixelHeight;
|
||||
_layoutSizeLocked = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isPagingBusy = false;
|
||||
NotifyPageNavigationStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapSource ApplyCompressionIfNeeded(BitmapSource rendered)
|
||||
{
|
||||
int width = rendered.PixelWidth;
|
||||
int height = rendered.PixelHeight;
|
||||
if (_compressLargePictures && (width > 1920 || height > 1080))
|
||||
{
|
||||
double scaleX = 1920.0 / width;
|
||||
double scaleY = 1080.0 / height;
|
||||
double scale = Math.Min(scaleX, scaleY);
|
||||
return new TransformedBitmap(rendered, new ScaleTransform(scale, scale));
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Windows.Data.Pdf;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用 Windows.Data.Pdf(WinRT)将 PDF 页渲染为 WPF 可用的位图。
|
||||
/// </summary>
|
||||
internal static class PdfWinRtHelper
|
||||
{
|
||||
public static async Task<uint> GetPageCountAsync(string pdfPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pdfPath) || !File.Exists(pdfPath))
|
||||
return 0;
|
||||
|
||||
var file = await StorageFile.GetFileFromPathAsync(pdfPath).AsTask();
|
||||
var doc = await PdfDocument.LoadFromFileAsync(file).AsTask();
|
||||
if (doc.IsPasswordProtected)
|
||||
return 0;
|
||||
return doc.PageCount;
|
||||
}
|
||||
|
||||
public static async Task<BitmapSource> RenderPageToBitmapSourceAsync(string pdfPath, uint pageIndex)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pdfPath) || !File.Exists(pdfPath))
|
||||
return null;
|
||||
|
||||
var file = await StorageFile.GetFileFromPathAsync(pdfPath).AsTask();
|
||||
var doc = await PdfDocument.LoadFromFileAsync(file).AsTask();
|
||||
if (doc.IsPasswordProtected)
|
||||
return null;
|
||||
if (pageIndex >= doc.PageCount)
|
||||
return null;
|
||||
|
||||
var page = doc.GetPage(pageIndex);
|
||||
try
|
||||
{
|
||||
using (var ras = new InMemoryRandomAccessStream())
|
||||
{
|
||||
await page.RenderToStreamAsync(ras).AsTask();
|
||||
ras.Seek(0);
|
||||
|
||||
var ms = new MemoryStream();
|
||||
using (var netStream = ras.AsStreamForRead())
|
||||
netStream.CopyTo(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
try
|
||||
{
|
||||
return await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
var bi = new BitmapImage();
|
||||
bi.BeginInit();
|
||||
bi.StreamSource = ms;
|
||||
bi.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bi.EndInit();
|
||||
bi.Freeze();
|
||||
return (BitmapSource)bi;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
ms.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
(page as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,7 @@
|
||||
<PackageReference Include="MdXaml" Version="1.27.0" />
|
||||
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1018" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.2" />
|
||||
<PackageReference Include="System.Runtime.WindowsRuntime" Version="4.7.0" />
|
||||
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="Microsoft.International.Converters.PinYinConverter" Version="1.0.0" />
|
||||
|
||||
@@ -4948,6 +4948,109 @@
|
||||
</Viewbox>
|
||||
</Border>
|
||||
|
||||
<Border Name="BorderPdfPageSidebar"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,0,0,0"
|
||||
Width="58"
|
||||
CornerRadius="10"
|
||||
Background="{DynamicResource FloatBarBackground}"
|
||||
BorderBrush="{DynamicResource FloatBarBorderBrush}"
|
||||
BorderThickness="1"
|
||||
SnapsToDevicePixels="True"
|
||||
Visibility="Collapsed"
|
||||
Panel.ZIndex="1001">
|
||||
<Border.Resources>
|
||||
<Style x:Key="PdfSidebarNavButtonStyle" TargetType="Border">
|
||||
<Setter Property="Width" Value="42" />
|
||||
<Setter Property="Height" Value="38" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource FloatBarForeground}" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="White" Opacity="0.10" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Color="White" Opacity="0.22" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Resources>
|
||||
<Grid>
|
||||
<ikw:SimpleStackPanel VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="10"
|
||||
Margin="0,8,0,8">
|
||||
<TextBlock Text="PDF"
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
Opacity="0.9" />
|
||||
<Border CornerRadius="8"
|
||||
Padding="6,8"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource FloatBarForeground}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="White" Opacity="0.08" />
|
||||
</Border.Background>
|
||||
<TextBlock Name="TextBlockPdfSidebarPageLabel"
|
||||
Text="— / —"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextAlignment="Center"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
Opacity="0.55" />
|
||||
</Border>
|
||||
<Border Name="BorderPdfSidebarPagePrev"
|
||||
Style="{StaticResource PdfSidebarNavButtonStyle}"
|
||||
Opacity="0.35"
|
||||
IsHitTestVisible="False"
|
||||
MouseDown="Border_MouseDown"
|
||||
MouseUp="BorderPdfSidebarPagePrev_MouseUp">
|
||||
<TextBlock Text="‹"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,-2,0,0"
|
||||
Foreground="{DynamicResource FloatBarForeground}" />
|
||||
</Border>
|
||||
<Border Name="BorderPdfSidebarPageNext"
|
||||
Style="{StaticResource PdfSidebarNavButtonStyle}"
|
||||
Opacity="0.35"
|
||||
IsHitTestVisible="False"
|
||||
MouseDown="Border_MouseDown"
|
||||
MouseUp="BorderPdfSidebarPageNext_MouseUp">
|
||||
<TextBlock Text="›"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,-2,0,0"
|
||||
Foreground="{DynamicResource FloatBarForeground}" />
|
||||
</Border>
|
||||
<TextBlock Text="翻页"
|
||||
FontSize="9"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource FloatBarForeground}"
|
||||
Opacity="0.65" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 图片缩放选择点 -->
|
||||
<Canvas Name="ImageResizeHandlesCanvas"
|
||||
Visibility="Collapsed"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ink_Canvas.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
@@ -56,48 +57,48 @@ namespace Ink_Canvas
|
||||
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";
|
||||
openFileDialog.Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf";
|
||||
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = openFileDialog.FileName;
|
||||
|
||||
Image image = await CreateAndCompressImageAsync(filePath);
|
||||
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
|
||||
|
||||
if (image != null)
|
||||
if (element != null)
|
||||
{
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
element.Name = timestamp;
|
||||
|
||||
// 设置图片属性,避免被InkCanvas选择系统处理
|
||||
image.IsHitTestVisible = true;
|
||||
image.Focusable = false;
|
||||
element.IsHitTestVisible = true;
|
||||
element.Focusable = false;
|
||||
|
||||
// 初始化InkCanvas选择设置
|
||||
InitializeInkCanvasSelectionSettings();
|
||||
|
||||
// 先添加到画布
|
||||
inkCanvas.Children.Add(image);
|
||||
inkCanvas.Children.Add(element);
|
||||
|
||||
// 等待图片加载完成后再进行后续处理
|
||||
image.Loaded += (s, args) =>
|
||||
element.Loaded += (s, args) =>
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
// 初始化TransformGroup
|
||||
InitializeElementTransform(image);
|
||||
InitializeElementTransform(element);
|
||||
|
||||
// 居中缩放
|
||||
CenterAndScaleElement(image);
|
||||
CenterAndScaleElement(element);
|
||||
|
||||
// 最后绑定事件处理器
|
||||
BindElementEvents(image);
|
||||
BindElementEvents(element);
|
||||
|
||||
LogHelper.WriteLogToFile($"图片插入完成: {image.Name}");
|
||||
LogHelper.WriteLogToFile($"图片插入完成: {element.Name}");
|
||||
}), DispatcherPriority.Loaded);
|
||||
};
|
||||
|
||||
timeMachine.CommitElementInsertHistory(image);
|
||||
timeMachine.CommitElementInsertHistory(element);
|
||||
|
||||
// 插入图片后切换到选择模式并刷新浮动栏高光显示
|
||||
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
||||
@@ -270,13 +271,13 @@ namespace Ink_Canvas
|
||||
ApplyMouseDragTransform(element, currentPoint, dragStartPoint);
|
||||
|
||||
// 如果是图片元素,更新工具栏位置
|
||||
if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(element) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(element);
|
||||
}
|
||||
|
||||
// 如果是图片元素,更新选择点位置
|
||||
if (element is Image && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(element) && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
|
||||
}
|
||||
@@ -306,13 +307,13 @@ namespace Ink_Canvas
|
||||
ApplyWheelScaleTransform(element, e);
|
||||
|
||||
// 如果是图片元素,更新工具栏位置
|
||||
if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(element) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(element);
|
||||
}
|
||||
|
||||
// 如果是图片元素,更新选择点位置
|
||||
if (element is Image && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(element) && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
|
||||
}
|
||||
@@ -396,13 +397,13 @@ namespace Ink_Canvas
|
||||
ApplyTouchManipulationTransform(element, e);
|
||||
|
||||
// 如果是图片元素,更新工具栏位置
|
||||
if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(element) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(element);
|
||||
}
|
||||
|
||||
// 如果是图片元素,更新选择点位置
|
||||
if (element is Image && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(element) && ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
|
||||
}
|
||||
@@ -523,12 +524,15 @@ namespace Ink_Canvas
|
||||
currentSelectedElement = element;
|
||||
|
||||
// 根据元素类型显示不同的选择工具栏
|
||||
if (element is Image)
|
||||
if (IsBitmapLikeCanvasElement(element))
|
||||
{
|
||||
if (BorderPdfPageSidebar != null)
|
||||
BorderPdfPageSidebar.Visibility = element is PdfEmbeddedView ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// 显示图片选择工具栏并设置位置
|
||||
if (BorderImageSelectionControl != null)
|
||||
{
|
||||
// 计算工具栏位置
|
||||
// 计算工具栏位置(内部会同步 PDF 右侧栏位置)
|
||||
UpdateImageSelectionToolbarPosition(element);
|
||||
BorderImageSelectionControl.Visibility = Visibility.Visible;
|
||||
}
|
||||
@@ -536,11 +540,25 @@ namespace Ink_Canvas
|
||||
// 显示图片缩放选择点
|
||||
ShowImageResizeHandles(element);
|
||||
|
||||
if (element is PdfEmbeddedView pdfView)
|
||||
{
|
||||
pdfView.PageNavigationStateChanged -= SelectedPdf_PageNavigationStateChanged;
|
||||
pdfView.PageNavigationStateChanged += SelectedPdf_PageNavigationStateChanged;
|
||||
UpdatePdfSidebarFromSelectedPdf();
|
||||
UpdatePdfPageSidebarPosition(pdfView);
|
||||
}
|
||||
else
|
||||
ResetPdfSidebarToIdle();
|
||||
|
||||
// 墨迹选择工具栏通过GridInkCanvasSelectionCover的可见性来控制
|
||||
// 不需要直接设置BorderStrokeSelectionControl.Visibility
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BorderPdfPageSidebar != null)
|
||||
BorderPdfPageSidebar.Visibility = Visibility.Collapsed;
|
||||
ResetPdfSidebarToIdle();
|
||||
|
||||
// 隐藏图片选择工具栏
|
||||
if (BorderImageSelectionControl != null)
|
||||
{
|
||||
@@ -584,6 +602,15 @@ namespace Ink_Canvas
|
||||
{
|
||||
// 去除选中效果
|
||||
|
||||
if (element is PdfEmbeddedView pdfView)
|
||||
{
|
||||
pdfView.PageNavigationStateChanged -= SelectedPdf_PageNavigationStateChanged;
|
||||
}
|
||||
|
||||
if (BorderPdfPageSidebar != null)
|
||||
BorderPdfPageSidebar.Visibility = Visibility.Collapsed;
|
||||
ResetPdfSidebarToIdle();
|
||||
|
||||
// 隐藏图片选择工具栏
|
||||
if (BorderImageSelectionControl != null)
|
||||
{
|
||||
@@ -914,15 +941,24 @@ namespace Ink_Canvas
|
||||
/// - 否则使用原始尺寸
|
||||
/// - 返回创建的Image对象
|
||||
/// </remarks>
|
||||
private async Task<Image> CreateAndCompressImageAsync(string filePath)
|
||||
/// <summary>与图片选择工具栏、缩放控制点联动的画布位图类元素(普通图片或多页 PDF 嵌入)。</summary>
|
||||
private static bool IsBitmapLikeCanvasElement(FrameworkElement fe)
|
||||
{
|
||||
return fe is Image || fe is PdfEmbeddedView;
|
||||
}
|
||||
|
||||
private async Task<FrameworkElement> CreateAndCompressImageAsync(string filePath)
|
||||
{
|
||||
string fileExtension = Path.GetExtension(filePath);
|
||||
if (string.Equals(fileExtension, ".pdf", StringComparison.OrdinalIgnoreCase))
|
||||
return await CreateAndCompressImageFromPdfAsync(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);
|
||||
|
||||
@@ -965,6 +1001,43 @@ namespace Ink_Canvas
|
||||
return image;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插入完整 PDF:嵌入控件内可翻页,右下角显示页码(类似希沃白板交互)。
|
||||
/// </summary>
|
||||
private async Task<PdfEmbeddedView> CreateAndCompressImageFromPdfAsync(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
|
||||
if (!Directory.Exists(savePath))
|
||||
Directory.CreateDirectory(savePath);
|
||||
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
string newFilePath = Path.Combine(savePath, timestamp + ".pdf");
|
||||
await Task.Run(() => File.Copy(filePath, newFilePath, true));
|
||||
|
||||
uint pageCount = await PdfWinRtHelper.GetPageCountAsync(newFilePath);
|
||||
if (pageCount == 0)
|
||||
{
|
||||
ShowNotification("无法打开 PDF(可能已加密、损坏或不支持)。");
|
||||
return null;
|
||||
}
|
||||
|
||||
bool compress = isLoaded && Settings.Canvas.IsCompressPicturesUploaded;
|
||||
var view = new PdfEmbeddedView();
|
||||
await view.InitializeAsync(newFilePath, pageCount, compress);
|
||||
view.Tag = filePath;
|
||||
return view;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"插入 PDF 失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
ShowNotification($"插入 PDF 失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Media
|
||||
@@ -1453,6 +1526,9 @@ namespace Ink_Canvas
|
||||
|
||||
// 设置工具栏位置
|
||||
BorderImageSelectionControl.Margin = new Thickness(toolbarLeft, toolbarTop, 0, 0);
|
||||
|
||||
if (element is PdfEmbeddedView && BorderPdfPageSidebar?.Visibility == Visibility.Visible)
|
||||
UpdatePdfPageSidebarPosition(element);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1460,6 +1536,54 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private const double PdfPageSidebarGap = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 将 PDF 专用页码栏贴在当前所选 PDF 的右侧(画布坐标,与底部选中栏一致)。
|
||||
/// </summary>
|
||||
private void UpdatePdfPageSidebarPosition(FrameworkElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (BorderPdfPageSidebar == null || inkCanvas == null || !(element is PdfEmbeddedView))
|
||||
return;
|
||||
|
||||
Rect b = GetElementActualBounds(element);
|
||||
|
||||
BorderPdfPageSidebar.Measure(new Size(BorderPdfPageSidebar.Width, double.PositiveInfinity));
|
||||
double sidebarW = BorderPdfPageSidebar.DesiredSize.Width;
|
||||
double sidebarH = BorderPdfPageSidebar.DesiredSize.Height;
|
||||
if (sidebarW <= 0)
|
||||
sidebarW = BorderPdfPageSidebar.Width;
|
||||
if (sidebarH <= 0)
|
||||
sidebarH = BorderPdfPageSidebar.ActualHeight;
|
||||
if (sidebarH <= 0)
|
||||
sidebarH = 220;
|
||||
|
||||
double left = b.Right + PdfPageSidebarGap;
|
||||
double top = b.Top + (b.Height * 0.5) - (sidebarH * 0.5);
|
||||
|
||||
double maxLeft = Math.Max(0, inkCanvas.ActualWidth - sidebarW);
|
||||
double maxTop = Math.Max(0, inkCanvas.ActualHeight - sidebarH);
|
||||
|
||||
if (left > maxLeft)
|
||||
{
|
||||
double leftAlt = b.Left - PdfPageSidebarGap - sidebarW;
|
||||
if (leftAlt >= 0)
|
||||
left = leftAlt;
|
||||
}
|
||||
|
||||
left = Math.Max(0, Math.Min(left, maxLeft));
|
||||
top = Math.Max(0, Math.Min(top, maxTop));
|
||||
|
||||
BorderPdfPageSidebar.Margin = new Thickness(left, top, 0, 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"更新 PDF 右侧页码栏位置失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取元素的实际边界(考虑变换)
|
||||
/// </summary>
|
||||
@@ -1644,7 +1768,7 @@ namespace Ink_Canvas
|
||||
ApplyRotateTransform(currentSelectedElement, -45);
|
||||
|
||||
// 更新工具栏位置
|
||||
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(currentSelectedElement);
|
||||
}
|
||||
@@ -1678,7 +1802,7 @@ namespace Ink_Canvas
|
||||
ApplyRotateTransform(currentSelectedElement, 45);
|
||||
|
||||
// 更新工具栏位置
|
||||
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(currentSelectedElement);
|
||||
}
|
||||
@@ -1714,7 +1838,7 @@ namespace Ink_Canvas
|
||||
ApplyScaleTransform(currentSelectedElement, 0.9, elementCenter);
|
||||
|
||||
// 更新工具栏位置
|
||||
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(currentSelectedElement);
|
||||
}
|
||||
@@ -1750,7 +1874,7 @@ namespace Ink_Canvas
|
||||
ApplyScaleTransform(currentSelectedElement, 1.1, elementCenter);
|
||||
|
||||
// 更新工具栏位置
|
||||
if (currentSelectedElement is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
if (IsBitmapLikeCanvasElement(currentSelectedElement) && BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(currentSelectedElement);
|
||||
}
|
||||
@@ -1764,19 +1888,96 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetPdfSidebarToIdle()
|
||||
{
|
||||
if (TextBlockPdfSidebarPageLabel != null)
|
||||
{
|
||||
TextBlockPdfSidebarPageLabel.Text = "— / —";
|
||||
TextBlockPdfSidebarPageLabel.Opacity = 0.55;
|
||||
}
|
||||
|
||||
if (BorderPdfSidebarPagePrev != null)
|
||||
{
|
||||
BorderPdfSidebarPagePrev.Opacity = 0.35;
|
||||
BorderPdfSidebarPagePrev.IsHitTestVisible = false;
|
||||
}
|
||||
|
||||
if (BorderPdfSidebarPageNext != null)
|
||||
{
|
||||
BorderPdfSidebarPageNext.Opacity = 0.35;
|
||||
BorderPdfSidebarPageNext.IsHitTestVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePdfSidebarFromSelectedPdf()
|
||||
{
|
||||
if (currentSelectedElement is PdfEmbeddedView pdf)
|
||||
{
|
||||
if (TextBlockPdfSidebarPageLabel != null)
|
||||
{
|
||||
TextBlockPdfSidebarPageLabel.Text = pdf.PageLabelText;
|
||||
TextBlockPdfSidebarPageLabel.Opacity = 1.0;
|
||||
}
|
||||
|
||||
bool prevOk = pdf.CanGoPrevious;
|
||||
bool nextOk = pdf.CanGoNext;
|
||||
if (BorderPdfSidebarPagePrev != null)
|
||||
{
|
||||
BorderPdfSidebarPagePrev.Opacity = prevOk ? 1.0 : 0.35;
|
||||
BorderPdfSidebarPagePrev.IsHitTestVisible = prevOk;
|
||||
}
|
||||
|
||||
if (BorderPdfSidebarPageNext != null)
|
||||
{
|
||||
BorderPdfSidebarPageNext.Opacity = nextOk ? 1.0 : 0.35;
|
||||
BorderPdfSidebarPageNext.IsHitTestVisible = nextOk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectedPdf_PageNavigationStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
UpdatePdfSidebarFromSelectedPdf();
|
||||
if (currentSelectedElement != null && IsBitmapLikeCanvasElement(currentSelectedElement))
|
||||
{
|
||||
UpdateImageSelectionToolbarPosition(currentSelectedElement);
|
||||
if (ImageResizeHandlesCanvas?.Visibility == Visibility.Visible)
|
||||
UpdateImageResizeHandlesPosition(GetElementActualBounds(currentSelectedElement));
|
||||
}
|
||||
}), DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private async void BorderPdfSidebarPagePrev_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (currentSelectedElement is PdfEmbeddedView pdf && pdf.CanGoPrevious)
|
||||
await pdf.GoToPreviousPageAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"PDF 上一页失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void BorderPdfSidebarPageNext_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (currentSelectedElement is PdfEmbeddedView pdf && pdf.CanGoNext)
|
||||
await pdf.GoToNextPageAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"PDF 下一页失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理图片删除功能
|
||||
/// </summary>
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="e">事件参数</param>
|
||||
/// <remarks>
|
||||
/// - 检查当前是否有选中元素
|
||||
/// - 保存删除前的编辑模式
|
||||
/// - 记录删除历史
|
||||
/// - 从画布中移除
|
||||
/// - 清除选中状态
|
||||
/// - 包含异常处理
|
||||
/// </remarks>
|
||||
private void BorderImageDelete_MouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -1789,11 +1990,12 @@ namespace Ink_Canvas
|
||||
// 记录删除历史
|
||||
timeMachine.CommitElementRemoveHistory(currentSelectedElement);
|
||||
|
||||
var toRemove = currentSelectedElement;
|
||||
// 从画布中移除
|
||||
inkCanvas.Children.Remove(currentSelectedElement);
|
||||
inkCanvas.Children.Remove(toRemove);
|
||||
|
||||
// 清除选中状态
|
||||
UnselectElement(currentSelectedElement);
|
||||
UnselectElement(toRemove);
|
||||
currentSelectedElement = null;
|
||||
|
||||
// 恢复到删除前的编辑模式
|
||||
@@ -2029,7 +2231,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
if (currentSelectedElement is Image image && sender is Ellipse ellipse)
|
||||
if (IsBitmapLikeCanvasElement(currentSelectedElement) && sender is Ellipse ellipse)
|
||||
{
|
||||
isResizingImage = true;
|
||||
imageResizeStartPoint = e.GetPosition(inkCanvas);
|
||||
@@ -2072,10 +2274,10 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isResizingImage && currentSelectedElement is Image image && sender is Ellipse ellipse)
|
||||
if (isResizingImage && IsBitmapLikeCanvasElement(currentSelectedElement) && sender is Ellipse ellipse)
|
||||
{
|
||||
var currentPoint = e.GetPosition(inkCanvas);
|
||||
ResizeImageByHandle(image, imageResizeStartPoint, currentPoint, activeResizeHandle);
|
||||
ResizeImageByHandle(currentSelectedElement, imageResizeStartPoint, currentPoint, activeResizeHandle);
|
||||
imageResizeStartPoint = currentPoint;
|
||||
e.Handled = true;
|
||||
}
|
||||
@@ -2087,11 +2289,11 @@ namespace Ink_Canvas
|
||||
}
|
||||
|
||||
// 根据控制点缩放图片
|
||||
private void ResizeImageByHandle(Image image, Point startPoint, Point currentPoint, string handleName)
|
||||
private void ResizeImageByHandle(FrameworkElement element, Point startPoint, Point currentPoint, string handleName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (image.RenderTransform is TransformGroup transformGroup)
|
||||
if (element.RenderTransform is TransformGroup transformGroup)
|
||||
{
|
||||
var scaleTransform = transformGroup.Children.OfType<ScaleTransform>().FirstOrDefault();
|
||||
var translateTransform = transformGroup.Children.OfType<TranslateTransform>().FirstOrDefault();
|
||||
@@ -2099,7 +2301,7 @@ namespace Ink_Canvas
|
||||
if (scaleTransform == null || translateTransform == null) return;
|
||||
|
||||
// 获取图片的当前边界
|
||||
Rect currentBounds = GetElementActualBounds(image);
|
||||
Rect currentBounds = GetElementActualBounds(element);
|
||||
double deltaX = currentPoint.X - startPoint.X;
|
||||
double deltaY = currentPoint.Y - startPoint.Y;
|
||||
|
||||
@@ -2160,7 +2362,10 @@ namespace Ink_Canvas
|
||||
translateTransform.Y += translateY;
|
||||
|
||||
// 更新选择点位置
|
||||
UpdateImageResizeHandlesPosition(GetElementActualBounds(image));
|
||||
UpdateImageResizeHandlesPosition(GetElementActualBounds(element));
|
||||
|
||||
if (BorderImageSelectionControl?.Visibility == Visibility.Visible)
|
||||
UpdateImageSelectionToolbarPosition(element);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ink_Canvas.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Modern;
|
||||
using System;
|
||||
@@ -3774,32 +3775,29 @@ namespace Ink_Canvas
|
||||
// Open file dialog to select image
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
|
||||
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = dialog.FileName;
|
||||
Image image = await CreateAndCompressImageAsync(filePath);
|
||||
if (image != null)
|
||||
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
|
||||
if (element != null)
|
||||
{
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
element.Name = timestamp;
|
||||
|
||||
// 初始化TransformGroup
|
||||
if (image is FrameworkElement element)
|
||||
{
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(0, 0));
|
||||
transformGroup.Children.Add(new RotateTransform(0));
|
||||
element.RenderTransform = transformGroup;
|
||||
}
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(0, 0));
|
||||
transformGroup.Children.Add(new RotateTransform(0));
|
||||
element.RenderTransform = transformGroup;
|
||||
|
||||
CenterAndScaleElement(image);
|
||||
CenterAndScaleElement(element);
|
||||
|
||||
// 设置图片属性,避免被InkCanvas选择系统处理
|
||||
image.IsHitTestVisible = true;
|
||||
image.Focusable = false;
|
||||
element.IsHitTestVisible = true;
|
||||
element.Focusable = false;
|
||||
|
||||
// 初始化InkCanvas选择设置
|
||||
if (inkCanvas != null)
|
||||
@@ -3810,29 +3808,25 @@ namespace Ink_Canvas
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
|
||||
inkCanvas.Children.Add(image);
|
||||
inkCanvas.Children.Add(element);
|
||||
|
||||
// 绑定事件处理器
|
||||
if (image is FrameworkElement elementForEvents)
|
||||
{
|
||||
// 鼠标事件
|
||||
elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
|
||||
elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
|
||||
elementForEvents.MouseMove += Element_MouseMove;
|
||||
elementForEvents.MouseWheel += Element_MouseWheel;
|
||||
element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
|
||||
element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
|
||||
element.MouseMove += Element_MouseMove;
|
||||
element.MouseWheel += Element_MouseWheel;
|
||||
|
||||
// 触摸事件
|
||||
elementForEvents.TouchDown += Element_TouchDown;
|
||||
elementForEvents.TouchUp += Element_TouchUp;
|
||||
elementForEvents.IsManipulationEnabled = true;
|
||||
elementForEvents.ManipulationDelta += Element_ManipulationDelta;
|
||||
elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
|
||||
// 触摸事件
|
||||
element.TouchDown += Element_TouchDown;
|
||||
element.TouchUp += Element_TouchUp;
|
||||
element.IsManipulationEnabled = true;
|
||||
element.ManipulationDelta += Element_ManipulationDelta;
|
||||
element.ManipulationCompleted += Element_ManipulationCompleted;
|
||||
|
||||
// 设置光标
|
||||
elementForEvents.Cursor = Cursors.Hand;
|
||||
}
|
||||
// 设置光标
|
||||
element.Cursor = Cursors.Hand;
|
||||
|
||||
timeMachine.CommitElementInsertHistory(image);
|
||||
timeMachine.CommitElementInsertHistory(element);
|
||||
|
||||
// 插入图片后切换到选择模式并刷新浮动栏高光显示
|
||||
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
||||
@@ -3847,32 +3841,29 @@ namespace Ink_Canvas
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
|
||||
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = dialog.FileName;
|
||||
Image image = await CreateAndCompressImageAsync(filePath);
|
||||
if (image != null)
|
||||
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
|
||||
if (element != null)
|
||||
{
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
element.Name = timestamp;
|
||||
|
||||
// 初始化TransformGroup
|
||||
if (image is FrameworkElement element)
|
||||
{
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(0, 0));
|
||||
transformGroup.Children.Add(new RotateTransform(0));
|
||||
element.RenderTransform = transformGroup;
|
||||
}
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(0, 0));
|
||||
transformGroup.Children.Add(new RotateTransform(0));
|
||||
element.RenderTransform = transformGroup;
|
||||
|
||||
CenterAndScaleElement(image);
|
||||
CenterAndScaleElement(element);
|
||||
|
||||
// 设置图片属性,避免被InkCanvas选择系统处理
|
||||
image.IsHitTestVisible = true;
|
||||
image.Focusable = false;
|
||||
element.IsHitTestVisible = true;
|
||||
element.Focusable = false;
|
||||
|
||||
// 初始化InkCanvas选择设置
|
||||
if (inkCanvas != null)
|
||||
@@ -3883,29 +3874,25 @@ namespace Ink_Canvas
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
|
||||
inkCanvas.Children.Add(image);
|
||||
inkCanvas.Children.Add(element);
|
||||
|
||||
// 绑定事件处理器
|
||||
if (image is FrameworkElement elementForEvents)
|
||||
{
|
||||
// 鼠标事件
|
||||
elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
|
||||
elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
|
||||
elementForEvents.MouseMove += Element_MouseMove;
|
||||
elementForEvents.MouseWheel += Element_MouseWheel;
|
||||
element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
|
||||
element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
|
||||
element.MouseMove += Element_MouseMove;
|
||||
element.MouseWheel += Element_MouseWheel;
|
||||
|
||||
// 触摸事件
|
||||
elementForEvents.TouchDown += Element_TouchDown;
|
||||
elementForEvents.TouchUp += Element_TouchUp;
|
||||
elementForEvents.IsManipulationEnabled = true;
|
||||
elementForEvents.ManipulationDelta += Element_ManipulationDelta;
|
||||
elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
|
||||
// 触摸事件
|
||||
element.TouchDown += Element_TouchDown;
|
||||
element.TouchUp += Element_TouchUp;
|
||||
element.IsManipulationEnabled = true;
|
||||
element.ManipulationDelta += Element_ManipulationDelta;
|
||||
element.ManipulationCompleted += Element_ManipulationCompleted;
|
||||
|
||||
// 设置光标
|
||||
elementForEvents.Cursor = Cursors.Hand;
|
||||
}
|
||||
// 设置光标
|
||||
element.Cursor = Cursors.Hand;
|
||||
|
||||
timeMachine.CommitElementInsertHistory(image);
|
||||
timeMachine.CommitElementInsertHistory(element);
|
||||
|
||||
// 插入图片后切换到选择模式并刷新浮动栏高光显示
|
||||
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
||||
@@ -3920,32 +3907,29 @@ namespace Ink_Canvas
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
|
||||
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
string filePath = dialog.FileName;
|
||||
Image image = await CreateAndCompressImageAsync(filePath); // 补充image定义
|
||||
if (image != null)
|
||||
FrameworkElement element = await CreateAndCompressImageAsync(filePath);
|
||||
if (element != null)
|
||||
{
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
element.Name = timestamp;
|
||||
|
||||
// 初始化TransformGroup
|
||||
if (image is FrameworkElement element)
|
||||
{
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(0, 0));
|
||||
transformGroup.Children.Add(new RotateTransform(0));
|
||||
element.RenderTransform = transformGroup;
|
||||
}
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(0, 0));
|
||||
transformGroup.Children.Add(new RotateTransform(0));
|
||||
element.RenderTransform = transformGroup;
|
||||
|
||||
CenterAndScaleElement(image);
|
||||
CenterAndScaleElement(element);
|
||||
|
||||
// 设置图片属性,避免被InkCanvas选择系统处理
|
||||
image.IsHitTestVisible = true;
|
||||
image.Focusable = false;
|
||||
element.IsHitTestVisible = true;
|
||||
element.Focusable = false;
|
||||
|
||||
// 初始化InkCanvas选择设置
|
||||
if (inkCanvas != null)
|
||||
@@ -3956,29 +3940,25 @@ namespace Ink_Canvas
|
||||
inkCanvas.EditingMode = InkCanvasEditingMode.None;
|
||||
}
|
||||
|
||||
inkCanvas.Children.Add(image);
|
||||
inkCanvas.Children.Add(element);
|
||||
|
||||
// 绑定事件处理器
|
||||
if (image is FrameworkElement elementForEvents)
|
||||
{
|
||||
// 鼠标事件
|
||||
elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
|
||||
elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
|
||||
elementForEvents.MouseMove += Element_MouseMove;
|
||||
elementForEvents.MouseWheel += Element_MouseWheel;
|
||||
element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
|
||||
element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
|
||||
element.MouseMove += Element_MouseMove;
|
||||
element.MouseWheel += Element_MouseWheel;
|
||||
|
||||
// 触摸事件
|
||||
elementForEvents.TouchDown += Element_TouchDown;
|
||||
elementForEvents.TouchUp += Element_TouchUp;
|
||||
elementForEvents.IsManipulationEnabled = true;
|
||||
elementForEvents.ManipulationDelta += Element_ManipulationDelta;
|
||||
elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
|
||||
// 触摸事件
|
||||
element.TouchDown += Element_TouchDown;
|
||||
element.TouchUp += Element_TouchUp;
|
||||
element.IsManipulationEnabled = true;
|
||||
element.ManipulationDelta += Element_ManipulationDelta;
|
||||
element.ManipulationCompleted += Element_ManipulationCompleted;
|
||||
|
||||
// 设置光标
|
||||
elementForEvents.Cursor = Cursors.Hand;
|
||||
}
|
||||
// 设置光标
|
||||
element.Cursor = Cursors.Hand;
|
||||
|
||||
timeMachine.CommitElementInsertHistory(image);
|
||||
timeMachine.CommitElementInsertHistory(element);
|
||||
|
||||
// 插入图片后切换到选择模式并刷新浮动栏高光显示
|
||||
SetCurrentToolMode(InkCanvasEditingMode.Select);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ink_Canvas.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
using iNKORE.UI.WPF.Controls;
|
||||
using System;
|
||||
@@ -610,7 +611,7 @@ namespace Ink_Canvas
|
||||
|
||||
// 检查是否有图片元素被选中(通过InkCanvas的选中元素)
|
||||
var selectedElements = inkCanvas.GetSelectedElements();
|
||||
bool hasImageElement = selectedElements.Any(element => element is Image);
|
||||
bool hasImageElement = selectedElements.Any(element => element is Image || element is PdfEmbeddedView);
|
||||
|
||||
// 如果有图片元素被选中,不显示选择框
|
||||
if (hasImageElement)
|
||||
@@ -621,7 +622,7 @@ namespace Ink_Canvas
|
||||
}
|
||||
|
||||
// 检查是否有图片元素被选中(通过currentSelectedElement)
|
||||
if (currentSelectedElement != null && currentSelectedElement is Image)
|
||||
if (currentSelectedElement != null && (currentSelectedElement is Image || currentSelectedElement is PdfEmbeddedView))
|
||||
{
|
||||
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
|
||||
HideSelectionDisplay();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ink_Canvas.Controls;
|
||||
using Ink_Canvas.Helpers;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
@@ -101,7 +102,7 @@ namespace Ink_Canvas.Windows
|
||||
private double _collapsedOffset = 200; // 折叠时的偏移量(隐藏内容区域)
|
||||
private MainWindow _mainWindow;
|
||||
|
||||
private Dictionary<System.Windows.Controls.Image, int> _pptImages = new Dictionary<System.Windows.Controls.Image, int>();
|
||||
private Dictionary<FrameworkElement, int> _pptImages = new Dictionary<FrameworkElement, int>();
|
||||
|
||||
private Dictionary<int, List<string>> _pptImagePaths = new Dictionary<int, List<string>>();
|
||||
|
||||
@@ -183,9 +184,9 @@ namespace Ink_Canvas.Windows
|
||||
{
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
if (item is System.Windows.Controls.Image image)
|
||||
if (item is FrameworkElement fe && _pptImages.ContainsKey(fe))
|
||||
{
|
||||
RemoveImageFromPPT(image);
|
||||
RemoveImageFromPPT(fe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,20 +197,20 @@ namespace Ink_Canvas.Windows
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveImageFromPPT(System.Windows.Controls.Image image)
|
||||
private void RemoveImageFromPPT(FrameworkElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (image == null) return;
|
||||
if (element == null) return;
|
||||
|
||||
if (_pptImages.ContainsKey(image))
|
||||
if (_pptImages.ContainsKey(element))
|
||||
{
|
||||
int slideNumber = _pptImages[image];
|
||||
_pptImages.Remove(image);
|
||||
int slideNumber = _pptImages[element];
|
||||
_pptImages.Remove(element);
|
||||
|
||||
if (_pptImagePaths.ContainsKey(slideNumber))
|
||||
{
|
||||
string imagePath = image.Tag as string;
|
||||
string imagePath = element.Tag as string;
|
||||
if (!string.IsNullOrEmpty(imagePath) && _pptImagePaths[slideNumber].Contains(imagePath))
|
||||
{
|
||||
_pptImagePaths[slideNumber].Remove(imagePath);
|
||||
@@ -929,7 +930,7 @@ namespace Ink_Canvas.Windows
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
|
||||
Filter = "图片与 PDF|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.pdf|图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|PDF|*.pdf"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
@@ -941,14 +942,14 @@ namespace Ink_Canvas.Windows
|
||||
|
||||
if (createImageMethod != null)
|
||||
{
|
||||
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { filePath }) as System.Threading.Tasks.Task<System.Windows.Controls.Image>;
|
||||
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { filePath }) as System.Threading.Tasks.Task<FrameworkElement>;
|
||||
if (imageTask != null)
|
||||
{
|
||||
var image = await imageTask;
|
||||
if (image != null)
|
||||
var inserted = await imageTask;
|
||||
if (inserted != null)
|
||||
{
|
||||
image.Tag = filePath;
|
||||
await InsertImageToMainWindow(image, filePath);
|
||||
inserted.Tag = filePath;
|
||||
await InsertImageToMainWindow(inserted, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1185,9 +1186,9 @@ namespace Ink_Canvas.Windows
|
||||
}, DispatcherPriority.Normal);
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task InsertImageToMainWindow(System.Windows.Controls.Image image, string originalFilePath = null, bool saveToJson = true)
|
||||
private async System.Threading.Tasks.Task InsertImageToMainWindow(FrameworkElement element, string originalFilePath = null, bool saveToJson = true)
|
||||
{
|
||||
if (_mainWindow == null || image == null) return;
|
||||
if (_mainWindow == null || element == null) return;
|
||||
|
||||
// 确保在UI线程上执行
|
||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
@@ -1196,11 +1197,11 @@ namespace Ink_Canvas.Windows
|
||||
{
|
||||
// 生成唯一名称
|
||||
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
|
||||
image.Name = timestamp;
|
||||
element.Name = timestamp;
|
||||
|
||||
// 设置图片属性
|
||||
image.IsHitTestVisible = true;
|
||||
image.Focusable = false;
|
||||
element.IsHitTestVisible = true;
|
||||
element.Focusable = false;
|
||||
|
||||
System.Windows.Controls.InkCanvas inkCanvas = null;
|
||||
var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas",
|
||||
@@ -1274,7 +1275,7 @@ namespace Ink_Canvas.Windows
|
||||
// 如果在PPT模式下,记录图片和页面编号的关联,并保存图片路径
|
||||
if (currentSlideNumber > 0 && !string.IsNullOrEmpty(originalFilePath) && saveToJson)
|
||||
{
|
||||
_pptImages[image] = currentSlideNumber;
|
||||
_pptImages[element] = currentSlideNumber;
|
||||
|
||||
// 添加到页面图片路径列表
|
||||
if (!_pptImagePaths.ContainsKey(currentSlideNumber))
|
||||
@@ -1289,14 +1290,14 @@ namespace Ink_Canvas.Windows
|
||||
else if (currentSlideNumber > 0)
|
||||
{
|
||||
// 即使不保存到JSON,也要记录图片和页面编号的关联(用于翻页显示/隐藏)
|
||||
_pptImages[image] = currentSlideNumber;
|
||||
_pptImages[element] = currentSlideNumber;
|
||||
}
|
||||
|
||||
// 先添加到画布(与MainWindow的实现保持一致)
|
||||
inkCanvas.Children.Add(image);
|
||||
inkCanvas.Children.Add(element);
|
||||
|
||||
// 等待图片加载完成后再进行后续处理
|
||||
image.Loaded += (s, args) =>
|
||||
element.Loaded += (s, args) =>
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
@@ -1305,19 +1306,19 @@ namespace Ink_Canvas.Windows
|
||||
// 初始化TransformGroup
|
||||
var initializeTransformMethod = _mainWindow.GetType().GetMethod("InitializeElementTransform",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
initializeTransformMethod?.Invoke(_mainWindow, new object[] { image });
|
||||
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[] { image });
|
||||
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[] { image });
|
||||
bindEventsMethod?.Invoke(_mainWindow, new object[] { element });
|
||||
|
||||
LogHelper.WriteLogToFile($"图片插入完成: {image.Name}, PPT页面: {currentSlideNumber}");
|
||||
LogHelper.WriteLogToFile($"图片插入完成: {element.Name}, PPT页面: {currentSlideNumber}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1333,7 +1334,7 @@ namespace Ink_Canvas.Windows
|
||||
if (timeMachine != null)
|
||||
{
|
||||
var commitMethod = timeMachine.GetType().GetMethod("CommitElementInsertHistory");
|
||||
commitMethod?.Invoke(timeMachine, new object[] { image });
|
||||
commitMethod?.Invoke(timeMachine, new object[] { element });
|
||||
}
|
||||
|
||||
// 切换到选择模式
|
||||
@@ -1699,12 +1700,16 @@ namespace Ink_Canvas.Windows
|
||||
|
||||
// 检查已存在的图片路径(通过Tag)
|
||||
var existingImagePaths = new HashSet<string>();
|
||||
foreach (System.Windows.Controls.Image existingImage in inkCanvas.Children.OfType<System.Windows.Controls.Image>())
|
||||
foreach (var existingImage in inkCanvas.Children.OfType<System.Windows.Controls.Image>())
|
||||
{
|
||||
if (existingImage.Tag is string tagPath && !string.IsNullOrEmpty(tagPath))
|
||||
{
|
||||
existingImagePaths.Add(tagPath);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var existingPdf in inkCanvas.Children.OfType<PdfEmbeddedView>())
|
||||
{
|
||||
if (existingPdf.Tag is string tagPath && !string.IsNullOrEmpty(tagPath))
|
||||
existingImagePaths.Add(tagPath);
|
||||
}
|
||||
|
||||
// 使用反射调用MainWindow的CreateAndCompressImageAsync方法
|
||||
@@ -1733,17 +1738,17 @@ namespace Ink_Canvas.Windows
|
||||
continue;
|
||||
}
|
||||
|
||||
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { imagePath }) as System.Threading.Tasks.Task<System.Windows.Controls.Image>;
|
||||
var imageTask = createImageMethod.Invoke(_mainWindow, new object[] { imagePath }) as System.Threading.Tasks.Task<FrameworkElement>;
|
||||
if (imageTask != null)
|
||||
{
|
||||
var image = await imageTask;
|
||||
if (image != null)
|
||||
var inserted = await imageTask;
|
||||
if (inserted != null)
|
||||
{
|
||||
// 保存原始文件路径到Tag
|
||||
image.Tag = imagePath;
|
||||
inserted.Tag = imagePath;
|
||||
|
||||
// 插入图片(不保存路径,因为已经存在)
|
||||
await InsertImageToMainWindow(image, imagePath, false);
|
||||
await InsertImageToMainWindow(inserted, imagePath, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +168,12 @@
|
||||
"System.Text.Json": "8.0.5"
|
||||
}
|
||||
},
|
||||
"System.Runtime.WindowsRuntime": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.7.0, )",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "RQxUkf37fp7MSWbOfKRjUjyudyfZb2u79YY5i1s+d7vuD80A7kmr2YfefO0JprQUhanxSm8bhXigCVfX2kEh+w=="
|
||||
},
|
||||
"WebDav.Client": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.0, )",
|
||||
@@ -247,11 +253,6 @@
|
||||
"System.Memory": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
@@ -290,11 +291,6 @@
|
||||
"resolved": "4.3.0",
|
||||
"contentHash": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA=="
|
||||
},
|
||||
"System.Runtime.WindowsRuntime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.0",
|
||||
"contentHash": "IWrs1TmbxP65ZZjIglNyvDkFNoV5q2Pofg5WO7I8RKQOpLdFprQSh3xesOoClBqR4JHr4nEB1Xk1MqLPW1jPuQ=="
|
||||
},
|
||||
"System.Runtime.WindowsRuntime.UI.Xaml": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.0",
|
||||
@@ -352,19 +348,6 @@
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
|
||||
},
|
||||
"inkcanvas.pluginhost": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"InkCanvas.PluginSdk": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"inkcanvas.pluginsdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "[13.0.3, )",
|
||||
"System.ComponentModel.Annotations": "[5.0.0, )"
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.7.2/win": {
|
||||
|
||||
Reference in New Issue
Block a user