Transparency of picture box - vb.net

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 backcoloras 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 backcoloris not really transparent. it will only get the backcolorof form and use it as backcolorof image instead of transparent.
Is there any solution to make it fully transparent?

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

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) < Me.Parent.Controls.Count) Then
Me.Parent.Controls.SetChildIndex(Me, ndx + value)
End If
End Sub
' if you want to remove properties, this is how
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)>
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.

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 class.
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.

Related

Code to draw works in button click event but not in form load

why can I run the following code faultless under a button even but not under the form load even?
For Each line As String In System.IO.File.ReadAllLines("c:\pos.xml")
If line.Contains("<POS>") = True Then
Dim tagless As String = StripTags(line)
Dim parts As String() = tagless.Split(New Char() {","})
Dim XVAL As Decimal = parts(0)
Dim YVAL As Decimal = parts(1)
'paint markers...
Dim myBrush As New SolidBrush(Color.FromArgb(128, Color.Red))
Dim formGraphics As System.Drawing.Graphics
formGraphics = Me.PictureBox1.CreateGraphics()
formGraphics.FillEllipse(myBrush, New Rectangle(XVAL - 35, YVAL - 35, 70, 70))
myBrush.Dispose()
formGraphics.Dispose()
End If
Next
Here is the striptag function if requierd...
Function StripTags(ByVal html As String) As String
' Remove HTML tags.
Return Regex.Replace(html, "<.*?>", "")
End Function
The right way to draw is hardly ever with CreateGraphics. This will draw something that does not persist. When the area is invalidated such as the form is minimized or another form/app is dragged over it, your shapes will disappear.
You should also turn on Option Strict. There are numerous type errors in the code. For instance there is no Rectangle constructor which takes a Decimal. That is not even the right Class for non integers, but RectangleF doesn't take a Decimal either.
The core problem is that the form is shown at the end of the form load event. So, your code is running/drawing before the form is visible and nothing is shown. Even if the form was already showing, whatever you draw would not be retained if the user minimized the form or moved another window across it.
' form level list to store the data
Private XY As New List(Of PointF) ' pts
Then in form load event, read the data and add to the list
For Each line As String In System.IO.File.ReadAllLines("...")
If line.Contains("<POS>") = True Then
Dim tagless As String = StripTags(line)
' c required under Option Strict
Dim parts As String() = tagless.Split(New Char() {","c})
' convert values to single. create a PointF
Dim ptF As New PointF(Convert.ToSingle(parts(0)), Convert.ToSingle(parts(1)))
' add to list
XY.Add(ptF)
End If
Next
The next thing that happens should be the form being shown and the paint event invoked. The data is used in the Paint Event:
Dim rectF As RectangleF
Using myB As New SolidBrush(Color.FromArgb(128, Color.Red))
For Each ptF As PointF In XY
rectF = New RectangleF(ptF.X - 35, ptF.Y - 35,
70, 70)
e.Graphics.FillEllipse(myB, rectF)
Next
End Using
If you have other code to add Point data, like a Button click, after you add, change or remove data, use Invalidate to force a redraw: Me.Invaludate() is you are drawing to the form, or PictureBox1.Invalidate() if you are drawing over a control.
The lesson is that now, every time the form needs to be repainted, your shapes will be redrawn as well.

Can I use an iTextSharp cell event to repeat data on the next page when a row is split?

I have a very deep row in a pdfptable. The columns in the left-hand side of this row contain a sub table which has many rows within it. I would like the deep row to split onto more than one page if it is too deep to fit on one page. I can set SplitLate to false to ensure that the row splits. However, the data for the deep columns on the right hand side only shows on the first page and I would like it to be repeated on the second page.
A bit of investigation suggested that I could use a cell event to put the text into the cells and have it repeated on the second page. So I created a cell event as follows:
Public Class PdfPCellEvent
Implements iTextSharp.text.pdf.IPdfPCellEvent
Private m_text As String
Private m_font As text.pdf.BaseFont
Public Sub New(ByVal text As String, font As text.pdf.BaseFont)
MyBase.New()
m_text = text
m_font = font
End Sub
Public Sub CellLayout(cell As PdfPCell, position As text.Rectangle, canvases() As PdfContentByte) Implements IPdfPCellEvent.CellLayout
Dim cb As PdfContentByte = canvases(PdfPTable.TEXTCANVAS)
cb.BeginText()
cb.SetFontAndSize(m_font, 8)
cb.ShowText(m_text)
cb.EndText()
End Sub
End Class
I then call this as follows:
Dim cell As PdfPCell = New PdfPCell()
Dim bfReport As BaseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, False)
Dim cellEvent As New PdfPCellEvent("Text", bfReport)
cell.CellEvent = cellEvent
mainTable.AddCell(cell)
document.Add(mainTable)
This all runs. I can see that the cell event is being called twice after the mainTable is added to the document. I presume it is being called once for each page the row appears on. I can also see that the correct text is being passed into CellLayout. However no text appears in the resulting pdf. What am I doing wrong? I have tried various ways of adding the text in CellLayout but none have worked.
There is something missing in this code snippet:
Dim cb As PdfContentByte = canvases(PdfPTable.TEXTCANVAS)
cb.BeginText()
cb.SetFontAndSize(m_font, 8)
cb.ShowText(m_text)
cb.EndText()
You are creating a text block, but you forget to specify coordinates. I suspect that the BT/ET sequence is added to the content stream, but there's no way of telling where the text will show up on the page.
In the cell layout, you have access to position As text.Rectangle. You can ask the position variable for coordinates. You can use these coordinates in the code snippet about (using the SetTextMatrix() method), but it will be easier for you if you don't use cb.BeginText(), cb.SetFontAndSize(), cb.SetTextMatrix(), cb.ShowText(), cb.EndText(). Use ColumnText.ShowTextAligned() instead. You can pass the cb instance as a parameter, along with a Phrase, an alignment (left, right, center), an X,Y coordinate and an angle.
Thanks, the following now works perfectly:
Public Class PdfPCellEvent
Implements iTextSharp.text.pdf.IPdfPCellEvent
Private m_phrase As text.Phrase
Public Sub New(ByVal phrase As text.Phrase)
MyBase.New()
m_phrase = phrase
End Sub
Public Sub CellLayout(cell As PdfPCell, position As text.Rectangle, canvases() As PdfContentByte) Implements IPdfPCellEvent.CellLayout
Dim cb As PdfContentByte = canvases(PdfPTable.TEXTCANVAS)
Dim CenterX As Single = position.GetLeft(0) + (position.Width() / 2)
Dim CenterY As Single = position.GetBottom(0) + (position.Height() / 2)
ColumnText.ShowTextAligned(cb, text.Element.ALIGN_CENTER, m_phrase, CenterX, CenterY, 0)
End Sub
End Class
And I call this as follows:
phrase = New text.Phrase("Text", fontSmaller)
Dim cell As PdfPCell = New PdfPCell()
Dim cellEvent As New PdfPCellEvent(phrase)
cell.CellEvent = cellEvent
mainTable.AddCell(cell)
document.Add(mainTable)
The text now appears on both pages and is centred vertically and horizontally in the cell.

Listview wont display any data when using SetStyle(ControlStyles.UserPaint, True)

I am trying to add a custom message to the background of a listview when there is no data to display. I have turned on the UserPaint flag via the call below which has given me exactly what I want.
SetStyle(ControlStyles.UserPaint, True)
However, this then disables all of the Item and SubItem drawing for the entire ListView. I am only assuming that with this set to true, I am now responsible for drawing all items myself (I could be wrong).
If I am correct, does anyone have a tutorial on how to custom draw list items manually, or give me some examples as I am very new to working with messages and would love to get my head around it all.
Below is my code for drawing my custom message via the OnPaint method:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim MessageFont As Font = _EmptyMessageFont
Dim MessageColour As Color = _EmptyMessageColour
Dim MessageText As String = _EmptyMessageString
If Not Me.Items.Count > 0 Then
Dim flags As TextFormatFlags
Dim rec As New Rectangle(Me.Location.X + 2, Me.Location.Y + 2, Me.Width - 4, Me.Height - 4)
flags = TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter Or TextFormatFlags.SingleLine
TextRenderer.DrawText(e.Graphics, MessageText, MessageFont, rec, MessageColour, flags)
End If
MyBase.OnPaint(e)
End Sub
Not sure how to add code in this method to handle the normal drawing of list items.

Dropping shadow on WinForm distorts interface

I'm using the following code to create a Windows7 style drop shadow effect on my WinForms:
<DllImport("dwmapi.dll", PreserveSig:=True)> _
Private Shared Function DwmSetWindowAttribute(hwnd As IntPtr, attr As Integer, ByRef attrValue As Integer, attrSize As Integer) As Integer
End Function
<DllImport("dwmapi.dll")> _
Private Shared Function DwmExtendFrameIntoClientArea(hWnd As IntPtr, ByRef pMarInset As Margins) As Integer
End Function
Private Function CreateDropShadow() As Boolean
Try
Dim val As Integer = 2
Dim ret1 As Integer = DwmSetWindowAttribute(Me.Handle, 2, val, 4)
If ret1 = 0 Then
Dim m As New Margins(0, 0, 0, 0)
Dim ret2 As Integer = DwmExtendFrameIntoClientArea(Me.Handle, m)
Return ret2 = 0
Else
Return False
End If
Catch ex As Exception
' Probably dwmapi.dll not found (incompatible OS)
Return False
End Try
End Function
Protected Overrides Sub OnHandleCreated(e As EventArgs)
CreateDropShadow()
MyBase.OnHandleCreated(e)
End Sub
The result of above code creates a nice drop shadow effect on my borderless winform, but it causes the UI to distort. All the controls and labels on my form aren't appearing properly, with text not readable.
Am I missing something here? I don't want to use the traditional drop shadow effect using CreateParams, its too 'boxy' look and doesn't give a nice shadow effect.
Here are screenshots of without shadow and with shadow:
Thanks.
Dim m As New Margins(0, 0, 0, 0)
There's a subtle mistake visible here, looks a lot like you are using System.Drawing.Printing.Margins. But that's not a type that's compatible with the Windows' MARGINS type. Which is a structure, not a class.
So this just goes complete wrong, Windows reads nonsense instead of (0, 0, 0, 0). And extends the frame into the entire client area. Which then plays havoc on any control that draws with GDI, it is a 24bpp drawing api that leaves the alpha at 0 so anything that should be black becomes transparent instead.
Fix this by declaring a proper MARGINS structure:
Structure MARGINS
Public Left, Right, Top, Bottom As Integer
End Structure

VB.NET Cartesian Coordinate System

I'd like to make a Cartesian Coordinate System in a Windows form and be able to plot (x,y) coordinates in it.
How do i do this? I already did my research but unfortunately i only land on "charts" and not the Cartesian plane.
Any links regarding my problem will help ... thanks ...
In WinForms, you can use a PictureBox control and then draw on it using primitives such as DrawLine, DrawEllipse, etc. The following SO question contains an example:
how to draw drawings in picture box
In WPF, you can use a Canvas control similarly:
WPF canvas drawing with Graphics
If you want automatic axes and labeling, Charts are indeed the way to go. For your use case, a point chart seems like the right solution:
Point Chart (Chart Controls)
You should create a custom UserControl and use the Paint even to draw on the surface of the control. The Paint event provides you with a Graphics object which you can use to draw the graph. The big thing to know, however, is that you will need to swap your Y axis. In windows, the top-left of the screen is 0,0 rather than the bottom-left.
So, for instance, the following code will draw the x and y axis of a graph on a contorl:
Public Class CartesianGraph
Public Property BottomLeftExtent() As Point
Get
Return _bottomLeftExtent
End Get
Set(ByVal value As Point)
_bottomLeftExtent = value
End Set
End Property
Private _bottomLeftExtent As Point = New Point(-100, -100)
Public Property TopRightExtent() As Point
Get
Return _topRightExtent
End Get
Set(ByVal value As Point)
_topRightExtent = value
End Set
End Property
Private _topRightExtent As Point = New Point(100, 100)
Private Sub CartesianGraph_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim extentHeight As Integer = _topRightExtent.Y - _bottomLeftExtent.Y
Dim extentWidth As Integer = _topRightExtent.X - _bottomLeftExtent.X
If (extentHeight <> 0) And (extentWidth <> 0) Then
If (_bottomLeftExtent.Y <= 0) And (_topRightExtent.Y >= 0) Then
Dim xAxis As Integer = e.ClipRectangle.Height - (_bottomLeftExtent.Y * -1 * e.ClipRectangle.Height \ extentHeight)
e.Graphics.DrawLine(New Pen(ForeColor), 0, xAxis, e.ClipRectangle.Width, xAxis)
End If
If (_bottomLeftExtent.X <= 0) And (_topRightExtent.X >= 0) Then
Dim yAxis As Integer = e.ClipRectangle.Width * _bottomLeftExtent.X * -1 \ extentWidth
e.Graphics.DrawLine(New Pen(ForeColor), yAxis, 0, yAxis, e.ClipRectangle.Height)
End If
End If
End Sub
End Class
.NET has a charting library, but there are a few open source projects that do this sort of thing quite well. If you want to plot coordinates Zedgraph makes this relatively easy and is quite flexible.
this ZedGraph Example gives a great introduction
Dynamic Data Display is also worth looking at, but it is WPF, not Windows Forms