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

WPF截图应用:矩形坐标转屏幕坐标问题求助

问题原因分析

你的截图区域偏移/仅捕获部分内容,核心问题有两个:

  1. WPF与GDI+的坐标体系不匹配:WPF使用设备无关像素(DIP),而System.Drawing.CopyFromScreen使用物理屏幕像素,当系统启用高DPI缩放(如125%、150%)时,两者坐标会出现偏移。
  2. 坐标转换对象错误:你用Window的PointToScreen方法转换Canvas内部坐标,虽然Canvas占满窗口,但DPI缩放或窗口布局差异会导致转换后的屏幕坐标不准确。
解决方案

1. 获取DPI缩放比例

添加方法获取当前窗口的DPI缩放因子,用于将WPF的DIP坐标转换为物理像素:

private double GetDpiScale()
{
    var source = PresentationSource.FromVisual(this);
    if (source?.CompositionTarget != null)
    {
        return source.CompositionTarget.TransformToDevice.M11;
    }
    return 1.0;
}

2. 修正坐标获取与转换逻辑

直接在鼠标事件中获取屏幕坐标(而非Canvas内部坐标),再转换为Canvas坐标用于绘制选区,最后通过DPI缩放得到物理像素坐标用于截图:

完整修正后的C#代码

using System;
using System.Drawing;
using System.Windows;
using System.Windows.Input;

namespace ScreenCapture
{
    public partial class MainWindow : Window
    {
        private System.Windows.Shapes.Rectangle DragRectangle = null;
        private System.Windows.Point StartPoint, LastPoint;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape) { this.Close(); e.Handled = true; }
        }

        private void canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // 直接获取屏幕坐标
            StartPoint = Mouse.GetPosition(null);
            LastPoint = StartPoint;

            DragRectangle = new System.Windows.Shapes.Rectangle
            {
                Width = 1,
                Height = 1,
                Stroke = System.Windows.Media.Brushes.Red,
                StrokeThickness = 1,
                Cursor = Cursors.Cross
            };

            canvas.Children.Add(DragRectangle);
            // 转换为Canvas内部坐标,用于绘制选区
            var canvasStart = canvas.PointFromScreen(StartPoint);
            Canvas.SetLeft(DragRectangle, canvasStart.X);
            Canvas.SetTop(DragRectangle, canvasStart.Y);

            canvas.MouseMove += canvas_MouseMove;
            canvas.MouseUp += canvas_MouseUp;
            canvas.CaptureMouse();
        }

        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (DragRectangle == null) return;

            LastPoint = Mouse.GetPosition(null);
            var canvasLast = canvas.PointFromScreen(LastPoint);
            var canvasStart = canvas.PointFromScreen(StartPoint);

            // 更新选区尺寸与位置
            DragRectangle.Width = Math.Abs(canvasLast.X - canvasStart.X);
            DragRectangle.Height = Math.Abs(canvasLast.Y - canvasStart.Y);
            Canvas.SetLeft(DragRectangle, Math.Min(canvasLast.X, canvasStart.X));
            Canvas.SetTop(DragRectangle, Math.Min(canvasLast.Y, canvasStart.Y));
        }

        private void canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (DragRectangle == null) return;

            canvas.ReleaseMouseCapture();

            double dpiScale = GetDpiScale();
            // 计算物理屏幕像素坐标
            int x = (int)(Math.Min(LastPoint.X, StartPoint.X) * dpiScale);
            int y = (int)(Math.Min(LastPoint.Y, StartPoint.Y) * dpiScale);
            int width = (int)(Math.Abs(LastPoint.X - StartPoint.X) * dpiScale);
            int height = (int)(Math.Abs(LastPoint.Y - StartPoint.Y) * dpiScale);

            if (width > 0 && height > 0)
            {
                CaptureScreen(x, y, width, height);
            }

            this.Close();
        }

        private double GetDpiScale()
        {
            var source = PresentationSource.FromVisual(this);
            if (source?.CompositionTarget != null)
            {
                return source.CompositionTarget.TransformToDevice.M11;
            }
            return 1.0;
        }

        private void CaptureScreen(int x, int y, int width, int height)
        {
            using (Bitmap bitmap = new Bitmap(width, height))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    // 直接使用物理像素坐标捕获屏幕
                    g.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height));
                }

                string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
                string filePath = System.IO.Path.Combine(desktopPath, $"Screenshot_{DateTime.Now:yyyyMMdd_HHmmss}.png");
                bitmap.Save(filePath);
                MessageBox.Show($"Screenshot saved to {filePath}");
            }
        }
    }
}

3. XAML代码无需修改

保持你原有的XAML布局即可,因为窗口已经设置为全屏透明,Canvas占满整个窗口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:38:11