简体   繁体   English

C#Picturebox透明背景似乎不起作用

[英]C# Picturebox transparent background doesn't seem to work

For a project of mine I need images to display with a transparent background. 对于我的项目,我需要使用透明背景显示图像。 I made some .png images that have a transparent background(to check this I opened them in Photoshop). 我制作了一些具有透明背景的.png图像(为了检查这一点,我在Photoshop中打开它们)。 Now I have a class that extends PictureBox: 现在我有一个扩展PictureBox的类:

class Foo : PictureBox
{
    public Foo(int argument)
        : base()
    {
        Console.WriteLine(argument);//different in the real application of course.
        //MyProject.Properties.Resources.TRANSPARENCYTEST.MakeTransparent(MyProject.Properties.Resources.TRANSPARENCYTEST.GetPixel(1,1)); //<-- also tried this
        this.Image = MyProject.Properties.Resources.TRANSPARENCYTEST;
        ((Bitmap)this.Image).MakeTransparent(((Bitmap)this.Image).GetPixel(1, 1));
        this.SizeMode = PictureBoxSizeMode.StretchImage;
        this.BackColor = System.Drawing.Color.Transparent;
    }
}

this however just displays the picturebox with a white background, I just can't seem to make it work with a transparent background. 然而,这只是显示带有白色背景的图片框,我似乎无法使其与透明背景一起工作。

If you want to overlay images over images (and not images over form), this would make the trick: 如果你想在图像上叠加图像(而不是在图像上叠加图像),这就可以了:

overImage.Parent = backImage;
overImage.BackColor = Color.Transparent;
overImage.Location = thePointRelativeToTheBackImage;

Where overImage and backImage are PictureBox with png (with transparent background). 其中overImage和backImage是带有png的PictureBox(具有透明背景)。

This is because, as said before, the transparency of an image is rendered using the back color of the Parent container. 这是因为,如前所述,使用父容器的背景颜色渲染图像的透明度。 PictureBoxes haven't a "Parent" property so you have to make it manually (or create a cutom control of course). PictureBoxes没有“Parent”属性,所以你必须手动创建它(或者当然创建一个cutom控件)。

It probably works perfectly. 它可能完美无缺。 You are seeing what's behind the picture box control. 您正在看到图片框控件背后的内容。 Which is the form. 哪种形式。 Whose BackColor is probably white. 谁的BackColor可能是白色的。 You can set the form's BackgroundImage property to be sure, you should see the image through the picture box. 您可以设置表单的BackgroundImage属性以确保,您应该通过图片框看到图像。 Like this: 像这样:

在此输入图像描述

Punching a hole through both the picture box and the form requires a bigger weapon, Form.TransparencyKey 通过两个图片框冲压孔形式需要一个更大的武器,Form.TransparencyKey

There's an excellent solution on the CodeProject website at CodeProject网站上有一个很好的解决方案

Making Transparent Controls - No Flickering 制作透明控制 - 无闪烁

essentially the trick is to override the paintbackground event so as to loop through all the controls underlying the picturebox and redraw them. 本质上的技巧是覆盖paintbackground事件,以便遍历图片框底层的所有控件并重绘它们。 The function is:- 功能是: -

protected override void OnPaintBackground(PaintEventArgs e)
       // Paint background with underlying graphics from other controls
   {
       base.OnPaintBackground(e);
       Graphics g = e.Graphics;

       if (Parent != null)
       {
           // Take each control in turn
           int index = Parent.Controls.GetChildIndex(this);
           for (int i = Parent.Controls.Count - 1; i > index; i--)
           {
               Control c = Parent.Controls[i];

               // Check it's visible and overlaps this control
               if (c.Bounds.IntersectsWith(Bounds) && c.Visible)
               {
                   // Load appearance of underlying control and redraw it on this background
                   Bitmap bmp = new Bitmap(c.Width, c.Height, g);
                   c.DrawToBitmap(bmp, c.ClientRectangle);
                   g.TranslateTransform(c.Left - Left, c.Top - Top);
                   g.DrawImageUnscaled(bmp, Point.Empty);
                   g.TranslateTransform(Left - c.Left, Top - c.Top);
                   bmp.Dispose();
               }
           }
       }
   }

I know your Question is founded in C# , but due to the similarity, & ease of conversion from VB.NET , I'll add a full VB version that also allows updating of the control's background as you move it around. 我知道你的问题是建立在C#中的 ,但是由于VB.NET的相似性和易于转换,我将添加一个完整的VB版本,它还允许在移动时更新控件的背景。

You already have an answer, but this is for others who find this post by search engines, & would like a VB version, or simply want to find a FULL convertible sample if they also need it in C# . 你已经有了答案,但这适用于其他通过搜索引擎找到这篇文章的人,想要一个VB版本,或者只是想在C#中找到一个完全可转换的样本。

Create a new Custom Control Class , & paste the following into it... overwriting the default class stuff: 创建一个新的自定义控件类 ,并将以下内容粘贴到其中...覆盖默认类的东西:

Custom Control Class: 自定义控件类:

Public Class TransparentPictureBox
    Private WithEvents refresher As Timer
    Private _image As Image = Nothing

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        refresher = New Timer()
        'refresher.Tick += New EventHandler(AddressOf Me.TimerOnTick)
        refresher.Interval = 50
        refresher.Start()

    End Sub

    Protected Overrides ReadOnly Property CreateParams() As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H20
            Return cp
        End Get
    End Property

    Protected Overrides Sub OnMove(ByVal e As EventArgs)
        MyBase.OnMove(e)
        MyBase.RecreateHandle()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        'Add your custom paint code here
        If _image IsNot Nothing Then
            e.Graphics.DrawImage(_image, CInt(Width / 2) - CInt(_image.Width / 2), CInt(Height / 2) - CInt(_image.Height / 2))
        End If
    End Sub


    Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
        ' Paint background with underlying graphics from other controls
        MyBase.OnPaintBackground(e)
        Dim g As Graphics = e.Graphics

        If Parent IsNot Nothing Then
            ' Take each control in turn
            Dim index As Integer = Parent.Controls.GetChildIndex(Me)
            For i As Integer = Parent.Controls.Count - 1 To index + 1 Step -1
                Dim c As Control = Parent.Controls(i)

                ' Check it's visible and overlaps this control
                If c.Bounds.IntersectsWith(Bounds) AndAlso c.Visible Then
                    ' Load appearance of underlying control and redraw it on this background
                    Dim bmp As New Bitmap(c.Width, c.Height, g)
                    c.DrawToBitmap(bmp, c.ClientRectangle)
                    g.TranslateTransform(c.Left - Left, c.Top - Top)
                    g.DrawImageUnscaled(bmp, Point.Empty)
                    g.TranslateTransform(Left - c.Left, Top - c.Top)
                    bmp.Dispose()
                End If
            Next
        End If
    End Sub

    Public Property Image() As Image
        Get
            Return _image
        End Get
        Set(value As Image)
            _image = value
            MyBase.RecreateHandle()
        End Set
    End Property

    Private Sub refresher_Tick(sender As Object, e As System.EventArgs) Handles refresher.Tick
        MyBase.RecreateHandle()
        refresher.Stop()
    End Sub
End Class

...save the class, then clean your project, & build again. ...保存课程,然后清理您的项目,然后重新构建。 The new control should appear as a new tool Item. 新控件应显示为新工具项。 Find it, & drag it to your form. 找到它,然后将其拖到表单中。

I have had issues with this control though... It happens when I try to load an animated "Loading" .gif image. 我有这个控件的问题...当我尝试加载动画“加载” .gif图像时会发生这种情况。

The image does not animate, & also has display problems when you hide the control, then try to display it again. 图像没有动画,并且在隐藏控件时也会出现显示问题,然后再次尝试显示它。

Sort those issues out, & you'll have a perfect Custom Control Class. 排除这些问题,您将拥有一个完美的自定义控件类。 :) :)

EDIT: 编辑:

I have no idea if the following will work in C# 's IDE or not, but here's my attempt at conversion: 我不知道以下是否可以在C#的IDE中运行,但这是我尝试转换:

using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class TransparentPictureBox
{
    private Timer withEventsField_refresher;
    private Timer refresher {
        get { return withEventsField_refresher; }
        set {
            if (withEventsField_refresher != null) {
                withEventsField_refresher.Tick -= refresher_Tick;
            }
            withEventsField_refresher = value;
            if (withEventsField_refresher != null) {
                withEventsField_refresher.Tick += refresher_Tick;
            }
        }
    }

    private Image _image = null;

    public TransparentPictureBox()
    {
        // This call is required by the designer.
        InitializeComponent();

        // Add any initialization after the InitializeComponent() call.
        refresher = new Timer();
        //refresher.Tick += New EventHandler(AddressOf Me.TimerOnTick)
        refresher.Interval = 50;
        refresher.Start();

    }

    protected override CreateParams CreateParams {
        get {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | 0x20;
            return cp;
        }
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        base.RecreateHandle();
    }

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

        //Add your custom paint code here
        if (_image != null) {
            e.Graphics.DrawImage(_image, Convert.ToInt32(Width / 2) - Convert.ToInt32(_image.Width / 2), Convert.ToInt32(Height / 2) - Convert.ToInt32(_image.Height / 2));
        }
    }


    protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
    {
        // Paint background with underlying graphics from other controls
        base.OnPaintBackground(e);
        Graphics g = e.Graphics;

        if (Parent != null) {
            // Take each control in turn
            int index = Parent.Controls.GetChildIndex(this);
            for (int i = Parent.Controls.Count - 1; i >= index + 1; i += -1) {
                Control c = Parent.Controls(i);

                // Check it's visible and overlaps this control
                if (c.Bounds.IntersectsWith(Bounds) && c.Visible) {
                    // Load appearance of underlying control and redraw it on this background
                    Bitmap bmp = new Bitmap(c.Width, c.Height, g);
                    c.DrawToBitmap(bmp, c.ClientRectangle);
                    g.TranslateTransform(c.Left - Left, c.Top - Top);
                    g.DrawImageUnscaled(bmp, Point.Empty);
                    g.TranslateTransform(Left - c.Left, Top - c.Top);
                    bmp.Dispose();
                }
            }
        }
    }

    public Image Image {
        get { return _image; }
        set {
            _image = value;
            base.RecreateHandle();
        }
    }

    private void refresher_Tick(object sender, System.EventArgs e)
    {
        base.RecreateHandle();
        refresher.Stop();
    }
}

Try it out, & see for yourself i guess :P 尝试一下,我自己看看:P

ps: I'm not a guru, so expect all kinds of mistakes in both the C#, & VB.NET versions. ps:我不是大师,所以期待C#和VB.NET版本中的各种错误。 lol 大声笑

如果您在图片框中显示带有透明度的png,它将自动考虑透明度,因此您无需设置透明色

The above answers seem to solve your problem. 以上答案似乎可以解决您的问题。 You are indeed seeing what's behind the picture box control - the form itself with backColor white. 你确实看到了图片框控件背后的东西 - 表格本身带有backColor white。 I have here created a simple function that first converts an image of byte type (array) into a bitmap and thereafter setting specific colors (from the bitmap pic) to transparent. 我在这里创建了一个简单的函数,它首先将字节类型(数组)的图像转换为位图,然后将特定颜色(从位图pic)设置为透明。 Something you might as well use: 你不妨使用的东西:

  using System; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; 
    public void LogoDrawTransparent(PaintEventArgs e)
    {
        // Create a Bitmap object from an image file.
        Image myImg;
        Bitmap myBitmap;

        try
        {
            myImg = cls_convertImagesByte.GetImageFromByte(newImg);
            myBitmap = new Bitmap(myImg); // @"C:\Temp\imgSwacaa.jpg");  

            // Get the color of a background pixel.
            Color backColor = myBitmap.GetPixel(0, 0); // GetPixel(1, 1); 
            Color backColorGray = Color.Gray;
            Color backColorGrayLight = Color.LightGray;
            Color backColorWhiteSmoke = Color.WhiteSmoke;
            Color backColorWhite = Color.White;
            Color backColorWheat = Color.Wheat;

            // Make backColor transparent for myBitmap.
            myBitmap.MakeTransparent(backColor);
                    // OPTIONALLY, you may make any other "suspicious" back color transparent (usually gray, light gray or whitesmoke)
            myBitmap.MakeTransparent(backColorGray);
            myBitmap.MakeTransparent(backColorGrayLight);
            myBitmap.MakeTransparent(backColorWhiteSmoke);

            // Draw myBitmap to the screen.
            e.Graphics.DrawImage(myBitmap, 0, 0, pictureBox1.Width, pictureBox1.Height); //myBitmap.Width, myBitmap.Height);
        }
        catch
        {
            try { pictureBox1.Image = cls_convertImagesByte.GetImageFromByte(newImg); }
            catch { } //must do something
        }
    }

You may fire this func on Paint of the pictureBox. 你可以在pictureBox的Paint上触发这个函数。 This is my class that is referenced n the function above: 这是我在上面的函数中引用的类:

    class cls_convertImagesByte
{

    public static Image GetImageFromByte(byte[] byteArrayIn)
    {
        MemoryStream ms = new MemoryStream(byteArrayIn);
        Image returnImage = Image.FromStream(ms);
        return returnImage;
    }

    public static byte[] GetByteArrayFromImage(System.Drawing.Image imageIn)
    {
        MemoryStream ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
        return ms.ToArray();
    }
}

Thanks. 谢谢。 Chagbert Chagbert

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM