3

I've got a custom control with two PictureBox controls that are animated and a label control over them.

The child indexes are set so that label is always on top but the picture boxes are interchanging so when animated they display different images each time.

As I understand, label needs to have a parent control on top of which it can support a semi transparent color (Argb). Since the label has active picture box as its parent it will also be animated with which is not what I want at all.

Is there a way to fix a child position relative to parents parent?

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Dino
  • 467
  • 1
  • 5
  • 14
  • You cannot achieve this effect in WinForms as the platform does not support true control variable opacity. "Transparent backgrounds" are implemented as a hack that makes the root parent (the underlying `Form`) repaint it's background. You cannot successfully layer multiple transparent controls on top. – Dai Dec 17 '15 at 12:59
  • @Dai - I thought so too but hoped for better ;). Is there any other way to achieve similar effect using win forms. I've seen DevXpress people make controls with similar effect so I wanted to give it a try. Any other solution that do not include WPF or going from win forms? – Dino Dec 17 '15 at 13:19
  • @Dino Let me know if I understand your question correctly: Do you need to have a transparent label above your picture box that only shows the text? – Reza Aghaei Dec 17 '15 at 13:24
  • @Reza Aghaei - Not quite. I want a semi-transparent color over animated picture boxes. I want to create something similar to windows tile. – Dino Dec 17 '15 at 13:34
  • @Dino, can you show us what have you tried? – arman1991 Dec 17 '15 at 13:34
  • @Dino Being animated makes no difference. Do you need semi-transparent background color for the label or or semi-transparent fore color for `Label`? – Reza Aghaei Dec 17 '15 at 13:40
  • @Reza Aghaei - Background color only – Dino Dec 17 '15 at 13:53

1 Answers1

6

To have a transparent label control, you can override the OnPaint method and draw all controls that intersects with label, at last draw the background and text of the label.

Also when moving your picture boxes, don't forget to call the Invalidate() method of the transparent label.

Screenshot

enter image description here

Sample Implementation

public class TransparentLabel : Label
{
    public TransparentLabel()
    {
        this.transparentBackColor = Color.Blue;
        this.opacity = 50;
        this.BackColor = Color.Transparent;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        if (Parent != null)
        {
            using (var bmp = new Bitmap(Parent.Width, Parent.Height))
            {
                Parent.Controls.Cast<Control>()
                      .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
                      .Where(c => c.Bounds.IntersectsWith(this.Bounds))
                      .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
                      .ToList()
                      .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));


                e.Graphics.DrawImage(bmp, -Left, -Top);
                using (var b = new SolidBrush(Color.FromArgb(this.Opacity, this.TransparentBackColor)))
                {
                    e.Graphics.FillRectangle(b, this.ClientRectangle);
                }
                e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
            }
        }
    }

    private int opacity;
    public int Opacity
    {
        get { return opacity; }
        set
        {
            if (value >= 0 && value <= 255)
                opacity = value;
            this.Invalidate();
        }
    }

    public Color transparentBackColor;
    public Color TransparentBackColor
    {
        get { return transparentBackColor; }
        set
        {
            transparentBackColor = value;
            this.Invalidate();
        }
    }

    [Browsable(false)]
    public override Color BackColor
    {
        get
        {
            return Color.Transparent;
        }
        set
        {
            base.BackColor = Color.Transparent;
        }
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • To make it full transparent background, set opacity to 0. Let me know if you have any question about the answer :) – Reza Aghaei Dec 17 '15 at 23:18
  • I will try it in the next few hours when I find the time and will make a reply with the results. Thank you very much. – Dino Dec 18 '15 at 07:38
  • You are welcome and Thank you for your feedback :) The screenshot has taken from the execution and it worked properly here. – Reza Aghaei Dec 18 '15 at 07:39
  • I'm a noob so this was a little bit over my head. The way I implemented your solution didn't work well. I know that I need to call `Invalidate()` on the `TransparentLabel` every time background changes but I'm a little bit lost since this is all new to me. I've been using [link](https://code.google.com/p/dot-net-transitions/) dot-net-transition to experiment and learn about animations and implementing your solution in this sample project [link](http://www.mediafire.com/download/jvgq5u4pxrwsqv2) was as described. The last link is the sample project so you can see what mean. – Dino Dec 18 '15 at 08:52
  • You should test the answer in a simple test case, for example programmatically using a `Timer` change the position and `BackGround` of the `PictureBox` and you will see the solution work properly. And then you can try to apply the answer to your project. – Reza Aghaei Dec 18 '15 at 08:57
  • I know that it works well. Your solution is awesome. The problem is the transition library that I'm using is confusing for me and cant find a way to call Invalidate() inside its moving function. Link is in the previous comment. [link](http://www.mediafire.com/download/jvgq5u4pxrwsqv2) – Dino Dec 18 '15 at 09:05
  • @Dino thank you for your kind feedback. For a simple test, I made those pictire boxes public and then in the form, I subscribed for `PositionChaged` event of them and callled ` this.transparentLabel1.Invalidate();` and it worked properly. – Reza Aghaei Dec 18 '15 at 09:27
  • But it's better to subscribe for the event of both picture boxes in the user control and when you received the `LocationChanged` raise a new custom event and subscribe for this event on your form and put `invalidate` code there. – Reza Aghaei Dec 18 '15 at 09:28
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/98344/discussion-between-dino-and-reza-aghaei). – Dino Dec 18 '15 at 12:19
  • Works perfectly. Thank you. – Dino Dec 18 '15 at 14:28
  • I updated onpaint method a bit. You also may find this post helpful: [How to make two transparent layer with c#?](http://stackoverflow.com/a/36102074/3110834) – Reza Aghaei Mar 24 '16 at 20:30
  • This answer has been here for 9 years, Why Microsoft did not integrate this class in .Net framework already? – Dohab Jun 03 '23 at 01:15
  • @Dohab Well, WinForms doesn't support opacity for child controls. So this implementation is just a hack. If you extensively have such requirements, you may want to use WPF or other frameworks. – Reza Aghaei Jun 03 '23 at 18:38