0

My application (C#, WinForms, .NET 4.0, test under Windows 7 SP1) should work as follows:

There is a PictureBox somewhere on a Form. It's image has a rectangle area:

enter image description here

When user clicks that area, it is framed by a colored line:

enter image description here

This selection should take into account resizing of the Form:

enter image description here

And it also should take into account different DPI (assuming that it is set before application startup and never changes while application is running).

I wrote the following code:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private readonly RectangleF rectangleRelativeToPicturebox = new RectangleF(335.0F, 47.0F, 65.0F, 44.0F);
        private Boolean rectangleIsClicked = false;
        internal static readonly Pen clickedRectangleBorderPen = new Pen(Color.RoyalBlue, 4);

        private readonly float pictureBoxInitialWidth, pictureBoxInitialHeight;
        private float scaleByFormResizeX, scaleByFormResizeY, scaleByDpiX, scaleByDpiY, scaleX, scaleY;

        public Form1()
        {
            InitializeComponent();

            pictureBoxInitialWidth = pictureBox1.ClientSize.Width;
            pictureBoxInitialHeight = pictureBox1.ClientSize.Height;

            using (var graphics = CreateGraphics())
            {
                scaleByDpiX = graphics.DpiX / 96;
                scaleByDpiY = graphics.DpiY / 96;
            }

            scaleByFormResizeX = 1.0F;
            scaleByFormResizeY = 1.0F;

            scaleX = scaleByFormResizeX * scaleByDpiX;
            scaleY = scaleByFormResizeY * scaleByDpiY;
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            scaleByFormResizeX = pictureBox1.ClientSize.Width / pictureBoxInitialWidth;
            scaleByFormResizeY = pictureBox1.ClientSize.Height / pictureBoxInitialHeight;

            scaleX = scaleByFormResizeX * scaleByDpiX;
            scaleY = scaleByFormResizeY * scaleByDpiY;
        }

        private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
        {
            if ((e.Location.X >= rectangleRelativeToPicturebox.Left * scaleX) && (e.Location.X <= rectangleRelativeToPicturebox.Right * scaleX) &&
                     (e.Location.Y >= rectangleRelativeToPicturebox.Top * scaleY) && (e.Location.Y <= rectangleRelativeToPicturebox.Bottom * scaleY))
                rectangleIsClicked = true;
            else
                rectangleIsClicked = false;

            pictureBox1.Refresh();
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (rectangleIsClicked)
                e.Graphics.DrawRectangle(clickedRectangleBorderPen, rectangleRelativeToPicturebox.X * scaleX, rectangleRelativeToPicturebox.Y * scaleY,
                    rectangleRelativeToPicturebox.Width * scaleX, rectangleRelativeToPicturebox.Height * scaleY);
        }
    }
}

I have the rectangle coordinates relative to the PictureBox.
I calculate scaleByFormResizeX and scaleByFormResizeY when the Form is resized.
That part works fine. But looks like I calculate scaleByDpiX and scaleByDpiY incorrectly, because for 120 DPI it looks like:

enter image description here

So whats wrong with my approach? Is there a better solution?

P.S. I've uploaded my sample application here

bairog
  • 3,143
  • 6
  • 36
  • 54
  • When the form is resized, where do you adjust the rectangleRelativeToPicturebox rectangle? It doesn't look like it is ever changed from its initialized position. – Jeff R. Jan 29 '19 at 15:26
  • I don't really understand all those *calculations*. You're using a PictureBox control. You can just check its Bounds when you need to paint a Rectangle around it. Or even its `ClientRectangle` and paint a border using this measure. You could, possibly, have some *problems* if your application is not DPIAware. You *might* get wrong dimensions, if you paint it from the *outside*. But your app's DPI Awareness staus is undefined. – Jimi Jan 29 '19 at 17:14
  • @JeffR.inside **pictureBox1_Paint** event I use scaleX and scaleY to adjust rectangleRelativeToPicturebox rectangle's X/Y coordinate and Width/Height. scaleX is calculated as scaleByFormResizeX * scaleByDpiX; scaleY is calculated as scaleByFormResizeY * scaleByDpiY inside **Form1_Resize** event. – bairog Jan 30 '19 at 06:08
  • @Jimi I'm checking pictureBox1.ClientSize inside **Form1_Resize** event and calculate scaleByFormResizeX and scaleByFormResizeY coefficients according to this. – bairog Jan 30 '19 at 06:10
  • @Jimi I'm painting border not for my **whole** picturebox but for some rectangle area inside it. That's why I need rectangleRelativeToPicturebox rectangle (X/Y and Width/Height). I've updated my images to make it clearer. – bairog Jan 30 '19 at 06:20
  • Well, now it's understandable. See these answers here: [Translate Rectangle Position in Zoom Mode Picturebox](https://stackoverflow.com/questions/53800328/translate-rectangle-position-in-zoom-mode-picturebox/53802043#53802043). Unfortunately, the code I posted there is adapted to Zoomed selection only. Reza Aghaei's answer can be instead adapted to stretched selections, since it's using the PictureBox's internal (private) `ImageRectangleFromSizeMode` method, which works in relation to the current `SizeMode` See whether it fits. If not, I'll give a look at your project. – Jimi Jan 30 '19 at 06:34
  • However, I can see that your Application is actually not DPIAware. You should really consider changing this. – Jimi Jan 30 '19 at 06:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187542/discussion-between-bairog-and-jimi). – bairog Jan 30 '19 at 07:14

0 Answers0