7

我一直在尝试显示具有透明边框的图像作为控件的背景。

不幸的是,透明区域在父窗体中创建了一个洞,如下所示:

在上图中,表单有一个红色背景,我希望在透明区域的控件后面看到它。

我使用的代码如下:

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        if (this.Image != null)
        {
            Graphics g = Graphics.FromImage(this.Image);

            ImageAttributes attr = new ImageAttributes();

            //set the transparency based on the top left pixel
            attr.SetColorKey((this.Image as Bitmap).GetPixel(0, 0), (this.Image as Bitmap).GetPixel(0, 0));

            //draw the image using the image attributes.
            Rectangle dstRect = new Rectangle(0, 0, this.Image.Width, this.Image.Height);

            e.Graphics.DrawImage(this.Image, dstRect, 0, 0, this.Image.Width, this.Image.Height,
                GraphicsUnit.Pixel, attr);
        }
        else
        {
            base.OnPaint(e);
        }
    }

    protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
    {
        //base.OnPaintBackground(e);
    }

这个类是从 PictureBox 继承的,因为我需要一个实现 OnMouseMove 和 OnMouseUp 事件的控件。

我一天中的大部分时间都在研究,但没有成功测试出不同的想法,但不幸的是,大多数人只在完整的框架上工作,而不是在 .Net CF 上工作。

任何想法将不胜感激。

4

1 回答 1

6

啊,CF透明的乐趣。我可以继续讨论它(并且在我的博客和我多年前所做的Project Resistance 代码中有)。

要点是这样的。子控件必须绘制它的区域,但首先它必须回调它的父级(在您的情况下为 Form)并告诉它在除子剪辑区域之外的任何地方重绘它的背景图像,然后在上面绘制自己. 如果这听起来有点令人困惑,那是因为它是。

例如,如果您查看Project Resistance,一个视图(它只是一个控件)会绘制一个电阻器和带。它位于具有图像背景的表单中,并且该背景需要“显示”电阻器的透明区域:

在此处输入图像描述

所以在电阻器的绘图代码中它这样做:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    try
    {
        RECT rect = new RECT(this.Bounds);

        // draw the blank
        Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_blankImage, Bounds, 
              new Rectangle(0, 0, m_blankImage.Width, m_blankImage.Height));

        if (m_bandsImage != null)
        {
            // draw the bands
            Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_bandsImage, Bounds, 
                 new Rectangle(0, 0, m_bandsImage.Width, m_bandsImage.Height));
        }
    }
    finally
    {
    }

    if (!Controller.TouchMode)
    {
        // TODO: draw in the selection arrow
        // Controller.SelectedBand
    }
}

这很简单。关键是它调用了它的基础 OnPaint,它执行以下操作:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    // this assumes we're in a workspace, on MainForm (the whole Parent.Parent thing)
    IBackgroundPaintProvider bgPaintProvider = Parent.Parent as IBackgroundPaintProvider;
    if (bgPaintProvider != null)
    {
        Rectangle rcPaint = e.ClipRectangle;
        // use the parent, since it's the workspace position in the Form we want, 
        // not our position in the workspace
        rcPaint.Offset(Parent.Left, Parent.Top);
        bgPaintProvider.PaintBackground(e.Graphics, e.ClipRectangle, rcPaint);
    }
}

您可以看到它正在调用PaintBackground包含表单(在这种情况下它是 Parent.Parent,因为 Control 实际上位于一个称为 Workspace 的容器中 - 在您的情况下您不需要走两次)。这会在您当前视为“洞”的区域中绘制背景图像

public void PaintBackground(Graphics g, Rectangle targetRect, Rectangle sourceRect)
{
    g.DrawImage(m_bmBuffer, targetRect, sourceRect, GraphicsUnit.Pixel);
}
于 2012-06-30T19:07:47.077 回答