I'm filling the background of some VB.NET 2005 WinForms form with a nice pretty gradient fill (by overriding the OnPaint event). This works fine but the various labels etc on the form show with a solid background even after I set the BackColor to Color.Transparent. Setting the transparency key of the form itself seems to affect this but I cannot get the labels to have a truely transparent BackColor, is there an easy way to get around this or am I looking at custom controls etc?
Add a new class to your project and paste the code shown below. Build. Drop the new control from the top of your toolbox onto your form.
Public Class TransparentLabel
Inherits Label
Public Sub New()
Me.SetStyle(ControlStyles.Opaque, True)
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, False)
End Sub
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or &H20 ' Turn on WS_EX_TRANSPARENT
Return cp
End Get
End Property
End Class
The flicker might be noticeable, no fix.
After some experimentation I've found that the following works for gradiant filling form backgrounds and preserving label transparency:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim formGraphics As Graphics = e.Graphics
Dim gradientBrush As New LinearGradientBrush(New Point(0, Height), New Point(0, 0), Me.AppSettings.FormGradiantFrom, Me.AppSettings.FormGradiantTo)
formGraphics.FillRectangle(gradientBrush, ClientRectangle)
End Sub
And in the form load event:
SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer Or _
ControlStyles.ResizeRedraw Or ControlStyles.UserPaint, True)
Related
I have two labels in my form which are placed side-by-side to act as one label. When I hover over the labels, I have a function that fades the labels to different colours, which works well. I am trying to apply the MouseHover and MouseLeave event to both labels, so that when I hover over Label1 and move to Label2 (and vice versa), the function doesn't then fade the colour back to the original colour. Currently, moving between the two labels activates MouseLeave followed by MouseHover again in the new label.
I have tried to add both labels to the event trigger, but this hasn't worked. I have also tried placing both labels in a Panel, but that then doesn't trigger the event.
Private Sub fadeHeaderIn(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Label1.MouseHover, Label2.MouseHover
Call fadeLabel("In")
End Sub
Private Sub fadeHeaderOut(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Label1.MouseLeave, Label2.MouseLeave
Call fadeLabel("Out")
End Sub
If the functionality to change the colour of part of the label existed, I'd not need the two labels, so if there is a better way of doing this altogether, I'm happy to do so. Thanks!
I have also tried placing both labels in a Panel, but that then
doesn't trigger the event.
That should work. The panel would act as the boundaries for both labels. You'll get a MouseLeave, though, when you move from the panel to the labels contained within. To prevent a false trigger, simply check if the mouse is still within the bounds of the panel. You can prevent multiple fade ins when moving from label to label by tracking the faded state with a boolean. It'd look something like this:
Public Faded As Boolean = False
Private Sub fadeHeaderIn(sender As Object, e As EventArgs) Handles Label1.MouseHover, Label2.MouseHover
If Not Faded Then
Faded = True
fadeLabel("In")
End If
End Sub
Private Sub fadeHeaderOut(sender As Object, e As EventArgs) Handles Panel1.MouseLeave
If Not Panel1.ClientRectangle.Contains(Panel1.PointToClient(Cursor.Position)) Then
If Faded Then
Faded = False
fadeLabel("Out")
End If
End If
End Sub
Here's a sample of what has been described in comments.
The Text of a control (here, derived from a standard Label) is split in two sections of the same measure. Each section can have a different color.
The active and inactive colors are custom public properties, they can be set in the designer.
Each section is tracked, meaning that the control is aware of what side the Mouse Pointer is currently hovering.
The size of the text is measured using the TextRenderer.MeasureText method. This size is used to calculate the rectangles that include the sections of text.
The Rectangle.Contains([Point]) method is then used to determine which section of the text the mouse pointer in hovering. [Point] is calculated using the MousePosition property, translated to client coordinates using the Control.PointToClient() method.
When the mouse pointer is moved from one section of text to the other (here, just two sections, more could be defined adding more rectangles), the control is Invalidated, causing a call to the OnPaint method of the control.
If the mouse pointer is not hovering a section of text, base.OnPaint(e) is called (also causing the Paint event to raise), which draws the default text with the default color.
In the OnPaint method, the Graphics region is clipped using the Rectangles that define the text sections. A subsequent call to TextRenderer.DrawText, setting the TextFormatFlags.PreserveGraphicsClipping flag, clips the text in the defined region, so just the section of text that fits in the clipping region is painted.
The Graphics.ExcludeClip() method is used here to define these clipping regions.
The TextFormatFlags.ExternalLeading and TextFormatFlags.TextBoxControl are also used to replicate the default text rendering, so the custom text is rendered in the same relative position.
This is how it behaves:
Custom Control Class to test the functionality:
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
<DesignerCategory("Code")>
Public Class LabelSplitText
Inherits Label
Private m_Text As String = String.Empty
Private m_Sections As RectangleF() = Nothing
Private m_PaintText As Boolean = False
ReadOnly flags As TextFormatFlags = TextFormatFlags.ExternalLeading Or
TextFormatFlags.PreserveGraphicsClipping Or
TextFormatFlags.TextBoxControl
Public Sub New()
InitializeComponent()
End Sub
Private Sub InitializeComponent()
ResizeRedraw = True
End Sub
Public ReadOnly Property ActiveRectangle As RectangleF
Public ReadOnly Property ActiveSide As String = String.Empty
Public Property ActiveColor As Color = Color.White
Public Property InactiveColor As Color = Color.DimGray
Protected Overrides Sub OnLayout(e As LayoutEventArgs)
MyBase.OnLayout(e)
Me.AutoSize = False
m_Text = Me.Text
End Sub
Protected Overrides Sub OnMouseEnter(e As EventArgs)
m_Text = Me.Text
Text = String.Empty
m_PaintText = True
MyBase.OnMouseEnter(e)
Invalidate()
End Sub
Protected Overrides Sub OnMouseLeave(e As EventArgs)
m_PaintText = False
Me.Text = m_Text
MyBase.OnMouseLeave(e)
End Sub
Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
MyBase.OnMouseMove(e)
Invalidate()
If m_Sections Is Nothing Then Return
Me._ActiveRectangle = If(m_Sections(0).Contains(e.Location), m_Sections(0), m_Sections(1))
End Sub
Protected Overrides Sub OnMouseClick(e As MouseEventArgs)
Me._ActiveSide = If(m_Sections(0).Contains(e.Location), "left", "right")
MyBase.OnMouseClick(e)
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
If Not m_PaintText Then
MyBase.OnPaint(e)
Return
End If
Dim textSize As SizeF = TextRenderer.MeasureText(e.Graphics, m_Text, Me.Font, Me.ClientSize, flags)
m_Sections = GetTextAreaSections(textSize)
e.Graphics.ExcludeClip(Rectangle.Round(m_Sections(1)))
TextRenderer.DrawText(e.Graphics, m_Text, Me.Font, Point.Empty, GetSectionColor(0), flags)
e.Graphics.ResetClip()
e.Graphics.ExcludeClip(Rectangle.Round(m_Sections(0)))
TextRenderer.DrawText(e.Graphics, m_Text, Me.Font, Point.Empty, GetSectionColor(1), flags)
End Sub
Private Function GetSectionColor(section As Integer) As Color
Return If(m_Sections(section).Contains(PointToClient(MousePosition)),
Me.ActiveColor, Me.InactiveColor)
End Function
Private Function GetTextAreaSections(textSize As SizeF) As RectangleF()
If textSize.Width > Me.ClientSize.Width Then textSize.Width = Me.ClientSize.Width
Dim rectLeft = New RectangleF(PointF.Empty,
New SizeF(textSize.Width / 2.0F, Me.ClientSize.Height))
Dim rectRight = New RectangleF(New PointF(textSize.Width / 2.0F, 0),
New SizeF(textSize.Width / 2.0F, Me.ClientSize.Height))
Return {rectLeft, rectRight}
End Function
End Class
I want clean the PictureBox image, so I make this code: TransparentPictureBox.Image = Nothing, the image don't disappear and I obtain all new image overlapping to the previous. How can I fix this?
I stay using a custom control, this is the class:
Public Class TransparentPictureBox
Inherits PictureBox
Public Sub New()
Me.SetStyle(ControlStyles.Opaque, True)
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, False)
End Sub
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or &H20 ' Turn on WS_EX_TRANSPARENT
Return cp
End Get
End Property
End Class
The control is working exactly as you designed it.
With the ControlStyles.Opaque option set to True, it does not draw its background:
If true, the control is drawn opaque and the background is not painted.
When you set the Image property to Nothing, you're expecting it to be emptied.
How does a PictureBox draw itself though? It first draws the background color, then it draws the image on top of that. Since you've disabled background painting, though, the background doesn't get drawn and the previous image doesn't get erased.
Why do you have that option set? Remove it if that is not the behavior you're looking for.
Just write this although this not optimal solution but it will work
picturebox1.imagelocation = ""
I have a control derived from checkbook which I called "SettingBooleanButton", but when any window or dialog is dragged over the control the control keeps signs of the drag
The next image shows the effect of dragging an application window over control
This is the code block that I have for OnPaint()
Public Class SettingBooleanButton
Inherits CheckBox
Private _settingSection As String
Private _settingName As String
Private _associatedSetting As Setting
Public Event StateChange(ByVal affectedSetting As Setting)
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Appearance = Appearance.Button
FlatStyle = FlatStyle.Flat
TextAlign = ContentAlignment.MiddleCenter
AutoSize = False
End Sub
Public Property SettingSection As String
Get
Return _settingSection
End Get
Set(value As String)
_settingSection = value
End Set
End Property
Public Property SettingName As String
Get
Return _settingName
End Get
Set(value As String)
_settingName = value
End Set
End Property
''' <summary>
''' Sets a boolean value to indicate the initial checked state of the control.
''' </summary>
''' <value>
''' <c>true</c> to set it as [checked state]; otherwise, <c>false</c>.
''' </value>
Public Property CheckedState As Boolean
Get
Return Checked
End Get
Set(value As Boolean)
_associatedSetting = New Setting(_settingSection, _settingName, String.Empty)
RemoveHandler CheckedChanged, AddressOf StateChanged
Checked = value
SetText()
AddHandler CheckedChanged, AddressOf StateChanged
End Set
End Property
Private Sub StateChanged(sender As Object, e As EventArgs)
If IsNothing(_associatedSetting) Then
Return
End If
_associatedSetting.Value = Checked.ToString()
SetText()
RaiseEvent StateChange(_associatedSetting)
End Sub
Public Sub SetText()
If Checked Then
Font = New Font(Font.FontFamily, Font.Size, FontStyle.Bold)
ForeColor = Color.WhiteSmoke
Text = Resource.SettingBooleanButton_TrueState
Else
Font = New Font(Font.FontFamily, Font.Size, FontStyle.Regular)
ForeColor = SystemColors.ControlText
Text = Resource.SettingBooleanButton_FalseState
End If
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
MyBase.OnPaint(e)
If Checked Then
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, Color.Black, ButtonBorderStyle.Solid)
End If
End Sub
End Class
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, ...)
Using e.ClipRectangle like this is a traditional bug in a Paint event handler. It is not a rectangle that matches the border you want to draw. It is only the part of the control that needs to be painted. Which is usually the entire control, but not always. Such as in your case when you drag a window across your control, only the part that is revealed needs to be repainted. So now you are painting the border in the wrong position, producing those black lines.
You only ever use the ClipRectangle if your painting code is expensive and you want to take the opportunity to skip that expensive code when it isn't needed anyway. Which is pretty rare, clipping in Windows is already pretty efficient.
You'll need to pass the actual rectangle of your border. Fix:
ControlPaint.DrawBorder(e.Graphics, Me.ClientRectangle, _
Color.Black, ButtonBorderStyle.Solid)
Sometimes the simplest solutions (or causes) are overlooked.
I have a panel with 15 buttons on it and each has an image. Depending on rows selected from a data grid they all might be enabled or disabled.
It all worked fine except toggling between enabled and disabled was taking 2+ seconds and caused lag when multi-selecting from the data grid.
Tried a few things, then I thought maybe it was something to do with the images.
The images were all in an imagelist and size was set to 24,24 which was a compromise between 32,32 and 16,16. I changed the size in the imagelist to 32,32 as that is the native size of all the images... and shazam!!! All the buttons are basically rendered instantly now. No idea ATM whether being small PNG images makes a difference... but I'm going to convert all the images I have to ICO format.
Also... as all my buttons are on a panel I enable/disable the panel which in turn enables and disables all the children on it.
I am trying to set the background color of textbox to transparent, to blend with my backcolor of my form.
I have tried the following below.
TextBox1.BackColor = Color.Transparent 'This doesn't work it stays white'
Is there something I am missing?
When I set TextBox.BackColor to Color.Transparent, it throws System.ArgumentException. I got this message :
Unvalid property value, The control does not support transparent colors background.
Hope am not late to the party, but this actually works for me. First create a class for the panel as below
Partial Public Class iPanel
Inherits Panel
Public Sub New()
SetStyle(ControlStyles.SupportsTransparentBackColor Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint, True)
BackColor = Color.Transparent
End Sub
End Class
Then create a RichTextBox (Instead of a Textbox) as below
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
Now compile the code and add the iRichTextBox inside the panel. Works for me
Private Sub TextBox1_Paint(sender As Object, e As PaintEventArgs) Handles TextBox1.Paint
TextBox1.ForeColor = Color.White
TextBox1.BackColor = Color.Transparent
End Sub
Instead of doing the first one, You can try this. Just put your code in Form Load
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.BackColor = color.(color of your Choice, same color of your background)
TextBox1.ForeColor = color.White
End Sub
As simple as that, it works for me
As far as I know textbox does not support transparent color property. But if you set the back color of the textbox to the same color as of its background component, still it can be considered as transparent.
How to do that - You can get the color name of the background component(in your case it is the form) and pass that name to the component which you want to be transparent.
Dim lname As String = Me.BackColor.ToString
Dim name As String = lname.Substring(7, lname.Length - 8)
txtbox1.BackColor = System.Drawing.Color.FromName(name)
Explanation -
The first line in the code gets you the name of the color, but there's a rub, it gets the name something like this - Color [Dark Orange] and we need only the name of the color i.e Dark Orange.
Thus the second line is to get the exact color name by removing this - Color [] part
And the last line to set that color same as of the background component color.
Hope it works, still have problem let me know...
I am struggling to get my form to have a transparent background in vb.net
Currently in the form New I set
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, true)
But still the form shows up as having the default grey background
Can anyone help??
EDIT: I need the controls on the form to be visible so I don't think setting the opacity to 0 will work
EDIT: I tried the transparency key solution but it doesn't work. I have a circular image with a black background. OnPaint I set the transparency key to the img pixel at 0,0, this then leaves me with circular image (which I want ) It hides the black background but I am still left with the default grey rectangle of the form.
below is the code I have -
Public Sub New()
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColor = Color.Transparent
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.Timer1.Start()
End Sub
Private Sub frmWoll_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim img As Bitmap = CType(Me.BackgroundImage, Bitmap)
img.MakeTransparent(img.GetPixel(2, 2))
Me.TransparencyKey = img.GetPixel(2, 2)
End Sub
Use TransparencyKey for transparent form.
eg.
TransparencyKey = Color.Red
Button1.BackColor = Color.Red
Now run the form you will find that the button1 has a hole in it.
So using this method you can create a mask image in paint for which part has to be transparent and apply that image to form and voila the form is now transparent.
Edit:
Sorry for late reply.
Following is your code modified to suit your requirement
Public Sub New()
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColor = Color.Transparent
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim img As Bitmap = CType(Me.BackgroundImage, Bitmap)
'img.MakeTransparent(img.GetPixel(2, 2))
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
Me.TransparencyKey = img.GetPixel(2, 2)
End Sub
Set Form's TransparencyKey color property same as form's Background color property
There are a few methods you could use.
Use the forms TransparencyKey
Override OnPaintBackground (WM_ERASEBKGND)
Override WndProc and handle the paint messages (WM_NCPAINT, WM_PAINT, etc)
I recommend overriding the window procedure to get optimal results.
Me.Opacity = 0
Be warned that:
This is for the entire form, rather than just the background. There are work-arounds to make certain parts more opague.
It's only psuedo-transparency where it takes a snapshot of what's behind it. It's smart enough to know when you move the form, but not when you move other transparent objects on top of the form.