3

I've modified the SuperContextMenuStrip found at CodeProject to meet some of my projects needs. I'm using it as a tooltip for map markers on a GMap.NET Map Control. Here is a sample of what it looks like:

enter image description here

What I would like to do is pretty this up a little by making it look more like a bubble. Similar to an old Google Maps stytle tooltip:

enter image description here

I've spent some time searching on control transparency and I know this isn't an easy thing. This SO question in particular illustrates that.

I have considered overriding the OnPaint method of the SuperContextMenuStrip to draw a background of the GMap.NET control that is underneath the SuperContextMenuStrip, but even that would fail in cases where the marker is hanging off the GMap.NET control:

enter image description here

What is the correct way to create the type of transparency I am looking for?

Community
  • 1
  • 1
Michael Mankus
  • 4,628
  • 9
  • 37
  • 63
  • This answer might help you - I am unsure... http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/c71b3076-dca6-48ac-9b19-45f58346b9b1?persist=True – MoonKnight Sep 25 '12 at 11:38
  • Make it a Form instead, display it with the Show(owner) overload. Use its TransparencyKey and Opacity key for effects. – Hans Passant Sep 25 '12 at 12:23
  • @HansPassant - many years since this comment, but making it a form means by moving the app between monitors it's not moving automatically. 2. when doing alt+tab - it get's a tab. 3. z order of forms isn't promissed, so I might get a funny effect of forms being between the "suprt context menu" and the main form. That's why user control should be the correct way, and setting TransparentKey to the main form instead, but this doesn't work for me for any reason... – ephraim Aug 20 '23 at 15:10
  • @HansPassant - I've made my case a question, if you can help - I'd really appreciate it! https://stackoverflow.com/questions/76939939/c-sharp-net-setting-transparentkey-results-unexpected-error-incorrect-paramete – ephraim Aug 20 '23 at 15:10

1 Answers1

4

In Windows Forms, you achieve transparency (or draw irregularly shaped windows) by defining a region. To quote MSDN

The window region is a collection of pixels within the window where the operating system permits drawing.

In your case, you should have a bitmap that you will use as a mask. The bitmap should have at least two distinct colors. One of these colors should represent the part of the control that you want to be transparent.

You would then create a region like this:

// this code assumes that the pixel 0, 0 (the pixel at the top, left corner) 
// of the bitmap passed contains the color you  wish to make transparent.

       private static Region CreateRegion(Bitmap maskImage) {
           Color mask = maskImage.GetPixel(0, 0);
           GraphicsPath grapicsPath = new GraphicsPath(); 
           for (int x = 0; x < maskImage.Width; x++) {
               for (int y = 0; y < maskImage.Height; y++) {
                   if (!maskImage.GetPixel(x, y).Equals(mask)) {
                           grapicsPath.AddRectangle(new Rectangle(x, y, 1, 1));
                       }
                   }
           }

           return new Region(grapicsPath);
       }

You would then set the control’s Region to the Region returned by the CreateRegion method.

this.Region = CreateRegion(YourMaskBitmap);

to remove the transparency:

this.Region = new Region();

As you can probably tell from the code above, creating regions is expensive resource-wise. I'd advice saving regions in variables should you need to use them multiple times. If you use cached regions this way, you'd soon experience another problem. The assignment would work the first time but you would get an ObjectDisposedException on subsequent calls.

A little investigation with refrector would reveal the following code within the set accessor of the Region Property:

         this.Properties.SetObject(PropRegion, value);
            if (region != null)
            {
                region.Dispose();
            }

The Region object is disposed after use! Luckily, the Region is clonable and all you need to do to preserve your Region object is to assign a clone:

private Region _myRegion = null;
private void SomeMethod() {
    _myRegion = CreateRegion(YourMaskBitmap);            
}

private void SomeOtherMethod() {
    this.Region = _myRegion.Clone();
}
Gichamba
  • 997
  • 12
  • 16
  • 1
    You are the man! I had never played around with the `Region` property of a `Control`. And in all my online searching for an answer to this question, it's never come up. It works perfectly. In my case, however, I am not using a `Bitmap` to generate the `GraphicsPath.` Instead, I am drawing the `GraphicsPath` directly. Mine is quite simple - it is a rounded rectangle with a little point in the lower left corner. I used this code for drawing the rounded rectangle. http://www.switchonthecode.com/tutorials/csharp-creating-rounded-rectangles-using-a-graphics-path Thanks again! – Michael Mankus Nov 27 '12 at 12:30