Search code examples
c#wpfscreenshotgdi

Delay while taking Screenshot of Desktop C# (WPF)


I am doing some program to take screenshot. Its functionality is same as Windows's Snipping Tool. User defines area by drawing rectangle on screen and takes a shot.

I followed this an amazing tutorial, like opening new window by covering whole desktop. Then I draw rectangle on Window and take shot inside of rectangle. If Mouse is moved normally, screenshot is taken correctly. Like this image

enter image description here

But, when mouse is moved faster, screenshot is taken incorrectly as in below picture. Shot is taken out of rectangle area

Shot is taken out of rectangle area

Here is source code:

public partial class CapturingArea : Window
{

    public  BitmapSource    mTakenScreenShot;
    private Point           mStartPoint;
    private Point           mEndPoint;                
    private Rectangle       mDrawRectangle;       

    public CapturingArea()
    {
        InitializeComponent();
        InitMainWindow();            
        Mouse.OverrideCursor = Cursors.Cross;

        mStartPoint = new Point();
        mEndPoint   = new Point();            
    }      

    /*Close Window by pressing ESC Button*/
    private void Window_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Escape)
        {
            Mouse.OverrideCursor = Cursors.Arrow;
            this.Close();                
        }
    }        

    /*When Mouse is clicked 
     get the current point of Mouse and Start Drawing Rectangle on the Canvas*/
    private void cnDrawingArea_MouseDown(object sender, MouseButtonEventArgs e)
    {            
        if(mDrawRectangle != null)
            this.cnDrawingArea.Children.Remove(mDrawRectangle);

        mStartPoint = e.GetPosition(this);                  

        mDrawRectangle = new Rectangle
        {
            Stroke = Brushes.Red,
            StrokeThickness = 0.5
        };

        Canvas.SetLeft(mDrawRectangle, mStartPoint.X);
        Canvas.SetTop(mDrawRectangle, mStartPoint.Y);
        this.cnDrawingArea.Children.Add(mDrawRectangle);
    }

    /* Continue drawing Rectangle while Mouse is moving on the Canvas Area*/
    private void cnDrawingArea_MouseMove(object sender, MouseEventArgs e)
    {
        if(e.LeftButton == MouseButtonState.Released)
        {
            return;
        }

        Point tmpPoint = e.GetPosition(this.cnDrawingArea);

        int xPos = (int) Math.Min(tmpPoint.X, mStartPoint.X);
        int yPos = (int) Math.Min(tmpPoint.Y, mStartPoint.Y);

        int recWidth = (int) Math.Max(tmpPoint.X, mStartPoint.X) - xPos;
        int recHeight = (int)Math.Max(tmpPoint.Y, mStartPoint.Y) - yPos;

        mDrawRectangle.Width = recWidth;
        mDrawRectangle.Height = recHeight;
        Canvas.SetLeft(mDrawRectangle, xPos);
        Canvas.SetTop(mDrawRectangle, yPos);
    }
    /*Initialize Window to cover whole screen*/
    private void InitMainWindow()
    {
        this.WindowStyle = WindowStyle.None;
        this.Title = string.Empty;
        this.ShowInTaskbar = false;
        this.AllowsTransparency = true;
        this.Background = new SolidColorBrush(Color.FromArgb(0x10, 0x10, 0x10, 0x10));
     //   this.Topmost = true;
        this.Left   = SystemParameters.VirtualScreenLeft;
        this.Top    = SystemParameters.VirtualScreenTop;
        this.Width  = SystemParameters.VirtualScreenWidth;
        this.Height = SystemParameters.VirtualScreenHeight;
    }


    /*First calculate Starting Ending points according to 
     mouse move and take screenshot*/
    private void CaptureScreen(int X1, int Y1, int X2, int Y2)
    {
        int StartXPosition = 0;
        int StartYPosition = 0;
        int tmpWidth = 0;
        int tmpHeight = 0;

        if (X1 < X2 && Y1 < Y2)          /*Drawing Left to Right*/
        {
            StartXPosition = X1;
            StartYPosition = Y1;
            tmpWidth  = X2 - X1;
            tmpHeight = Y2 - Y1;
        } 
        else if(X1 > X2 && Y1 < Y2)     /*Drawing Top to Down*/
        {
            StartXPosition = X2;
            StartYPosition = Y1;
            tmpWidth  = X1 - X2;
            tmpHeight = Y2 - Y1;
        } 
        else if(X1 > X2 && Y1 > Y2)     /*Drawing Down to Top*/
        {
            StartXPosition = X2;
            StartYPosition = Y2;
            tmpWidth = X1 - X2;
            tmpHeight = Y1 - Y2;
        } 
        else if(X1 < X2 && Y1 >Y2)      /*Drawing Right to Left */
        {
            StartXPosition = X1;
            StartYPosition = Y2;
            tmpWidth  = X2 - X1;
            tmpHeight = Y1 - Y2;
        }
        StartXPosition += 2;
        StartYPosition += 2;
        tmpWidth -= 2;
        tmpHeight -= 2;
        mTakenScreenShot = ScreenCapture.CaptureRegion(StartXPosition, StartYPosition, tmpWidth, tmpHeight, false);
        Mouse.OverrideCursor = Cursors.Arrow;
    }

    /*get the screenshot and by calculating real positions of Desktop*/
    private void cnDrawingArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {            
            if (e.LeftButton == MouseButtonState.Released)
            {
                mEndPoint = e.GetPosition(this.cnDrawingArea);

                if (mDrawRectangle != null)
                    this.cnDrawingArea.Children.Remove(mDrawRectangle);

                Point StartDesktopPosition = this.PointToScreen(mStartPoint);   
                Point EndDesktopPosition = this.PointToScreen(mEndPoint);

                int tempX1 = (int)StartDesktopPosition.X;
                int tempY1 = (int)StartDesktopPosition.Y;
                int tempX2 = (int)EndDesktopPosition.X;
                int tempY2 = (int)EndDesktopPosition.Y;

                CaptureScreen(tempX1, tempY1, tempX2, tempY2);
                this.DialogResult = true;
                this.Close();
            }
        }
    }

Is there any solution or suggestion when mouse is moved fast to take right screenshot.

Thank you.


Solution

  • When the mouse is moved very quickly, Windows will aggregate mouse moves into a single message so as to not overload any programs with tons of WM_MOUSEMOVEs. You can get a list of these intermediate points in WPF via Mouse.GetIntermediatePoints.

    I suspect your problem is that the mouse is moving fast across the screen between the last move and the left button up and you are missing a whole bunch of intermediate points.

    Try this as an experiment, also draw your rectangle in the left button up handler. I believe that will make the rectangle match the screen shot area. If you then check the intermediate points, you will likely see a bunch of move data queued up.

    To get around this, why not just use the last mouse move as mEndPoint instead of the call to GetPosition in the left button up handler? That should give you the correct behavior since it will exactly mirror your current method of drawing the rectangle. Then, regardless of intermediate moves, you will be capturing the rectangle coordinates itself, not the new rectangle formed by the left button up position.