bitmap/graphic operations
I am trying to take a snapshot, or a copy of a control, for a bitmap. I'm wanting to do this to a listbox, but part of the listbox may be hidden.
On the mouse_down event I need to get the snapshot of the whole listbox (even though parts may be hidden behind other controls, I need the whole listbox) and draw it to the screen in the same location as the original listbox (but the hidden parts still stay hidden behind the other controls on the bitmap).
On the mouse_move event I need to move the snapshot up/down based on mouse movement. This also moves the listbox up/down and this code is done. I will just move the snapshot of the listbox the same way as the original listbox.
On the mouse_up event, I will dispose the bitmap and display the original listbox again.
I am doing this because moving a listbox up/down is really messy. It's a custom drawn listbox and looks horrible moving, so I figured if I could snapshot it, move the bitmap while visible, and listbox behind the scenes, it will flow smoothly and no one would notice I am scrolling a bitmap instead.
Double buffering the form and listbox didn't help :(. I've done some graphics.drawstring before, but not much on other graphics.
Re: bitmap/graphic operations
Every control has a DrawToBitmap method, which copies the control to a bitmap even when it is partly or fully hidden by another control. Here's an example in the form of a function:
Code:
Private Function SnapShot(ByVal ctrl As Control) As Bitmap
Dim bmp As Bitmap
With ctrl
bmp = New Bitmap(.Width, .Height)
.DrawToBitmap(bmp, New Rectangle(0, 0, .Width, .Height))
End With
Return bmp
End Function
It works for most if not all controls apart from RichTextBoxes.
BB
Re: bitmap/graphic operations
Quote:
Originally Posted by
boops boops
Every control has a DrawToBitmap method, which copies the control to a bitmap
Seems if you use .Bounds then it doesn't capture the right and bottom edges, and if the listbox (in this case) has a scrollbar its not captured at all.
Quick attempt...
Code:
Public Class Form1
Private Declare Function SendMessageW Lib "User32.dll" (ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
Private Declare Sub ReleaseCapture Lib "User32.dll" ()
Private Const WM_NCLBUTTONDOWN As Integer = &HA1
Private Const HT_CAPTION As Integer = 2
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
PictureBox1.Visible = False ' pic box to draw listbox onto.
PictureBox1.BorderStyle = BorderStyle.None ' no border
' add some test items
For i As Integer = 1 To 50
ListBox1.Items.Add("Testing " & i.ToString)
Next
End Sub
Private Sub ListBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
' // capture listbox to picbox, move with mouse //
If e.Button = Windows.Forms.MouseButtons.Left Then ' ignore if not left mouse button
With PictureBox1
.Size = ListBox1.Size
.Image = SnapShot(ListBox1)
.Location = ListBox1.Location
.BringToFront()
.Focus()
.Visible = True
ListBox1.Visible = False
ReleaseCapture() 'Release mouse capture/restore normal mouse input processing
SendMessageW(.Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0) ' move pic box with mouse
' [ Detect "MouseUp" here! ]
ListBox1.Location = .Location
ListBox1.Visible = True
ListBox1.Focus()
.Visible = False
End With
End If
End Sub
Private Function SnapShot(ByVal ctrl As Control) As Bitmap
Dim bmp As Bitmap
With ctrl
bmp = New Bitmap(.Width, .Height)
.DrawToBitmap(bmp, New Rectangle(0, 0, .Width, .Height))
End With
Return bmp
End Function
End Class
Re: bitmap/graphic operations
Thanks for the correction Edgemeal. BB:o
Re: bitmap/graphic operations
Quote:
Originally Posted by
boops boops
Thanks for the correction Edgemeal. I guess .Bounds should be replaced by .ClientRectangle. BB
I tried using ClientRectangle first and seems to give same result as Bounds so ended up using Width & Height.
Re: bitmap/graphic operations
It's working well so far... I modified it a little to fit inside of my listbox class. With a boolean to decide if scrolling is allowed, the code will either run or not. The only part I'm having trouble with is activating a mouse_up event.
I put code in the mouse_up event and it didn't fire. I commented out the releasecapture() line and the mouse_up event fired, but it slowed the code way down.
You have 'detect mouse up here', but the code after that runs anyways. I'm assuming to capture the WM_NCLBUTTONUP message, but I'm not sure how, as it looks like sendmessage with UP would activate the UP event everytime. Only on the actual mouse up event do I want to run this code. It will snap the listbox back into place based on x/y coordinates.
Code:
If Me.Location.Y > 0 Then
Me.Location = New Point(Me.Location.X, 0)
ElseIf Me.Height > Me.Parent.Height Then
Dim h As Double
h = Me.Height - Me.Parent.Height
If Me.Location.Y < -h Then
Me.Location = New Point(Me.Location.X, -h)
End If
End If
If Me.Location.X > 0 Then
Me.Location = New Point(0, Me.Location.Y)
ElseIf Me.Location.X < Me.Parent.Width Then
Dim h As Double
h = Me.Width - Me.Parent.Width
If Me.Location.X < -h Then
Me.Location = New Point(-h, Me.Location.Y)
End If
End If
Re: bitmap/graphic operations
well I modified the code some and got it to work. For some reason dim'ng a value was causing errors, so I added it like this:
Code:
...
' [ Detect "MouseUp" here! ]
Me.Location = .Location
If Me.Location.Y > 0 Then
Me.Location = New Point(Me.Location.X, 0)
ElseIf Me.Location.Y < -(Me.Height - Me.Parent.Height) Then
Me.Location = New Point(Me.Location.X, -(Me.Height - Me.Parent.Height))
End If
If Me.Location.X > 0 Then
Me.Location = New Point(0, Me.Location.Y)
ElseIf Me.Location.X < -(Me.Width - Me.Parent.Width) Then
Me.Location = New Point(-(Me.Width - Me.Parent.Width), Me.Location.Y)
End If
Me.Visible = True
...
I am now trying to figure out if there is a way to restrict the mouse movements caught by the SendMessageW() function. In reality, all horizontal or x-axis movement should be ignored, so only vertical or y-axis movement is shown.
Re: bitmap/graphic operations
This seems to work better, using setcapture, otherwise mouse stops moving pic box unless mouse stays inside pic box.
Code:
Imports System.Runtime.InteropServices
Public Class Form1
Private mDragging As Boolean
Private iY As Integer
<DllImport("user32.dll")> _
Private Shared Function SetCapture(ByVal hWnd As IntPtr) As IntPtr
End Function
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
PictureBox1.Visible = False ' pic box to draw listbox onto.
PictureBox1.BorderStyle = BorderStyle.None ' no border
' add some test items
For i As Integer = 1 To 50
ListBox1.Items.Add("Testing " & i.ToString)
Next
End Sub
Private Sub ListBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
' // capture listbox to picbox //
If e.Button = Windows.Forms.MouseButtons.Left Then ' ignore if not left mouse button
With PictureBox1
.Size = ListBox1.Size
.Image = SnapShot(ListBox1)
.Location = ListBox1.Location
iY = e.Y ' save mouse Y pos.
ListBox1.Visible = False ' hide list
.BringToFront() ' show pic box
.Focus()
.Visible = True
' allow drag/move of p[ic box
mDragging = True
' set capture on pic box so mouse moves work even if mouse not over pic box
SetCapture(.Handle)
End With
End If
End Sub
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
If mDragging Then ' move picbox with mouse
PictureBox1.Top += e.Y - iY
End If
End Sub
Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
' [ Detect "MouseUp" here! ]
mDragging = False
ListBox1.Location = PictureBox1.Location
ListBox1.Visible = True
ListBox1.Focus()
PictureBox1.Visible = False
End Sub
Private Function SnapShot(ByVal ctrl As Control) As Bitmap
Dim bmp As Bitmap
With ctrl
bmp = New Bitmap(.Width, .Height)
.DrawToBitmap(bmp, New Rectangle(0, 0, .Width, .Height))
End With
Return bmp
End Function
End Class
Re: bitmap/graphic operations
Working ok so far as well, with a couple things that I'm not sure if they can be fixed but here goes.
When clicking the listbox, it flickers once, because of the picturebox/listbox flashing visibilities. Setting picturebox visible=true before the listbox visible=false, and vice versa on mouse_up, didn't help. I'm thinking it's because of painting the listbox when changing the visible properties. The first code supplied that used sendmessagew() didn't flicker so I'm not positive what it could be?
When clicking the listbox, it will only actually select an item every second click?
When moving the picturebox down/up, if the top or bottom of the picturebox is moved within view and keeps moving, the picturebox seems to update slower and becomes jaggy?
I will try to see what I can come up with, in the meantime here is the code that draws the listbox's if it helps at all:
Code:
Public Sub New()
SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint Or ControlStyles.SupportsTransparentBackColor, True)
Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
Me.ItemHeight = 25
Me.BorderStyle = Windows.Forms.BorderStyle.None
End Sub
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.Style = cp.Style And Not &H200000 ' Turn off WS_VSCROLL style
Return cp
End Get
End Property
Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
e.DrawBackground()
If e.Index > -1 Then
e.Graphics.DrawString(Me.Items(e.Index), New Font(e.Font.FontFamily, 15), New SolidBrush(Color.Black), e.Bounds)
End If
MyBase.OnDrawItem(e)
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim iregion As Region = New Region(e.ClipRectangle)
e.Graphics.FillRegion(New SolidBrush(Color.FromArgb(alpha, Color.FromArgb(128, 240, 240, 240))), iregion)
If Me.Items.Count > 0 Then
For i = 0 To Me.Items.Count - 1
Dim irect As System.Drawing.Rectangle = Me.GetItemRectangle(i)
If e.ClipRectangle.IntersectsWith(irect) Then
If Me.SelectionMode = Windows.Forms.SelectionMode.One And Me.SelectedIndex = i Then
OnDrawItem(New DrawItemEventArgs(e.Graphics, Me.Font, irect, i, DrawItemState.Selected, Me.ForeColor, Color.FromArgb(alpha, Color.FromArgb(128, 240, 240, 240))))
Else
OnDrawItem(New DrawItemEventArgs(e.Graphics, Me.Font, irect, i, DrawItemState.Default, Me.ForeColor, Color.FromArgb(alpha, Color.FromArgb(128, 240, 240, 240))))
End If
iregion.Complement(irect)
End If
Next
MyBase.OnPaint(e)
End If
End Sub
Sub measureditem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs)
e.ItemHeight = 25
End Sub
Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
originalselectedindex = Me.SelectedIndex
Form1.Label1.Focus()
Me.Focus()
If originalselectedindex > -1 Then
Me.SelectedItem = Me.Items(originalselectedindex)
End If
MyBase.OnSelectedIndexChanged(e)
Me.Invalidate()
End Sub
Re: bitmap/graphic operations
I figured out that the 2nd form painting was causing the problems at hand... At least the listbox is scrolling as it should be and selecting items works just fine now.
Re: bitmap/graphic operations
Ok, so I majorly edited my last post and am now forgetting about all transparency issues. I will be adding the controls to a panel and placing the panel on the application's main form instead of calling everything on a second form. This will clear up ALL transparency issues I have been experiencing the past couple of days....
I did however notice one other thing that might be able to be cleared up. When taking the snapshot, or drawing the bitmap, of the listbox, the picturebox is identical, except that the parts of the listbox that was hidden at the time, gain the backcolor of the main form... To me, it doesn't seem like a very convincing/explanation of the problem, so this might help. This is after scrolling the listbox up to where the hidden parts are now visible...
Before scrolling:
http://img703.imageshack.us/img703/7...escrolling.jpg
While scrolling:
http://img84.imageshack.us/img84/169...escrolling.jpg
In fact, I noticed that the grey parts of the bitmapped picture of the listbox only appear if the listbox is more vertical than the form it's on is... Wonder if that's fixable at all?