8

UPDATE: I took a break from messing with the transparency stuff for a few days. I started messing with it again tonight. I got a new result using Hans Passant's solution: new result http://img3.imageshack.us/img3/4265/icontransp.jpg

Passant's solution does solve the issue of the transparent background gradient. However, I'm still running into the problem with the transparent colors in my icon blending with the form's BackColor. You can see the fuchsia around various parts of the icon in the above image.

ORIGINAL CONTENT:

I've been going at this for several hours now, and I haven't had much luck. I've messed with Control.Region, Form.TransparencyKey, Form.Opacity, and a couple other random things with some funky effects.

Lately I've been trying to customize my desktop and decided to mess with Application Docks. After seeing what the Mac dock and a few third-party Windows implementations had to offer, I decided I wanted to build my own.

Eventually I want to move on to using the Win32 API. For now I just want to get something working using as much C# and .Net framework capabilities as possible.

There are a few things I want to be able to do in this application:

  • Display a form/menu with a gradient background.
  • Allow the form/menu to have transparency while keeping icons opaque.
  • Display icons that contain transparent backgrounds.
  • The Menu and Icons should be able to receive mouse-related events (hover, leave, click, dragover, dragdrop, and a few others).

This is the effect I'm shooting for: Desired Effect http://img207.imageshack.us/img207/5716/desired.jpg

This image shows the visual effects I'm trying to achieve. This was a skin I made for a program called Rainmeter. The image shows Notepad++ behind the skin with a few of the skin's files open in the editor. The menu is transparent, but the icons remain opaque.

My Approach:

Using a Form to act as the menu seemed like a logical first choice to me. I have a basic understanding of events. I'm not quite sure how to create my own click events, so a form would make working with events a tad easier. I considered a few options for the icons. I decided I'd use PictureBoxes for the icons, since they can hold images and receive events.

Once I finished the code for all the structural logic of my menu, I started playing around with it to try to get the visual effect I wanted. Form.Opacity affected the transparency of everything on the form. Since I want the icons to be fully opaque, I left this property alone. I tried setting the BackColor to Color.Transparent, but that gives an error. I played around with a few combinations... Combination Effects http://img204.imageshack.us/img204/757/effectsi.jpg

I drew the gradient with a Drawing2D.LinearGradientBrush into a Bitmap. This Bitmap was then placed as the Form.BackgroundImage or as a PictureBox.Image. If used, the PictureBox was sized to cover the entire Form and sent to the back.

I noticed that some of the Form.BackgroundColor would be mixed in with the outlines of my icons. The icons have transparency along the edges for a smoother appearance. Since the icons are picking up the Form's BackgroundColor, this makes me think that the PictureBoxes are creating new images when the icons are loaded into the form. The semi-transparent portions of the image are then merged with the Form's BackgroundColor when they should merge with whatever colors are behind the form.

effect with white desktop http://img838.imageshack.us/img838/8299/whitedesktop.jpg

In this image you can see the Fuchsia existing in the icons even though the Form's Fuchsia color is now completely transparent. I forgot to point out that the same green to yellow gradient with an Alpha value of 150 was used in every case. In the images where the gradient doesn't look green, it's because the transparent colors are blending with the Fuchsia background.

I'm not really sure what to do from here. I feel like I could get what I want if I could somehow make the Form alone completely transparent. I was also thinking I may have better luck just drawing the icons instead of using PictureBoxes. The problem then would be setting up the icons to receive mouse events. (I've never made my own events, and I think it would involved some Win32 API calls.)

Is there something else I can do with the PictureBoxes to get the effect I want? Whichever the case, I'm open to any ideas or suggestions for the overall effect I'm trying to achieve.

Community
  • 1
  • 1
Cheese
  • 1,429
  • 3
  • 16
  • 17

2 Answers2

14

This is pretty easy to do in Winforms. What you need is a sandwich of two forms. The bottom one should provide the transparent gradient background, the top one should draw the icons and handle mouse clicks. Some sample code:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
        this.TopMost = true;
        this.FormBorderStyle = FormBorderStyle.None;
        this.TransparencyKey = this.BackColor = Color.Fuchsia;
        this.Opacity = 0.3;
        var overlay = new Form();
        overlay.FormBorderStyle = FormBorderStyle.None;
        overlay.TransparencyKey = overlay.BackColor = Color.Fuchsia;
        overlay.StartPosition = FormStartPosition.Manual;
        overlay.Location = this.Location;
        overlay.MouseDown += HandleIconClick;
        this.Resize += delegate { overlay.Size = this.Size; };
        this.LocationChanged += delegate { overlay.Location = this.Location; };
        overlay.Paint += PaintIcons;
        this.Paint += PaintBackground;
        this.Load += delegate { overlay.Show(this); };
    }
    private void PaintBackground(object sender, PaintEventArgs e) {
        var rc = new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height);
        using (var br = new LinearGradientBrush(rc, Color.Gainsboro, Color.Yellow, 0f)) {
            e.Graphics.FillRectangle(br, rc);
        }
    }
    private void PaintIcons(object sender, PaintEventArgs e) {
        e.Graphics.DrawIcon(Properties.Resources.ExampleIcon1, 50, 30);
        // etc...
    }
    void HandleIconClick(object sender, MouseEventArgs e) {
        // TODO
    }
}

Which looks like this with the somewhat random colors and icon I selected:

enter image description here

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I write a Paint event very similar to your, but it doesn't work. So I tried your event and also it doesn't work. It's a very strange behaviour. – Omar Apr 22 '12 at 11:15
  • This is the result without setting the FormBoderStyle. [SnapShot](http://i41.tinypic.com/2lwb21k.jpg) – Omar Apr 22 '12 at 11:36
  • Well, the bottom form of the sandwich is visible. You didn't implement Paint to draw the gradient background and there's no sign of the top form, possibly because it didn't implement Paint either. Please start your own question if you need help. – Hans Passant Apr 22 '12 at 11:42
  • I've already implemented the Paint event. So I had another problem. But I solved it. Thank you. :) – Omar Apr 22 '12 at 11:57
  • Thanks for your suggestion. I never considered using different forms to handle various layers of the application. I still foresee an issue when using PictureBoxes like what I described in the third image above. This may be a limitation to using PictureBoxes. Would you recommend just painting all the icons with a graphics object? If I took that route, how should I handle receiving events? I can currently think of two possibilities. First, I can have a separate form for each icon to separate the events. Second, I can detect the location of an event in the single form. Then I'd calculate... – Cheese Apr 22 '12 at 16:55
  • ... which icon should be "receiving" the event and handle it appropriately. Are there any drawbacks to either of these solutions? Might you advise doing something else? – Cheese Apr 22 '12 at 16:57
  • 2
    Don't use PictureBoxes. They are just unnecessary and mess up the graphical effects. Just use the Paint event to draw whatever you want, as shown in the snippet. The only thing you need to do is detect that they are clicked. In the HandleIconClick method, you know where you drew the icons so you also know when e.Location overlaps an icon. Key point is to stop thinking that everything needs to be a control. – Hans Passant Apr 22 '12 at 16:59
  • I suppose I'll have to play around with events for a bit to get the hang of them. Using controls was my way of avoiding this for the time being. – Cheese Apr 22 '12 at 17:16
  • I ignored the transparency issue for a bit and started messing with it again tonight. Your approach gave me a result I had not yet achieved, but it's still not quite there. The background is transparent and the icon is opaque. However, the transparent colors in the icon are still blending with the form's BackColor. It seems to do the same thing whether I use PictureBoxes or draw the icons with graphics objects. http://img3.imageshack.us/img3/4265/icontransp.jpg – Cheese Apr 28 '12 at 05:47
  • You made it totally transparent, that's a problem. You will have to give up on anti-aliased edges. They can only work with a known background color, blending from the foreground to the background. Problem is, there is no consistent background. Or in other words, you have to use true icons, not bitmaps. – Hans Passant Apr 28 '12 at 08:27
  • I guess I'll leave it for now. I've got an idea for a solution that I think will work. I'll share it if I ever get it implemented. – Cheese Apr 28 '12 at 19:12
0

OK, I got a bit lost in all that, but from the description in the original paragraph, I would make sure the background rectangle is NOT the visual parent of the pictureboxes. Make them overlapping siblings, with the pictureboxes in front using Panel.Zindex.

Then you can just change the opacity of the rectangle, without affecting the icons. Also make sure the icon source image files have a transparent background.

Should work I think.

Jamona Mican
  • 1,574
  • 1
  • 23
  • 54
  • Note my answer should work in WPF. Not sure if things are different in Winforms. – Jamona Mican Apr 22 '12 at 10:56
  • Ah, I was just about to say that I've already pretty much handled what you described in your comment. My problem is that I can't get the transparency to go all the way through the form. I can get various elements on top of the form to behave the way I want them. However, the form's background has been giving me issues to give the entire application the transparency I want. – Cheese Apr 22 '12 at 16:42
  • (I hit enter to start a new paragraph for readability, but it ended the comment. I'll just start this new one.) I haven't tried anything in WPF yet. I've been seeing it used more often than in previous years when dealing with some graphics stuff. I think the xml (xaml?) portion of it is supposed to make setting things up a bit easier. I may try it out once I get this version of my project working. – Cheese Apr 22 '12 at 16:45
  • Yeah, I submitted my answer before I noticed you were not using WPF. I've done this type of thing before in WPF, but guess it does not apply. Good luck! – Jamona Mican Apr 22 '12 at 17:45