【问题标题】:Selecting a Screenshot isn't accurate in C# and WPF在 C# 和 WPF 中选择屏幕截图不准确
【发布时间】:2015-09-16 19:33:25
【问题描述】:

我制作了一个程序,用于从屏幕上的选定区域拍摄屏幕截图,但它不准确,我不知道为什么。这可能是因为鼠标坐标,但我不知道我做错了什么。也许你们中的一些人可以弄清楚。屏幕截图始终处于关闭状态,它会捕获实际选择上方的屏幕区域,因此会“剪切”选择的下部。这是我的代码:

     public  partial class Selektiranje : Window
    {
        public double x;
        public double y;
        public double width;
        public double height;
        public bool isMouseDown = false;


        public Selektiranje()
        {
            InitializeComponent();
        }

       private void Window_MouseDown(object sender, MouseButtonEventArgs e) 
        {
            isMouseDown = true;
            x = e.GetPosition(null).X;                                                  //Selekcija Screenshota
            y = e.GetPosition(null).Y;
        }

        public  void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (this.isMouseDown)
            {
                double curx = e.GetPosition(null).X;
                double cury = e.GetPosition(null).Y;

                System.Windows.Shapes.Rectangle r = new  , System.Windows.Shapes.Rectangle();
                SolidColorBrush brush = new SolidColorBrush(Colors.White);
                r.Stroke = brush;
                r.Fill = brush;
                r.StrokeThickness = 1;
                r.Width = Math.Abs(curx - x);
                r.Height = Math.Abs(cury - y);
                selekt.Children.Clear();
                selekt.Children.Add(r);
                Canvas.SetLeft(r, x);
                Canvas.SetTop(r, y);
                if (e.LeftButton == MouseButtonState.Released)
                {
                    selekt.Children.Clear();
                    width = e.GetPosition(null).X - x;
                    height = e.GetPosition(null).Y - y;
                    this.CaptureScreen(x, y, width, height);
                    this.x = this.y = 0;
                    this.isMouseDown = false;
                    this.Close();
                }
            }
        }
        public void CaptureScreen(double x, double y, double width, double height)
        {
            int ix, iy, iw, ih;
            ix = Convert.ToInt32(x);
            iy = Convert.ToInt32(y);
            iw = Convert.ToInt32(width);
            ih = Convert.ToInt32(height);
            Bitmap slika = new Bitmap(iw, ih,       System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            Graphics g = Graphics.FromImage(slika);
            g.CopyFromScreen(ix, iy, 0, 0,new System.Drawing.Size(iw, ih),CopyPixelOperation.SourceCopy);


 public void SaveScreen(double x, double y, double width, double height)
        {
            int ix, iy, iw, ih;
            ix = Convert.ToInt32(x);
            iy = Convert.ToInt32(y);
            iw = Convert.ToInt32(width);
            ih = Convert.ToInt32(height);
            try
            {
                Bitmap slika = new Bitmap(iw, ih);
                Graphics gr1 = Graphics.FromImage(slika);
                IntPtr dc1 = gr1.GetHdc();
                IntPtr dc2 = NativeMethods.GetWindowDC(NativeMethods.GetForegroundWindow());
                NativeMethods.BitBlt(dc1, ix, iy, iw, ih, dc2, ix, iy, 13369376);
                gr1.ReleaseHdc(dc1);
                System.Windows.Forms.SaveFileDialog dlg = new System.Windows.Forms.SaveFileDialog();
                dlg.DefaultExt = "png";
                dlg.Filter = "Png Files|*.png";
                DialogResult res = dlg.ShowDialog();
                if (res == System.Windows.Forms.DialogResult.OK)
                    slika.Save(dlg.FileName, ImageFormat.Png);

            }
            catch 
            {

            }

        }
        internal class NativeMethods
        {

            [DllImport("user32.dll")]
            public extern static IntPtr GetDesktopWindow();
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowDC(IntPtr hwnd);
            [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();
        [DllImport("gdi32.dll")]
        public static extern UInt64 BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, System.Int32 dwRop);

    }

}

}

【问题讨论】:

    标签: c# .net wpf winforms


    【解决方案1】:

    Window_MouseDown()Window_MouseMove() 中需要 Cursor.Position (MSDN),它返回绝对鼠标坐标。

       private void Window_MouseDown(object sender, MouseButtonEventArgs e) 
        {
            isMouseDown = true;
            x = Cursor.Position.X;
            y = Cursor.Position.Y;
        }
    

    下面的完整工作解决方案的代码(对我而言)。我删除了不必要的代码,例如绘制矩形。另外,我在截屏之前隐藏了表单。您还可以将表单的背景颜色设置为特定值,并将透明度键设置为相同的值或其他内容。

    但基本概念仍然存在:使用绝对屏幕坐标。

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Windows.Controls;
    using System.Windows.Forms;
    using System.Windows.Media;
    using PixelFormat = System.Drawing.Imaging.PixelFormat;
    using Rectangle = System.Windows.Shapes.Rectangle;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public double x;
            public double y;
            public double width;
            public double height;
            public bool isMouseDown;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            public void SaveScreen(double x, double y, double width, double height)
            {
                var ix = Convert.ToInt32(x);
                var iy = Convert.ToInt32(y);
                var iw = Convert.ToInt32(width);
                var ih = Convert.ToInt32(height);
                try
                {
                    var slika = new Bitmap(iw, ih, PixelFormat.Format32bppArgb);
                    var g = Graphics.FromImage(slika);
                    g.CopyFromScreen(ix, iy, 0, 0, new Size(iw, ih), CopyPixelOperation.SourceCopy);
    
                    var dlg = new SaveFileDialog
                    {
                        DefaultExt = "png",
                        Filter = "Png Files|*.png"
                    };
                    var res = dlg.ShowDialog();
                    if (res == DialogResult.OK) slika.Save(dlg.FileName, ImageFormat.Png);
                }
                catch
                {
                }
    
            }
    
            private void Form1_MouseDown(object sender, MouseEventArgs e)
            {
                isMouseDown = true;
                x = Cursor.Position.X;
                y = Cursor.Position.Y;
            }
    
            private void Form1_MouseMove(object sender, MouseEventArgs e)
            {
    
            }
    
            private void Form1_MouseUp(object sender, MouseEventArgs e)
            {
                width = Cursor.Position.X - x;
                height = Cursor.Position.Y - y;
                Hide();
                Size = new Size(0, 0);
                Application.DoEvents();
                SaveScreen(x, y, width, height);
                x = y = 0;
                isMouseDown = false;
                Close();
            }
    
        }
    }
    

    【讨论】:

    • 谢谢,我刚试过,我正在使用 System.Windows.Forms;在我的程序中,但它说它不识别 Position 属性
    • 不知何故你混合 WPF 和 Winforms 的方式我还不能让它编译。
    • 是的,我应该只用 WPF 来做,但我没有找到足够的解决方案,所以现在一切都搞砸了,我不知道重写这一切需要多少时间在 WPF 中(如果我能够做到的话)。除了屏幕截图不准确之外,它编译得很好并且可以工作。但是我仍然不知道为什么如果我使用 System.Windows.Forms 时它不会识别 Position
    • @drake 你在传递屏幕坐标(不是相对于表单的坐标)吗?在您在此处发布的代码中,您使用 e.x 和 e.y 传递与表单相关的坐标
    • @drake:我粘贴了一个完整的解决方案。请注意,我进一步减少了代码。我摆脱了绘制矩形等。
    【解决方案2】:

    Graphics.CopyFromScreen需要屏幕坐标拍照。由于使用了MouseEventArgs 的 e.x 和 e.y,您正在传递相对于表单的坐标。您应该改用鼠标的屏幕坐标,使用相同的Cursor.PositionMousePosition

    如果您使用的是 WPF

    有许多选项可以帮助您获取屏幕坐标,其中一些选项:

    选项 1

    您可以使用PointToScreen方法将坐标转换为屏幕坐标。

    选项 2

    在 WPF 中你不能使用这些方法,最简单的方法是在你的 WPF 项目中添加对System.Windows.Forms.dll 的引用,然后使用静态的System.Windows.Forms.Control.MousePosition

    选项 3

    作为另一种选择,您可以添加对System.Drawing.dll 的引用并使用它:

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
    internal static extern bool GetCursorPos(ref PointStruct point);
    
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    internal struct PointStruct
    {
        public Int32 X;
        public Int32 Y;
    };
    
    public static System.Drawing.Point MousePosition()
    {
        var mousePosition = new PointStruct();
        GetCursorPos(ref mousePosition);
        return new System.Drawing.Point(mousePosition.X, mousePosition.Y);
    }
    

    然后你可以使用MousePosition()来获取鼠标在屏幕上的当前位置。

    【讨论】:

    • 谢谢,但正如我所说,我的程序无法识别 .Position 属性,可能是因为我将 WPF 和 WinForms 混合在一起。
    • @drake 问题不大。你想只捕获表单区域吗?还是整个屏幕?当您无法捕获表单之外的内容时,可以使用这种方式。
    • 我想从屏幕上捕获我想要的任何区域,它可以是桌面,任何程序。它不必是表单区域
    • @drake 但是您使用的解决方案只能捕获表单区域
    • 不,我可以捕捉任何我想要的东西,问题是不准确
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 2010-12-19
    • 1970-01-01
    相关资源
    最近更新 更多