创建控件的透明部分以查看其下方的控件

3

我修改了在CodeProject找到的SuperContextMenuStrip以满足我的一些项目需求。我将其用作GMap.NET Map Control上地图标记的工具提示。这是它的样子:

enter image description here

我想要做的是通过使其看起来更像气泡来美化它。类似于旧版Google Maps样式的工具提示:

enter image description here

我花了一些时间搜索控制透明度的方法,我知道这不是一件容易的事情。特别是这个Stack Overflow问题说明了这一点。 我考虑重写SuperContextMenuStripOnPaint方法来绘制GMap.NET控件下方的背景,但即使在标记挂在GMap.NET控件之外的情况下,这种方法也会失败。

enter image description here

什么是创建我所需要的透明度的正确方法?

这个答案可能会对你有所帮助 - 我不确定... http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/c71b3076-dca6-48ac-9b19-45f58346b9b1?persist=True - MoonKnight
改为窗体,使用 Show(owner) 重载进行显示。使用 TransparencyKey 和 Opacity 属性来实现效果。 - Hans Passant
@HansPassant - 这条评论已经过去了很多年,但是将其变成一个表单意味着通过在监视器之间移动应用程序时它不会自动移动。2. 当执行alt+tab时,它会得到一个标签。3. 表单的z顺序没有保证,所以我可能会得到一个有趣的效果,即表单位于“超级上下文菜单”和主表单之间。 这就是为什么用户控件应该是正确的方式,并且将TransparentKey设置为主表单,但出于某种原因对我来说这并不起作用... - ephraim
@HansPassant - 我把我的问题提出来了,如果你能帮忙的话,我会非常感激!https://stackoverflow.com/questions/76939939/c-sharp-net-setting-transparentkey-results-unexpected-error-incorrect-paramete - ephraim
1个回答

4
在Windows Forms中,您可以通过定义区域来实现透明度(或绘制形状不规则的窗口)。引用MSDN的话来说:
“窗口区域是窗口内允许绘图的一组像素。”
在您的情况下,您应该拥有一个位图,您将使用它作为掩码。该位图应至少具有两种不同的颜色。其中一种颜色应代表您想要透明的控件部分。
然后,您可以创建如下区域:
// 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);
       }

你需要将控件的Region设置为CreateRegion方法返回的区域。
this.Region = CreateRegion(YourMaskBitmap);

去除透明度:

this.Region = new Region();

你可能已经从上面的代码中看出来了,创建区域在资源方面是很昂贵的。我建议,如果需要多次使用它们,可以将区域保存在变量中。如果以这种方式使用缓存区,很快就会遇到另一个问题。赋值第一次可以有效,但在后续调用中会出现ObjectDisposedException异常。

通过反射器进行简单的调查,你会发现Region属性的set访问器中有以下代码:

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

使用完 Region 对象后应该将其释放!幸运的是,Region 对象是可克隆的,你只需要分配一个克隆即可保留原有的 Region 对象:

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

private void SomeOtherMethod() {
    this.Region = _myRegion.Clone();
}

1
你太棒了!我从未尝试过使用ControlRegion属性。在我的在线搜索中,从来没有出现过这个问题的答案。但它完美地解决了我的问题。然而,在我的情况下,我没有使用Bitmap来生成GraphicsPath。相反,我直接绘制了GraphicsPath。我的代码非常简单——它是一个带有左下角小点的圆角矩形。我使用了这段代码来绘制圆角矩形。 http://www.switchonthecode.com/tutorials/csharp-creating-rounded-rectangles-using-a-graphics-path 再次感谢! - Michael Mankus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接