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