Results 1 to 11 of 11

Thread: bitmap/graphic operations

Hybrid View

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Nov 2008
    Location
    PA
    Posts
    365

    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.

  2. #2
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    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

  3. #3
    VB For Fun Edgemeal's Avatar
    Join Date
    Sep 2006
    Location
    WindowFromPoint
    Posts
    4,255

    Re: bitmap/graphic operations

    Quote Originally Posted by boops boops View Post
    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

  4. #4

  5. #5
    VB For Fun Edgemeal's Avatar
    Join Date
    Sep 2006
    Location
    WindowFromPoint
    Posts
    4,255

    Re: bitmap/graphic operations

    Quote Originally Posted by boops boops View Post
    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.

  6. #6

    Thread Starter
    Hyperactive Member
    Join Date
    Nov 2008
    Location
    PA
    Posts
    365

    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
    Last edited by detlion1643; Dec 14th, 2010 at 09:23 AM.

  7. #7

    Thread Starter
    Hyperactive Member
    Join Date
    Nov 2008
    Location
    PA
    Posts
    365

    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.

  8. #8
    VB For Fun Edgemeal's Avatar
    Join Date
    Sep 2006
    Location
    WindowFromPoint
    Posts
    4,255

    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

  9. #9

    Thread Starter
    Hyperactive Member
    Join Date
    Nov 2008
    Location
    PA
    Posts
    365

    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

  10. #10

    Thread Starter
    Hyperactive Member
    Join Date
    Nov 2008
    Location
    PA
    Posts
    365

    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.
    Last edited by detlion1643; Dec 14th, 2010 at 06:45 PM.

  11. #11

    Thread Starter
    Hyperactive Member
    Join Date
    Nov 2008
    Location
    PA
    Posts
    365

    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:


    While scrolling:


    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?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width