可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Im just looking for an answer in my simple problem. Here it is
I have a pricturebox that has image with transparent background
i Set the picturebox backcolor
as transparent.
and after that, the picture has transparent bg
. But after i added this code
ìmg1.Left = windows.forms.cursor.Position.X - me.Left
ìmg1.Top= windows.forms.cursor.Position.Y - me.Top
'code for using img as cursor
the image bg is not really transparent like this

I think the transaparent backcolor
is not really transparent. it will only get the backcolor
of form and use it as backcolor
of image instead of transparent.
Is there any solution to make it fully transparent?
回答1:
You are correct in your assumption.
Transparency in winforms does not mean that the object is actually transparent. Instead, it means that it will display it's parent object instead of it's background, including it's background, images and text, but not including any other controls on it, hence your problem.
Since the parent control of your top most picture box is not and can not be the other picture boxes, the fact that your top most picture box have a transparent background will not help.
Unfortunately, using the form's TransparencyKey
property will also not help for this. (It will make the selected color transparent, but will yield unexpected (and usually undesired) results.
In order to achieve your goal, you will have to follow OneFineDay's advice in the comments, and use Graphics
to draw the image yourself.
Fortunately, this is very easy to do:
Public Sub DrawImage(Image as Image, Location As Point) Using(Dim g as Graphics = Me.CreateGraphics()) g.DrawImage(Image, Location) EndUsing End Sub
回答2:
Drawing the image using a graphics object is the recommended procedure if you're going to use it as a cursor. But if you sometime want to use a PictureBox (for reasons like being able to quickly change image using it's Image
property, etc), that is possible too.
This code will draw a better "transparent" background, by drawing each control behind your PictureBox on it's background.
How to use:
1) Create a custom component.
2) Put Inherits PictureBox
below the Public Class ...
line.
3) Paste this code inside the class:
Protected Overrides Sub OnPaintBackground(e As System.Windows.Forms.PaintEventArgs) MyBase.OnPaintBackground(e) If Parent IsNot Nothing Then 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) If c.Bounds.IntersectsWith(Bounds) AndAlso c.Visible = True Then Dim bmp As New Bitmap(c.Width, c.Height, e.Graphics) c.DrawToBitmap(bmp, c.ClientRectangle) e.Graphics.TranslateTransform(c.Left - Left, c.Top - Top) e.Graphics.DrawImageUnscaled(bmp, Point.Empty) e.Graphics.TranslateTransform(Left - c.Left, Top - c.Top) bmp.Dispose() End If Next End If End Sub
4) Build your project.
5) Select your class from the toolbox and add it to your form/usercontrol.
回答3:
This blog article inspired this SO answer. These were the basis for a more robust control with scaling, text, contentalignment etc.
The following is a scaled back version (in VB) to primarily implement the appearance of true transparency. The core painting is nearly identical to the original SO post except to account for a border in the painting. A few control level features have also been retained.
'Namespace omitted to reduce indentation Imports System.Windows.Forms Imports System.Drawing Imports System.ComponentModel Imports System.Drawing.Drawing2D Public Class TransPicBox Inherits PictureBox Public Enum ImageSizing None Stretch Scale End Enum Public Sub New() ' defaults for a new one MyBase.BackColor = Color.Transparent MyBase.InitialImage = Nothing MyBase.ErrorImage = Nothing MyBase.Image = Nothing End Sub Public Overloads Property Image As Image Get Return MyBase.Image End Get Set(value As Image) MyBase.Image = value InvalidateParent() End Set End Property Private imgSizing As ImageSizing = ImageSizing.None Public Property ImageSizing As ImageSizing Get Return imgSizing End Get Set(value As ImageSizing) imgSizing = value InvalidateParent() End Set End Property ' because the child control displays are interdependent ' tell the parent to update when some things change ' Image, Scaling, Border, Text, BackColor etc Private Sub InvalidateParent() Invalidate() If MyBase.Parent IsNot Nothing Then MyBase.Parent.Invalidate() End If End Sub ' since the display depends on ZOrder, provide ' a control method to alter it Public Sub MoveUpZOrder() ChangeZOrder(-1) End Sub Public Sub MoveDownZOrder() ChangeZOrder(+1) End Sub Private Sub ChangeZOrder(value As Int32) Dim ndx As Integer = Parent.Controls.GetChildIndex(Me) If ((ndx + value) >= 0) AndAlso ((ndx + value) Public Shadows Property ErrorImage As Image Protected Overrides Sub OnPaintBackground(pevent As PaintEventArgs) If MyBase.BackColor = Color.Transparent Then ' magic happens here! PaintSiblings(pevent) Else ' do nothing special when the backcolor is not Transparent MyBase.OnPaintBackground(pevent) End If End Sub ' code for painting the image Protected Overrides Sub OnPaint(pe As PaintEventArgs) Dim rect As Rectangle If (MyBase.Image IsNot Nothing) Then rect = GetImgRect(Bounds) pe.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic pe.Graphics.CompositingQuality = CompositingQuality.HighQuality pe.Graphics.SmoothingMode = SmoothingMode.HighQuality pe.Graphics.DrawImage(Image, rect) End If End Sub Private Sub PaintSiblings(e As PaintEventArgs) ' need to access the parent' controls collection If (Parent IsNot Nothing) Then Dim borderSize As Integer = 0 Dim thisLeft As Single = -Left Dim thisTop As Single = -Top ' fix Select Case MyBase.BorderStyle Case BorderStyle.FixedSingle borderSize = SystemInformation.BorderSize.Width Case BorderStyle.Fixed3D borderSize = SystemInformation.Border3DSize.Width End Select ' Shift ClipBounds to form relative coords e.Graphics.TranslateTransform(thisLeft, thisTop) ' Get Parent to paint the part behind us: ' we cant know if thats been done or not Using pea As New PaintEventArgs(e.Graphics, e.ClipRectangle) InvokePaintBackground(Parent, pea) InvokePaint(Parent, pea) End Using ' shift back e.Graphics.TranslateTransform(-thisLeft, -thisTop) ' starting control index is...well, ours Dim startAt As Integer = Parent.Controls.GetChildIndex(Me) Dim ctl As Control ' Controls are in z-Order, so loop ' thru the controls "behind" me For n As Int32 = Parent.Controls.Count - 1 To startAt + 1 Step -1 ctl = Parent.Controls(n) ' skip if they are invisible, too small or do not overlap me If (ctl.Visible = False OrElse ctl.Width = 0 OrElse ctl.Height = 0 OrElse Bounds.IntersectsWith(ctl.Bounds) = False) Then Continue For Else Using bmp As New Bitmap(ctl.Width, ctl.Height, e.Graphics) ' draw this sibling to a bitmap ctl.DrawToBitmap(bmp, New Rectangle(0, 0, ctl.Width, ctl.Height)) ' shift the orientation relative to sibling and draw it thisLeft = ctl.Left - Left thisTop = ctl.Top - Top 'offset, then draw the image, reset e.Graphics.TranslateTransform(thisLeft - borderSize, thisTop - borderSize) e.Graphics.DrawImageUnscaled(bmp, New Point(0, 0)) e.Graphics.TranslateTransform(-thisLeft + borderSize, -thisTop + borderSize) End Using End If Next Else ' not sure how this could happen Using br As New SolidBrush(MyBase.BackColor) e.Graphics.FillRectangle(br, ClientRectangle) End Using End If End Sub ' image scaling is mainly a matter of the size and location ' of the img rect we use in Paint Private Function GetImgRect(destRect As Rectangle) As Rectangle Dim pt As New Point(0, 0) Dim sz As Size If MyBase.Image IsNot Nothing Then Select Case Me.ImageSizing Case ImageSizing.None sz = Image.Size Case ImageSizing.Scale If Width > Height Then sz = New Size(GetScaledWidth(Height), Height) Else sz = New Size(Width, GetScaledHeight(Width)) End If Case ImageSizing.Stretch sz = Me.Size End Select End If ' ToDo: move the pt if you add an Image ContentAlignment ' (Top, TopLeft, BottomRight...) property Return New Rectangle(pt, sz) End Function Private Function GetScaledWidth(h As Integer) As Integer Dim scale As Single = CSng(Image.Width / Image.Height) Return CInt(h * scale) End Function Private Function GetScaledHeight(w As Integer) As Integer Dim scale As Single = CSng(Image.Height / Image.Width) Return CInt(w * scale) End Function End Class
How to Use It
- Create a Class Library
- Replace the new class boilerplate code with the above
- Add references for the NameSpaces listed in the
Imports
statements - Build the Library. A new
TransPicBox
should show in the toolbox.
You can also just include the class code file in your project and rebuild to avoid a DLL dependency which includes just one thing. Results:

- Four small PNGs under a larger one; all are on a Panel (rose BG)
- The TopLeft and BottomRight images use a Transparent BG, the other 2 do not
- They all have the border turned on to show the client area; the BL Clock uses a 3D border
- The larger PNG uses
ImageSizing.Scale
to be about 150% larger
The parent backcolor shows thru the TL and BR images as well as the overlapping larger one in front. The control will display BMP and JPGs normally and still show whats behind empty areas (if any) when the control's back color is Transparent.
Notes:
- This is a fairly expensive
Paint
. Each time one of these needs to be painted, at least a portion of the parent and every control under it must be repainted as well. - When moving a
TransPicBox
in the form designer, VS temporarily brings the control to the front, so the display is temporarily whacked - Windows ambles on normally so things behind your special control are still hidden as far as it is concerned. The image below shows that the portions of a button partially behind a
TransPicBox
will not 'glow' when the mouse is over it. As far as Windows knows, that portion of it cant be seen so it is not repainted.
