截图 - 尺寸不匹配

Taking a screenshot - sizes do not match

我尝试编写自己的截图工具。它工作得很好,除了我 select 的区域大小与像素大小不匹配。

我创建了一个低不透明度的 window 并绘制了一个矩形以获得大小和位置。 window 有 300x300 尺寸(稍后它应该是全屏),但如果我使用截图工具截屏,它显示的尺寸为 375x375 像素。所以当我用自己的程序截屏时,我没有捕捉到我真正想要的所有功能。

最终目标是通过快捷方式(未实现)对同一位置进行多张截图,然后对特定区域进行一些OCR(未实现)并创建与OCR结果对应的文件名。

代码如下:

主要 window c#:

int number = 0;

    System.Windows.Point scsh_Start;
    System.Windows.Point scsh_Ende;
    System.Windows.Point OCR_Start;
    System.Windows.Point OCR_Ende;

    public MainWindow()
    {
        InitializeComponent();            
    }


    private void button_Click(object sender, RoutedEventArgs e)
    {
        int Width = (int)( Math.Abs(scsh_Start.X - scsh_Ende.X) );
        int Height = (int)( Math.Abs(scsh_Start.Y - scsh_Ende.Y) );
        using (Bitmap bmpScreenCapture = new Bitmap(Width,
                                        Height))
        {
            using (Graphics g = Graphics.FromImage(bmpScreenCapture))
            {                    
                string outputNumber = "";
                if (number < 10)
                {
                    outputNumber = "00" + number.ToString();
                }
                else if (number < 100)
                {
                    outputNumber = "0" + number.ToString();
                }
                else
                {
                    outputNumber = number.ToString();
                }
                Opacity = .0;                    
                g.CopyFromScreen((int)scsh_Start.X,
                                 (int)scsh_Start.Y,
                                 0, 0,
                                 bmpScreenCapture.Size);
                Directory.CreateDirectory("C:\Users\Public\Pictures\Sample Pictures\ScreenSave");
                bmpScreenCapture.Save("C:\Users\Public\Pictures\Sample Pictures\ScreenSave\test" + outputNumber + ".png");
                Opacity = 1;
                number++;
            }
        }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        this.WindowState = WindowState.Minimized;
        Window1 w1 = new Window1();
        w1.RaiseCustomEvent += pointsScreenshot;
        w1.ShowDialog();

    }

    private void pointsScreenshot(object sender, customEventArgs e)
    {
        if (e.Points[0] == null)
            return;
        scsh_Start = e.Points[0];
        scsh_Ende = e.Points[1];
        B_Image.IsEnabled = true;    
    }

主要windowXAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication1"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Button x:Name="B_Image" Content="Image" HorizontalAlignment="Left" Margin="70,31,0,0" VerticalAlignment="Top" Width="75" Click="button_Click" IsEnabled="False"/>
    <Button x:Name="B_MouseEvents" Content="Mouse" HorizontalAlignment="Left" Margin="70,76,0,0" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
</Grid>

二次window得到矩形c#:

    public partial class Window1 : Window
{
    public event EventHandler<customEventArgs> RaiseCustomEvent;
    private Point p_Start = new Point();
    private Point p_End = new Point();
    private Rectangle saveRect = null;
    private MoveType move = MoveType.Draw;
    bool top = false;
    bool left = false;


    public Window1()
    {
        InitializeComponent();
        this.Topmost = true;
        this.Activate();
        ////this.WindowState = WindowState.Maximized;       

    }

    private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        p_Start = e.GetPosition(null);

        double xAct = e.GetPosition(null).X;
        double yAct = e.GetPosition(null).Y;

        if (saveRect != null)
        {
            double rectLeft = saveRect.Margin.Left;
            double rectTop = saveRect.Margin.Top;
            double rectRight = saveRect.Margin.Left + saveRect.Width;
            double rectBottom = saveRect.Margin.Top + saveRect.Height;

            if (between(xAct, rectLeft + 3, rectRight - 3) &&
                between(yAct, rectTop + 3, rectBottom - 3))
                move = MoveType.Drag;

            else if (( between(xAct, rectLeft - 3, rectLeft) || between(xAct, rectRight - 3, rectRight) ) &&
                between(yAct, rectTop, rectBottom))
            {
                move = MoveType.ResizeWidth;
                left = between(xAct, rectLeft - 3, rectLeft + 3);
            }
            else if (( between(yAct, rectTop - 3, rectTop) || between(yAct, rectBottom - 3, rectBottom) ) &&
                between(xAct, rectLeft, rectRight))
            {
                move = MoveType.ResizeHeight;
                top = between(yAct, rectTop - 3, rectTop + 3);
            }
            else
                move = MoveType.Draw;
        }
        else
            move = MoveType.Draw;
    }

    private void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        move = MoveType.Draw;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {


    }

    private void Window_MouseMove(object sender, MouseEventArgs e)
    {
        double xAct = e.GetPosition(null).X;
        double yAct = e.GetPosition(null).Y;

        #region select Cursor
        if (e.LeftButton != MouseButtonState.Pressed)
        {
            if (saveRect != null)
            {
                double rectLeft = saveRect.Margin.Left;
                double rectTop = saveRect.Margin.Top;
                double rectRight = saveRect.Margin.Left + saveRect.Width;
                double rectBottom = saveRect.Margin.Top + saveRect.Height;

                if (between(xAct, rectLeft + 3, rectRight - 3) &&
                    between(yAct, rectTop + 3, rectBottom - 3))
                    Cursor = Cursors.Hand;

                else if (( between(xAct, rectLeft - 3, rectLeft) || between(xAct, rectRight - 3, rectRight) ) &&
                    between(yAct, rectTop, rectBottom))
                    Cursor = Cursors.SizeWE;
                else if (( between(yAct, rectTop - 3, rectTop) || between(yAct, rectBottom - 3, rectBottom) ) &&
                    between(xAct, rectLeft, rectRight))
                    Cursor = Cursors.SizeNS;

                else Cursor = Cursors.Pen;
            }
            else
                Cursor = Cursors.Pen;
            return;
        }
        else
        {
            switch (move)
            {
                case MoveType.Drag:
                    Cursor = Cursors.Hand;
                    break;
                case MoveType.Draw:
                    Cursor = Cursors.Pen;
                    break;
                case MoveType.ResizeHeight:
                    Cursor = Cursors.SizeNS;
                    break;
                case MoveType.ResizeWidth:
                    Cursor = Cursors.SizeWE;
                    break;
            }
        } 
        #endregion

        if (mainGrid.Children.Count > 0)
            this.mainGrid.Children.RemoveAt(0);
        Rectangle r = new Rectangle();
        p_End = e.GetPosition(null);
        switch (move)
        {
            case MoveType.Draw:                                     
                r.Stroke = new SolidColorBrush(Colors.Aqua);
                r.Opacity = 1;
                r.Height = Math.Abs(p_End.Y - p_Start.Y);
                r.Width = Math.Abs(p_End.X - p_Start.X);
                double t_left = p_End.X > p_Start.X ? p_Start.X : p_End.X;
                double t_top = p_End.Y > p_Start.Y ? p_Start.Y : p_End.Y;
                r.VerticalAlignment = VerticalAlignment.Top;
                r.HorizontalAlignment = HorizontalAlignment.Left;
                r.Margin = new Thickness(t_left, t_top, 0, 0);
                this.mainGrid.Children.Add(r);
                saveRect = r;
                break;
            case MoveType.Drag:
                r = saveRect;
                double moveHorizontal = p_Start.X - p_End.X;
                double moveVertical = p_Start.Y - p_End.Y;
                if (r.Margin.Left - moveHorizontal < 0)
                    moveHorizontal = r.Margin.Left;
                if (r.Margin.Top - moveVertical < 0)
                    moveVertical = r.Margin.Top;
                r.Margin = new Thickness(r.Margin.Left - moveHorizontal, r.Margin.Top - moveVertical, 0, 0);
                this.mainGrid.Children.Add(r);
                saveRect = r;
                p_Start = p_End;
                break;
            case MoveType.ResizeHeight:
                r = saveRect;
                double resize = p_Start.Y - p_End.Y;
                if (top)
                {                       
                    r.Margin = new Thickness(r.Margin.Left, r.Margin.Top - resize, 0, 0);                        
                    r.Height += resize;
                }
                else
                {                       
                    r.Height -= resize;
                }
                this.mainGrid.Children.Add(r);
                saveRect = r;
                p_Start = p_End;
                break;
            case MoveType.ResizeWidth:
                r = saveRect;
                double resizeX = p_Start.X - p_End.X;
                if (left)
                {
                    r.Margin = new Thickness(r.Margin.Left - resizeX, r.Margin.Top, 0, 0);
                    r.Width += resizeX;
                }
                else
                {
                    r.Width -= resizeX;
                }
                this.mainGrid.Children.Add(r);
                saveRect = r;
                p_Start = p_End;
                break;
        }
    }

    private void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        RaiseCustomEvent(this, new customEventArgs(getPoints()));
        this.Close();
    }

    public List<Point> getPoints()
    {
        List<Point> p = new List<Point>();
        this.Visibility = Visibility.Collapsed;
        if (mainGrid.Children.Count > 0)
        {
            p.Add(new Point(saveRect.Margin.Left, saveRect.Margin.Top));
            p.Add(new Point(saveRect.Width+ saveRect.Margin.Left, saveRect.Height+ saveRect.Margin.Top));
            return p;
        }

        return null;
    }

    private bool between(double actual, double min, double max)
    {
        if (( actual < min ) || ( actual > max ))
            return false;
        else return true;
    }

    private enum MoveType
    {
        Draw,
        Drag,
        ResizeHeight,
        ResizeWidth
    }

}

public class customEventArgs : EventArgs
{
    private List<Point> pts;
    public customEventArgs(List<Point> Points)
    {
        pts = Points;
    }

    public List<Point> Points
    {
        get { return pts; }
    }
}

二级windowXAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication1"
    mc:Ignorable="d"
    Title="W1" Height="300" Width="300"  WindowStyle="None" Topmost="True" BorderThickness="0" Foreground="{x:Null}" AllowsTransparency="True"  
    Left="0" Top="0"
    MouseLeftButtonDown="Window_MouseLeftButtonDown" Loaded="Window_Loaded" MouseMove="Window_MouseMove" MouseRightButtonDown="Window_MouseRightButtonDown" 
    MouseLeftButtonUp="Window_MouseLeftButtonUp" >
<Window.Background>
    <SolidColorBrush Opacity="0.3" Color="Gray"></SolidColorBrush>
</Window.Background>
<Grid Name="mainGrid">        
</Grid>

所以,这是我的问题:

  1. 为什么 window 的大小不是以像素为单位的实际大小?
  2. 我如何获得我想要的值,以便我拍摄的图像包含我想要的所有功能,而不是更少?

感谢我们的帮助。

Pikoh 的 link 是正确的方向。 把我带到这个 How can I get the DPI in WPF?

private double getDPIScale()
    {
        PresentationSource source = PresentationSource.FromVisual(this);

        double dpiX , dpiY;
        if (source != null)
        {
            dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
            dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
            return dpiX / 96.0;
        }

        return 0;            
    }

当差异足够大(100+ 像素)时,我仍然关闭 1 或 2 像素(不知道为什么),但我可以忍受。

编辑:差异大约是 8 个像素,它始终存在 - 我猜是 window 边框之类的。