Visual Basic Window/Form Routines
SendMessage: Creating a Scrollable Viewport to Simulate a Scrollable Form
     
Posted:   Sunday July 18, 2004
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   None
Author:   VBnet - Randy, MSKB
     

Related:  

SendMessage: Move Controls to Simulate Form Scrolling
ReleaseCapture: Simulating a Working Size-Grip on a VB Form

     
 Prerequisites
None.

SendMessage: Move Controls to Simulate Form Scrolling discussed two techniques for providing an application with functionality that VB intrinsically lacks - the ability to scrolling the controls on a form (as that demo showed), and this method - creating a scrollable viewport by embedding one picture box inside another. The concept for this is simple: control placement is restricted by the size of the form, but when a picture box is created inside another picture box, the innermost picture box can be resized to the maximum allowed by Windows. When this occurs, only a portion of the entire inner picture box is visible at any time within the base or viewport control, thereby allowing us to reposition the top and left properties of this inner control to simulate a scrollable viewport.

The small illustration above shows the controls required for this demo, coloured to provide clarity for the layout:

  • The Horizontal and Vertical scroll bars - both reside on the form;
  • The fake grab handle picture box (green background) - this also resides on the form;
  • The viewport picture box (red) residing on the form,
  • The scrolling picture box (cyan) inside the viewport picture box.

All controls that should "scroll" are placed inside the cyan picture box (named Picture2 in the demo) -- in other words, all controls to scroll must be created as child controls inside the cyan picture box. (If you can move a control you want to scroll outside the borders of the inner cyan control, then that control is not properly contained within the cyan picture box.)

To keep the code simple this demo uses the inner (cyan) picture box to simply display an image that is larger than the form, resized via the cyan picture box AutoSize property. Naturally, in an application that will host other controls you will have to code the resizing of the scrollable (cyan) picture box.

The viewport picture box (red) is sized to the form's client area minus the space needed to host the two scroll bars and the fake grab handle. When the user changes the scroll bars the cyan (inner) picture box hosting the graphic is repositioned through manipulation of the top and left properties to reveal different portions of the image through the red 'viewport' (Picture1 in this demo). Therefore, unlike the SendMessage: Move Controls to Simulate Form Scrolling demo this method has only one 'moving' control - the red viewport picture box.

This demo is pretty closely based on the two published MS KB scrolling viewport articles, and as such has minimal error checking or optimization. Improvements can be made, and may be applicable depending on the specific use you have for this code.

Note: the gripper handle in the bottom right corner is constructed using Windows' Marlett font. XP in themed mode does not use Marlett for the gripper image - on XP the gripper is a theme image rather than a font. It is possible to get the XP-style gripper (on XP machines), but the code required for this is outside the scope of this demo.

 
Revision history

July 18.2004      First posted
     

 BAS Module Code
None.

 Form Code

Add a picture box (Picture1) to act as the viewport (red). Inside Picture1 create a second picture box (Picture2 - cyan) to act as the container for the image or other controls as required. You can change the BackColor of these picture boxes to more easily see the effect when the form is sized larger than the viewport.

Add a horizontal and vertical scrollbar (HScroll1, VScroll1) to the form, plus one final picture box (Picture3) that will become the resizing gripper handle. The position and size of these three controls on the form is of no importance as they are positioned by code.  Add the following code:


Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'flag to enables/disable the ability to
'resize the window using the fake grabber
Private bWindowIsResizable As Boolean

Private Const WM_NCLBUTTONDOWN = &HA1
Private Const HTBOTTOMRIGHT = 17


Private Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long

Private Declare Function ReleaseCapture Lib "user32" () As Long



Private Sub Form_Load()
   
   HScroll1.Height = 255
   VScroll1.Width = 255
   
  'set flag indicating a resizable window
   bWindowIsResizable = (Me.BorderStyle = vbSizable) Or _
                        (Me.BorderStyle = vbSizableToolWindow)
                        
  'set up the picture box used to fill
  'the corner between the H and V scroll
  'bars, and if the window is sizable
  'print a 'gripper' image to the control
   With Picture3
   
      .AutoRedraw = True 
      .AutoSize = True
      .ForeColor = &H80000015
      .BackColor = Me.BackColor
      .BorderStyle = 0
      .ZOrder 0
       
     'if sizable windows print the gripper image
      If bWindowIsResizable Then
         .Font.Size = 11
         .Font.Name = "Marlett"
         .Font.Bold = False
          Picture3.CurrentX = 10 
          Picture3.CurrentY = 10 
          Picture3.Print "o"
       End If
   End With
   
   With Picture1
      .BorderStyle = 0
      .Move 0, 0
      .Cls
   End With
   
   With Picture2  'inner (cyan) picture box
     'as we're loading an image, expand pix2
     'to the size of the loaded graphic
      .AutoSize = True
      .BorderStyle = 0
      .Move 0, 0
      .Cls
      
     'obviously, change this to a valid image on your system 
      .Picture = LoadPicture("c:\windows\xp5layout.jpg")
   End With

   With HScroll1
      .Max = (Picture2.ScaleWidth - Picture1.ScaleWidth)
      .LargeChange = .Max \ 10
      .SmallChange = .Max \ 25
      .Enabled = (Picture1.ScaleWidth <= Picture2.ScaleWidth)
      .ZOrder 0
   End With

   With VScroll1
      .Max = (Picture2.ScaleHeight - Picture1.ScaleHeight)
      .LargeChange = .Max \ 10
      .SmallChange = .Max \ 25
      .Enabled = (Picture1.ScaleHeight <= Picture2.ScaleHeight)
      .ZOrder 0
   End With
   
   Picture3.ZOrder 0

End Sub


Private Sub Form_Resize()

  'Picture1 is the *outer* pix box (the red viewport)
  'Picture2 is the inner pix box (the cyan container to scroll within the viewport)

  'don't attempt resizing if minimized!
   If Me.WindowState <> vbMinimized Then
   
     'this prevents an error if
     'the form is sized too small
      If (Me.ScaleHeight > HScroll1.Height) And _
         (Me.ScaleWidth > VScroll1.Width) Then

         Picture1.Move 0, 0, Me.ScaleWidth - VScroll1.Width, _
                             Me.ScaleHeight - HScroll1.Height
                              
         With HScroll1
            .Left = 0
            .Top = Picture1.Height
            .Width = Picture1.Width
            .ZOrder 0
            .Enabled = (Picture1.ScaleWidth < Picture2.ScaleWidth)
            
           'if the form has been resized to
           'display the entire pixbox,
           'disable the scrollbars
            If .Enabled Then
               .Max = (Picture2.ScaleWidth - Picture1.ScaleWidth)
            End If
         End With

         With VScroll1
            .Left = Picture1.Width
            .Top = 0
            .Height = Picture1.Height
            .ZOrder 0
            .Enabled = (Picture1.ScaleHeight < Picture2.ScaleHeight)
            
            If .Enabled Then
               .Max = (Picture2.ScaleHeight - Picture1.ScaleHeight)
            End If
            
         End With
                
        'position the fake sizing grip
         Picture3.Move VScroll1.Left, HScroll1.Top
         
      End If  '(Me.ScaleHeight > HScroll1.Height) And ...
      
   End If  'Me.WindowState

End Sub


Private Sub Form_Unload(Cancel As Integer)
   Set Form1 = Nothing
End Sub


Private Sub Picture3_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

  'if a sizable window..
   If bWindowIsResizable Then
      
     '..fake a resize grabber action
      If Button = vbLeftButton Then
          ReleaseCapture
          SendMessage Me.hwnd, WM_NCLBUTTONDOWN, HTBOTTOMRIGHT, ByVal 0&
      End If

    End If

End Sub


Private Sub Picture3_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)


  'if a sizable window..
   If bWindowIsResizable Then

     '..users expect a sizing arrow
      Picture3.MousePointer = vbSizeNWSE
   
   End If

End Sub


Private Sub VScroll1_Change()
   Picture2.Top = -VScroll1.Value
End Sub


Private Sub VScroll1_Scroll()
   Picture2.Top = -VScroll1.Value
End Sub


Private Sub HScroll1_Change()
   Picture2.Left = -HScroll1.Value
End Sub


Private Sub HScroll1_Scroll()
   Picture2.Left = -HScroll1.Value
End Sub
 Comments

 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter