Visual Basic Subclassing Routines
WM_LBUTTONDOWN: Using a Combo in a MSFlexGrid to Select Data
     
Posted:   Monday February 8, 1999
Updated:   Monday December 26, 2011
     
Applies to:   VB5, VB6
Developed with:   VB6, Windows 98
OS restrictions:   None
Author:   VBnet - Randy Birch
     
 Prerequisites
VB5 or VB6.

When working with a MSFlexGrid control, the developer may want to display a combo box to provide a list of cell data available for selection.  While the actual code to reposition a combo in relation to a cell is trivial, should the combo be in its visible state when the user resizes the column, the combo would remain at its initial position and size until the cell is clicked again.

By utilizing subclassing techniques, we can trap the Windows' WM_MOVE and WM_PAINT messages, and in response to these, assure that the displayed combo is realigned to the cell's new width or position.

This example was inspired from source provided via the Microsoft Knowledge Base.

 BAS Module Code
Place the following code into the general declarations area of a bas module:

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.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const GWL_WNDPROC As Long = (-4)
Public Const WM_SIZE As Long = &H5
Public Const WM_PAINT As Long = &HF
Public Const CB_DELETESTRING As Long = &H144
Public Const CB_RESETCONTENT As Long = &H14B
Public Const WM_LBUTTONDOWN As Long = &H201
Public Const WM_LBUTTONUP As Long = &H202

Public defWinProc As Long
Public hWndFlex As Long

Declare Function CallWindowProc Lib "user32" _
   Alias "CallWindowProcA" _
  (ByVal lpPrevWndFunc As Long, _
   ByVal hwnd As Long, ByVal Msg As Long, _
   ByVal wParam As Long, ByVal lParam As Long) As Long

Declare Function SetWindowLong Lib "user32" _
   Alias "SetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long, _
   ByVal dwNewLong As Long) As Long

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


Public Sub Hook()

    If defWinProc = 0 Then
       
      defWinProc = SetWindowLong(hWndFlex, _
                                 GWL_WNDPROC, _
                                 AddressOf WindowProc)
      
    End If
    
End Sub

Public Sub Unhook()
    
   If defWinProc <> 0 Then

      Call SetWindowLong(hWndFlex, _
                         GWL_WNDPROC, _
                         defWinProc)
      defWinProc = 0

   End If
    
End Sub

Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, _
                           ByVal wParam As Long, ByVal lParam As Long) As Long
   
   WindowProc = CallWindowProc(defWinProc, hwnd, uMsg, wParam, lParam)

   Select Case uMsg
   
      Case WM_SIZE, WM_PAINT
      
         With Form1
            .Combo1.Width = .MSFlexGrid1.CellWidth
            .Combo1.Left = .MSFlexGrid1.CellLeft + .MSFlexGrid1.Left
            .Combo1.Top = .MSFlexGrid1.CellTop + .MSFlexGrid1.Top
         End With
         
      Case WM_LBUTTONDOWN

         With Form1
            .Combo1.Width = .MSFlexGrid1.CellWidth
            .Combo1.Left = .MSFlexGrid1.CellLeft + .MSFlexGrid1.Left
            .Combo1.Top = .MSFlexGrid1.CellTop + .MSFlexGrid1.Top
         End With
      
         With Form1.MSFlexGrid1
      
            If .Col > 0 Then            
               Form1.Combo1.Visible = False
               Form1.Combo1.Text = ""
               Form1.FillComboData .Col
               Form1.Combo1.Text = .Text
               Form1.Combo1.Visible = True
            End If
   
         End With
         
      Case Else

   End Select

End Function
 Form Code
To a form, add a command button (Command1), combo (Combo1) and MSFlexGrid control (MSFlexGrid1). Add the following code:

Option Explicit

Public Sub FillComboData(currCol As Integer)

   Static lastColumn As Integer

  'Based on the column passed, fill the combo 
  'with the data for that column
   
   If currCol <> lastColumn Then
      
     'erase current combo 
      Call SendMessage(Combo1.hwnd, CB_RESETCONTENT, 0&, ByVal 0&)
   
      Select Case currCol
         Case 1:
           'Load the ComboBox's list
            With Combo1
               .AddItem "Some text"
               .AddItem "Some more text"
               .AddItem "Still more text"
               .AddItem "Yet even more text"
               .AddItem "Way more text than that"
            End With
            
         Case 2:
           'Load the ComboBox's list
            With Combo1
               .AddItem "125.00"
               .AddItem "133.00"
               .AddItem "229.00"
               .AddItem "345.00"
               .AddItem "385.00"
            End With
            
         Case 3:
           'Load the ComboBox's list
            With Combo1
               .AddItem "box"
               .AddItem "carton"
               .AddItem "case"
               .AddItem "each"
               .AddItem "package"
            End With
                     
         Case Else
      End Select
   
   End If
   
   lastColumn = currCol
   
End Sub


Private Sub Command1_Click()

   Unload Me
   
End Sub


Private Sub Form_Unload(Cancel As Integer)
    
    If defWinProc <> 0 Then
       Unhook   'Stop checking messages
    End If
    
End Sub


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

   Static CurrentWidth As Single
   
  'Check to see if the Cell's width has changed
  
   With MSFlexGrid1
      If .CellWidth <> CurrentWidth Then
         
         Combo1.Width = .CellWidth
         CurrentWidth = .CellWidth
      
      End If
   End With
End Sub


Private Sub Form_Load()

   With MSFlexGrid1
   
      hWndFlex = .hwnd
      Hook          'Start checking messages
      .AllowUserResizing = flexResizeColumns
      .Cols = 4
      .Rows = 6
      .RowHeightMin = Combo1.Height
      
   End With
   
   With Combo1
      .Visible = False
      .ZOrder 0
      .Width = MSFlexGrid1.CellWidth
   End With
   
End Sub


Private Sub Combo1_Click()

   'Place the selected item into the
   'Cell and hide the ComboBox
    MSFlexGrid1.Text = Combo1.Text
    Combo1.Visible = False
    
End Sub
 Comments
Save the project before running, and use Start with Full Compile to catch any errors that might crash the subclassing. Clicking anywhere within the working area will display the combo box for that column. While the combo is displayed, resize the column. When you release the resize bar, the combo will resize to take up the entire cell.

To compare to the non-subclassed method, comment out the Hook statement in the form load and run again.


 
 

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