Saiful Aman Rifat
Saiful Aman Rifat

Reputation: 33

How to prevent multiple images from overlapping each other

I am trying to make some custom buttons in a Form, which should in theory look like the following image:

Buttons

I created custom windows form control class to make each button. They were implemented using a PictureBox, which holds one image for clicked state and another for not-clicked state. The code for the class is given below:

public partial class MyCustomButton: PictureBox
{
    public MyCustomButton()
    {
        InitializeComponent();

    }

    //variables
    private Image NormalImage;
    private Image ClickedImage;

    //public properties
    public Image ImangeNormal
    {
        get { return NormalImage; }
        set { NormalImage = value; }
    }
    public Image ImageClicked
    {
        get { return ClickedImage; }
        set { ClickedImage = value; }
    }

    //Button event properties implementation
    private void MyCustomButton_MouseDown(object sender, MouseEventArgs e)
    {
        if (HitTest(this, e.X, e.Y))
            this.Image = ClickedImage;

    }

    private void MyCustomButton_MouseUp(object sender, MouseEventArgs e)
    {
        if (HitTest(this, e.X, e.Y))
            this.Image = NormalImage;
    }

    private void MyCustomButton_MouseMove(object sender, MouseEventArgs e)
    {
        {
            if (HitTest(this, e.X, e.Y))
                this.Cursor = Cursors.Hand;
            else
                this.Cursor = Cursors.Default;
        }
    }

    //Test if the mouse is over the image
    public bool HitTest(PictureBox control, int x, int y)
    {
        var result = false;
        if (control.Image == null)
            return result;
        var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
          System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        var r = (Rectangle)method.Invoke(control, new object[] { control.SizeMode});
        using (var bm = new Bitmap(r.Width, r.Height))
        {
            using (var g = Graphics.FromImage(bm))
                g.DrawImage(control.Image, 0, 0, r.Width, r.Height);
            if (r.Contains(x, y) && bm.GetPixel(x - r.X, y - r.Y).A != 0)
                result = true;
        }
        return result;
    }
}

As u can see in the above class, it tests if the mouse is over the actual image and not the background of the PictureBox; and only when it is clicked over the actual image, the clicked image is visible. But I still was unable to rectify the problem with the PictureBoxes overlapping each other, because of the rectangular shape of PictureBox as shown in the picture below:

Overlapping Buttons

I am also aware that the setting background transparent in Picturebox.Backcolor property, would make the background transparent to its parent class. But since, the each PictureBox has partial overlapping with 2 other Picture Boxes and also with the form. How can this be made possible? Most likely scenario is, it can not be done with a PictureBox; but implemented with a Label. I have trying to make coding for that, but was unsuccessful. How to implement both the click and normal images, and also the HitTest(PictureBox control, int x, int y) method solving it, through a label? If anyone can help me solve this issue, or find a work-around, I would be really grateful.

Upvotes: 3

Views: 831

Answers (1)

Equalsk
Equalsk

Reputation: 8224

I was able to make a simple triangular button by overriding the OnPaint event to paint the triangle and then setting the Region to the same area.

I haven't spent time making the control rotatable and it's painted grey just for illustration. I've commented the key points of code that would need changing to support these things.

class ArrowButton : Button
{
    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.SmoothingMode = SmoothingMode.HighQuality;
        float h = this.Height;
        float w = this.Width;

        // This defines the shape of the triangle
        // This is an upwards pointing triangle
        PointF[] pts = new PointF[] { new PointF(w / 2, 0), new PointF(0, w), new PointF(w, h) };

        // This points down
        //PointF[] pts = new PointF[] { new PointF(0, 0), new PointF(w, 0), new PointF(w / 2, h) };

        // Paints the triangle a grey colour
        g.FillPolygon(Brushes.Gray, pts);

        GraphicsPath gp = new GraphicsPath();
        gp.AddPolygon(pts);
        this.Region = new Region(gp);
    }
}

Add the control to the form and ensure it has square dimensions. They'll look like they overlap in the designer, but click events will only fire inside the triangular area.

Upvotes: 1

Related Questions