Thoms Thoms - 1 month ago 13
C# Question

Taking a screenshot - sizes do not match

I tried to programm a snipping tool of my own. It works quite well, except that the size of the area I select does not match the size in Pixels.

I create a window with low opacity and draw a rectangle to get the size and location. The window has 300x300 dimensions (later it should be full Screen), but if I take a screenshot with snipping tool it shows a size of 375x375 Pixels. So when I take a scrennshot with my own programm I do not catch all the features I actually wanted.

The final goal is to take multiple screenshots of the same location with a shortcut (not implemented), then do some OCR(not implemented) on specific regions and create a filename corresponding to the OCR results.

Here is the code:

Main 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;
}


Main window XAML:

<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>




Secondary window to get the rectangle 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; }
}
}


secondary window XAML:

<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>




So, here are my questions:


  1. Why is the size of the window not the actual size in pixels?

  2. How do I get the values I want so that the images I take include all the features I want them to an not less?



Thanks for our help.

Answer

The link of Pikoh was the right direction. Led me to this 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;            
    }

I am still off 1 or 2 Pixel (donĀ“t know why) when the difference is big enough (100+ Pixel) but I can live with that.

Edit: The difference is about 8 Pixel an it is always there - i guess it is the window border or something.