Вопрос

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?

Это было полезно?

Решение

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();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top