You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WPF中InkCanvas笔触保存加载时的缩放与偏移计算方案

解决WPF InkCanvas Uniform背景图下笔触跨尺寸适配问题

核心问题在于你直接用InkCanvas的整体宽高计算缩放比例,但Uniform拉伸模式下图片会保持比例居中显示,存在留白区域,笔触坐标是相对于图片而非整个InkCanvas的,所以必须基于图片实际显示区域来做坐标转换。

步骤1:计算图片在InkCanvas中的实际显示区域

先实现一个工具方法,根据图片原始尺寸和InkCanvas当前尺寸,算出Uniform模式下图片的显示位置、宽高:

private Rect GetImageDisplayRect(InkCanvas inkCanvas, ImageBrush imageBrush)
{
    if (imageBrush.ImageSource == null) return Rect.Empty;

    var imgOriginalWidth = imageBrush.ImageSource.PixelWidth;
    var imgOriginalHeight = imageBrush.ImageSource.PixelHeight;
    var canvasWidth = inkCanvas.ActualWidth;
    var canvasHeight = inkCanvas.ActualHeight;

    // 计算Uniform缩放比例(取宽高比例的较小值,保证图片完整显示)
    double scaleRatio = Math.Min(canvasWidth / imgOriginalWidth, canvasHeight / imgOriginalHeight);
    var imgDisplayWidth = imgOriginalWidth * scaleRatio;
    var imgDisplayHeight = imgOriginalHeight * scaleRatio;

    // 计算图片居中的偏移量
    var offsetX = (canvasWidth - imgDisplayWidth) / 2;
    var offsetY = (canvasHeight - imgDisplayHeight) / 2;

    return new Rect(offsetX, offsetY, imgDisplayWidth, imgDisplayHeight);
}

步骤2:保存笔触时存储归一化坐标

不要直接保存InkCanvas的绝对坐标,而是转换为相对于图片原始尺寸的归一化坐标,确保跨尺寸加载时能准确还原:

// 示例:遍历所有笔触,转换并保存
var imageRect = GetImageDisplayRect(CurrentPage, CurrentPageImage);
var imgOriginalWidth = CurrentPageImage.ImageSource.PixelWidth;
var imgOriginalHeight = CurrentPageImage.ImageSource.PixelHeight;

List<List<Point>> savedStrokeData = new List<List<Point>>();
foreach (Stroke stroke in CurrentPage.Strokes)
{
    List<Point> normalizedPoints = new List<Point>();
    foreach (StylusPoint sp in stroke.StylusPoints)
    {
        // 1. 从InkCanvas坐标转换为图片显示区域的相对坐标
        double relativeX = (sp.X - imageRect.X) / imageRect.Width;
        double relativeY = (sp.Y - imageRect.Y) / imageRect.Height;
        // 2. 转换为图片原始尺寸下的绝对坐标(或直接保存比例值)
        double normX = relativeX * imgOriginalWidth;
        double normY = relativeY * imgOriginalHeight;
        normalizedPoints.Add(new Point(normX, normY));
    }
    savedStrokeData.Add(normalizedPoints);
}
// 将savedStrokeData保存到文件/数据库

步骤3:加载笔触时转换为当前尺寸坐标

根据当前InkCanvas中图片的显示区域,把归一化坐标还原为当前的绝对坐标:

// 示例:从存储读取数据并转换为笔触
var currentImageRect = GetImageDisplayRect(CurrentPage, CurrentPageImage);
var imgOriginalWidth = CurrentPageImage.ImageSource.PixelWidth;
var imgOriginalHeight = CurrentPageImage.ImageSource.PixelHeight;

foreach (List<Point> savedPoints in savedStrokeData)
{
    StylusPointCollection pointCollection = new StylusPointCollection();
    foreach (Point p in savedPoints)
    {
        // 1. 从图片原始尺寸坐标转换为相对比例
        double relativeX = p.X / imgOriginalWidth;
        double relativeY = p.Y / imgOriginalHeight;
        // 2. 转换为当前InkCanvas的绝对坐标
        double canvasX = currentImageRect.X + relativeX * currentImageRect.Width;
        double canvasY = currentImageRect.Y + relativeY * currentImageRect.Height;
        pointCollection.Add(new StylusPoint(canvasX, canvasY));
    }
    CurrentPage.Strokes.Add(new Stroke(pointCollection));
}

额外注意事项

  • 当InkCanvas尺寸变化(窗口 resize、切换全屏)时,若已加载笔触,需要重新执行转换逻辑更新坐标(或者每次加载时实时转换,避免存储中间状态)。
  • 不要依赖InkCanvas的RenderTransform做缩放,这会导致笔触和背景图的缩放逻辑不一致,应该始终基于背景图片的实际显示区域做坐标转换。

内容的提问来源于stack exchange,提问作者HoneyBee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 10:07:15