Search code examples
c#.netwinformscroppan

Switch between Pan and Crop by button click C#


I'm using WinForms. In my form i have a picturebox that has the ability to Pan and crop. The issue with my program is i don't have the ability to switch between crop and pan with a click of a button. How can i do this? I provided my code below.

    //------CROP::::::::::::::
    int cropX;
    int cropY;
    int cropWidth;

    int cropHeight;
    public Pen cropPen;
    //------PAN::::::::::::::::
    private Point _pt;
    private Point _pt2;
    bool _isPanning = false;
    Point startPt;
    //-----Button on/off:::::::
    private bool crop_btn_OFF = false;
    private bool pan_btn_OFF = false;

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        _isPanning = true;
        startPt = e.Location;

        if (e.Button == System.Windows.Forms.MouseButtons.Left )
            {
                Cursor = Cursors.Cross;
                cropX = e.X;
                cropY = e.Y;

                cropPen = new Pen(Color.FromArgb(153, 180, 209), 3);

                cropPen.DashStyle = DashStyle.DashDotDot;
            }
            pictureBox1.Refresh();

    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Left)
        {
            //X and Y are the position of the crop
            pictureBox1.Refresh();
            cropWidth = e.X - cropX;
            cropHeight = e.Y - cropY;
            pictureBox1.CreateGraphics().DrawRectangle(cropPen, cropX, cropY, cropWidth, cropHeight);
        }

        //if (_isPanning) Un-comment this to Pan the image
        //{    
        //    Cursor = Cursors.SizeAll;
        //    Control c = (Control)sender;
        //    c.Left = (c.Left + e.X) - startPt.X;
        //    c.Top = (c.Top + e.Y) - startPt.Y;
        //    c.BringToFront();
        //}
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        _isPanning = false;
        Cursor = Cursors.Default;
    }

    private void btn_Crop_Click(object sender, EventArgs e)
    {

        crop_btn_OFF = true;
        pan_btn_OFF = false;


        if (cropWidth < 1)
        {
            return;
        }
        Rectangle rect = new Rectangle(cropX, cropY, cropWidth, cropHeight);
        //First we define a rectangle with the help of already calculated points
        Bitmap OriginalImage = new Bitmap(pictureBox1.Image, pictureBox1.Width, pictureBox1.Height);
        //Original image
        Bitmap _img = new Bitmap(cropWidth, cropHeight);
        // for cropinf image
        Graphics g = Graphics.FromImage(_img);
        // create graphics
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
        //set image attributes
        g.DrawImage(OriginalImage, 0, 0, rect, GraphicsUnit.Pixel);

        pictureBox1.Image = _img;
        pictureBox1.Width = _img.Width;
        pictureBox1.Height = _img.Height;
    }

    private void btn_Pan_Click(object sender, EventArgs e)
    {
        crop_btn_OFF = false;
        pan_btn_OFF = true;
    }

enter image description here


Solution

  • Think carefully about what you want to happen when you click on a button or when you drag the image. At the moment, your crop button attempts to do two things: set the mode and apply the cropping.

    Try separating your modes better. Your button click handlers should only toggle the mode as suggested by @Leigh and the mouse handlers should either pan or crop depending on the mode.

    I haven't tested the contents of your methods so they may require some debugging. To make this easier, try to follow some kind of convention when naming your variables: e.g. private fields start with an underscore, methods and properties start with an uppercase letter, variables within a method start with a lower case letter. I've also added some whitespace after your comments because it wasn't immediately clear whether they applied to the line before or the line after.

    private enum State
    {
        Pan,
        Crop
    }
    private State _currentState;
    
    public void btnCrop_Click()
    {
        _currentState = State.Crop;
    }
    
    public void btnPan_Click()
    {
        _currentState = State.Pan;
    }
    
    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        if (_currentState == State.Crop)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left )
            {
                Cursor = Cursors.Cross;
                _cropX = e.X;
                _cropY = e.Y;
    
                _cropPen = new Pen(Color.FromArgb(153, 180, 209), 3);
    
                _cropPen.DashStyle = DashStyle.DashDotDot;
                pictureBox1.Refresh();
            }
        }
        else // state = pan
        {            
            _isPanning = true;
            _startPt = e.Location;
        }
    }
    
    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (_currentState == State.Crop)
        {
            Cursor = Cursors.Cross;
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                //X and Y are the position of the crop
                pictureBox1.Refresh();
                _cropWidth = e.X - _cropX;
                _cropHeight = e.Y - _cropY;
                pictureBox1.CreateGraphics().DrawRectangle(_cropPen, _cropX, _cropY, _cropWidth, _cropHeight);
            }
        }
        else // state = pan
            if (_isPanning)
            {    
                Cursor = Cursors.SizeAll;
                Control c = (Control)sender;
                c.Left = (c.Left + e.X) - _startPt.X;
                c.Top = (c.Top + e.Y) - _startPt.Y;
                c.BringToFront();
            }
        }
    }
    
    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        Cursor = Cursors.Default;
        if (_currentState == State.Crop)
        {            
            if (cropWidth < 1)
            {
                return;
            }
    
            Rectangle rect = new Rectangle(_cropX, _cropY, _cropWidth, _cropHeight);
            //First we define a rectangle with the help of already calculated points
    
            Bitmap originalImage = new Bitmap(pictureBox1.Image, pictureBox1.Width, pictureBox1.Height);
            //Original image
    
            Bitmap img = new Bitmap(_cropWidth, _cropHeight);
            // for cropinf image
    
            Graphics g = Graphics.FromImage(img);
            // create graphics
    
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            //set image attributes
    
            g.DrawImage(originalImage, 0, 0, rect, GraphicsUnit.Pixel);
    
            pictureBox1.Image = img;
            pictureBox1.Width = img.Width;
            pictureBox1.Height = img.Height;
        }
        else // state = pan
        {
            // nothing to do here but leaving it for symmetry with the other methods
        }        
    }
    

    To make the functionality more obvious to the user, you could replace the buttons with radio buttons or figure out how to make the button corresponding to the current state remain pushed in.