TheFitGeekGirl
TheFitGeekGirl

Reputation: 1253

ControlPaint.DrawBorder3D wrong location and size when zooming

ControlPaint.DrawBorder3D draws correctly at 100% but it is wrong if the zooming is less or more than 100%. It seems like it still draws with 100%. ControlPaint.DrawBorder is correct in all cases.

I found a similar post with the same problem but no solution yet: http://www.44342.com/dotnet-framework-f70-t8279-p1.htm and http://www.pcreview.co.uk/forums/differences-between-drawborder3d-and-drawborder-t1313989.html

Is this a known issue? Is there any workaround, other than using DrawBorder instead of DrawBorder3D?

Update: Here's a code sample for the scale transformation with DrawBorder3D and DrawBorder (the form has 2 buttons to zoom in and out, a PictureBox for the image and a label to display the current zoom). Watch what happens to the two borders when zooming: the red border adapts to the zoom factor but the 3D border won't change.

public partial class Form1 : Form
{
    private Image image;
    float currentZoom = 100.0f;
    float zoom = 1.0f;
    const int zoomIncrement = 10;

    public Form1()
    {
        InitializeComponent();

        const string fileName = @"C:\30400988_1506.jpg";
        image = Image.FromFile(fileName);
        pictureBox.Paint += pictureBox_Paint;
        lblCurrentZoom.Text = string.Empty;
    }

    private void pictureBox_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.ScaleTransform(zoom, zoom);
        e.Graphics.DrawImage(image, 0, 0, image.Width, image.Height);

        var units = GraphicsUnit.Pixel;
        var rectangle = Rectangle.Round(image.GetBounds(ref units));

        ControlPaint.DrawBorder3D(e.Graphics, rectangle, Border3DStyle.Sunken);
        ControlPaint.DrawBorder(e.Graphics, rectangle, Color.Red, ButtonBorderStyle.Solid);
    }

    private void btnZoomIn_Click(object sender, EventArgs e)
    {
        SetZoom(currentZoom + zoomIncrement);
        pictureBox.Invalidate();
        lblCurrentZoom.Text = "current zoom: " + zoom.ToString("p");
    }

    private void btnZoomOut_Click(object sender, EventArgs e)
    {
        SetZoom(currentZoom - zoomIncrement);
        pictureBox.Invalidate();
        lblCurrentZoom.Text = "current zoom: " + zoom.ToString("p");
    }

    private void SetZoom(float zoomPercentage)
    {
        zoom = zoomPercentage / 100.0f;
        if (zoom < 0.1f)
        {
            zoom = 0.1f;
        }
        currentZoom = zoom * 100.0f;
    }
}

I didn't have much time and I can't post our productive code so this example is adapted from http://tjclifton.com/2012/03/13/zooming-an-image-in-windows-forms/.

Upvotes: 0

Views: 400

Answers (2)

sn0wcat
sn0wcat

Reputation: 96

It seems that the DrawBorder3D method ignores all graphics transformations. e.g. in the following code, the first rectangle is not rotated but the second one is.

private void Form1_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.RotateTransform(30.0f);
        var rect = new Rectangle(10, 10, 100, 200);
        var rect2 = new Rectangle(300,10,100,200);
        // ignores the rotate transformation
        ControlPaint.DrawBorder3D(e.Graphics, rect, Border3DStyle.SunkenOuter, Border3DSide.All);
        // draws with rotate transformation
        ControlPaint.DrawBorder(e.Graphics, rect2, Color.Black, ButtonBorderStyle.Outset);
    }

I guess you could read the zoom factor from the current world Transformation Matrix (if you only have zooming) and scale manually the rectangle which you are using to draw the 3d border...something along the lines of

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.ScaleTransform(1.5f,1.5f);
        var rect = new Rectangle(10, 10, 100, 200);
        var rect2 = new Rectangle(300, 10, 100, 200);
        // scale the rectangle
       var scaleFactor = e.Graphics.Transform.Elements[0];

        rect.X = (int)(rect.X * scaleFactor);
        rect.Y = (int)(rect.Y * scaleFactor);
        rect.Width = (int)(rect.Width * scaleFactor);
        rect.Height = (int)(rect.Height * scaleFactor);

        ControlPaint.DrawBorder3D(e.Graphics, rect, Border3DStyle.SunkenOuter, Border3DSide.All);
        ControlPaint.DrawBorder(e.Graphics, rect2, Color.Black, ButtonBorderStyle.Outset);
    }

Upvotes: 1

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73502

Not sure why that doesn't work. Whatever you do with Graphics instance gets scaled, DrawBorder just draws border with Graphics.DrawRectangle method internally(which gets scaled) as opposed to DrawBorder3D which uses DrawEdge win32 api (which doesn't).

As a workaround you could manually scale the rectangle and pass it to DrawBorder3D.

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    int scaleX = 2;
    int scaleY = 2;

    Graphics g = e.Graphics;
    g.ScaleTransform(scaleX, scaleY);//Scale

    Rectangle rectangle = new Rectangle(10, 10, 60, 40);
    g.FillEllipse(Brushes.Gray, rectangle);

    var scaledRectangle = Scale(rectangle, scaleX, scaleY);//Scale the border manually
    ControlPaint.DrawBorder3D(g, scaledRectangle);//Note the use of scaled rectangle instead of actual rectangle.
}

private Rectangle Scale(Rectangle rectangle, int scaleX, int scaleY)
{
    if (rectangle.IsEmpty)
    {
        return rectangle;
    }

    rectangle.X *= scaleX;
    rectangle.Y *= scaleY;
    rectangle.Width *= scaleX;
    rectangle.Height *= scaleY;

    // If the scale in the X dimension is negative, we need to normalize X and Width
    if (scaleX < 0)
    {
        // Make X the left-most edge again
        rectangle.X += rectangle.Width;

        // and make Width positive
        rectangle.Width *= -1;
    }

    // Do the same for the Y dimension
    if (scaleY < 0)
    {
        // Make Y the top-most edge again
        rectangle.Y += rectangle.Height;

        // and make Height positive
        rectangle.Height *= -1;
    }

    return rectangle;
}

Scale method is taken from Reference Source

Upvotes: 1

Related Questions