|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Visual Basic
Common Control API Routines CreateWindowEx: Create the Common Control Header |
||
Posted: | Sunday January 25, 1998 | |
Updated: | Monday December 26, 2011 | |
Applies to: | VB4-32, VB5, VB6 | |
Developed with: | VB5, Windows 98 | |
OS restrictions: | None | |
Author: | VBnet - Randy Birch | |
Prerequisites |
Enhanced Comctl32 functionality is only available to users
with comctl32.dll version 4.70 or greater installed. This dll is typically installed with IE3.x or greater.
This project also requires the BAS module constructed in the accompanying article InitCommonControlsEx: Common Control Initialization Module. |
|
If
you're like myself, you've probably wished that some of Visual Basic's other controls provided the look (and functionality) of a ListView's
header. In this example, we'll use the API to create a real header control and position it overtop a list or textbox.
The method below does not provide any functionality to that control; it does not perform a sort of the contents in response to mouse clicks. It does however indicate the steps needed to create and position the control, and the missing sorting functionality could then be supplied by you by subclassing the control and responding to the passed HHT_XXX messages.
|
BAS Module Code |
Add the following code to 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. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '====== HEADER CONTROL ======== Public Const HEADER32_CLASS = "SysHeader32" Public Const HEADER_CLASS = "SysHeader" Public Type HD_ITEM mask As Long cxy As Long pszText As String hbm As Long cchTextMax As Long fmt As Long lParam As Long 'index of bitmap in ImageList: comctl 4.70+ iImage As Long 'where to draw this item: comctl 4.70+ iOrder As Long End Type 'Header info Public Const HDI_WIDTH = &H1 Public Const HDI_HEIGHT = HDI_WIDTH Public Const HDI_TEXT = &H2 Public Const HDI_FORMAT = &H4 Public Const HDI_LPARAM = &H8 Public Const HDI_BITMAP = &H10 Public Const HDI_IMAGE = &H20 Public Const HDI_DI_SETITEM = &H40 Public Const HDI_ORDER = &H80 'Header formats Public Const HDF_LEFT = 0 Public Const HDF_RIGHT = 1 Public Const HDF_CENTER = 2 Public Const HDF_JUSTIFYMASK = &H3 Public Const HDF_RTLREADING = 4 Public Const HDF_IMAGE = &H800 Public Const HDF_OWNERDRAW = &H8000& Public Const HDF_STRING = &H4000 Public Const HDF_BITMAP = &H2000 Public Const HDF_BITMAP_ON_RIGHT = &H1000 'Header styles Public Const HDS_HORZ = &H0 Public Const HDS_BUTTONS = &H2 Public Const HDS_HOTTRACK = &H4 Public Const HDS_HIDDEN = &H8 Public Const HDS_DRAGDROP = &H40 Public Const HDS_FULLDRAG = &H80 'Header messages Public Const HDM_FIRST = &H1200 Public Const HDM_GETITEMCOUNT = (HDM_FIRST + 0) Public Const HDM_INSERTITEM = (HDM_FIRST + 1) Public Const HDM_DELETEITEM = (HDM_FIRST + 2) Public Const HDM_GETITEM = (HDM_FIRST + 3) Public Const HDM_SETITEM = (HDM_FIRST + 4) Public Const HDM_LAYOUT = (HDM_FIRST + 5) Public Const HDM_HITTEST = (HDM_FIRST + 6) Public Const HDM_GETITEMRECT = (HDM_FIRST + 7) Public Const HDM_SETIMAGELIST = (HDM_FIRST + 8) Public Const HDM_GETIMAGELIST = (HDM_FIRST + 9) Public Const HDM_ORDERTOINDEX = (HDM_FIRST + 15) 'Header hittest messages Public Const HHT_NOWHERE = &H1 Public Const HHT_ONHEADER = &H2 Public Const HHT_ONDIVIDER = &H4 Public Const HHT_ONDIVOPEN = &H8 Public Const HHT_ABOVE = &H100 Public Const HHT_BELOW = &H200 Public Const HHT_TORIGHT = &H400 Public Const HHT_TOLEFT = &H800 Public Type WINDOWPOS hwnd As Long hWndInsertAfter As Long X As Long Y As Long cx As Long cy As Long flags As Long End Type Public Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Public Type HD_LAYOUT rc As RECT wp As WINDOWPOS End Type Public Type POINTAPI X As Long Y As Long End Type Public Type HD_HITTESTINFO pt As POINTAPI flags As Long iItem As Long End Type '====== OTHER NEEDED APIs ======== Public Const DEFAULT_GUI_FONT = 17 Public Const WM_SETFONT = &H30 Public Const WS_VISIBLE = &H10000000 Public Const WS_CHILD = &H40000000 Public Const WS_BORDER = &H800000 Public Const SWP_SHOWWINDOW = &H40 Public Declare Function GetStockObject Lib "gdi32" _ (ByVal nIndex As Long) As Long Public Declare Function GetDC Lib "user32" _ (ByVal hwnd As Long) As Long Public Declare Function SelectObject Lib "gdi32" _ (ByVal hdc As Long, _ ByVal hObject As Long) As Long Public Declare Function DeleteObject Lib "gdi32" _ (ByVal hObject As Long) As Long Public Declare Function CreateWindowEx Lib "user32" _ Alias "CreateWindowExA" _ (ByVal dwExStyle As Long, ByVal lpClassName As String, _ ByVal lpWindowName As String, ByVal dwStyle As Long, _ ByVal X As Long, ByVal Y As Long, _ ByVal nWidth As Long, ByVal nHeight As Long, _ ByVal hWndParent As Long, ByVal hMenu As Long, _ ByVal hInstance As Long, lpParam As Any) As Long Public Declare Function ShowWindow Lib "user32" _ (ByVal hwnd As Long, _ ByVal nCmdShow As Long) As Long Public Declare Function DestroyWindow Lib "user32" _ (ByVal hwnd As Long) As Long Public Declare Function SendMessage Lib "user32" _ Alias "SendMessageA" _ (ByVal hwnd As Long, ByVal wMsg As Long, _ ByVal wParam As Any, lParam As Any) As Long Public Declare Function IsWindow Lib "user32" _ (ByVal hwnd As Long) As Long Public Declare Function MoveWindow Lib "user32" _ (ByVal hwnd As Long, _ ByVal X As Long, _ ByVal Y As Long, _ ByVal nWidth As Long, _ ByVal nHeight As Long, _ ByVal bRepaint As Long) As Long Public Declare Function SetWindowPos Lib "user32" _ (ByVal hwnd As Long, _ ByVal hWndInsertAfter As Long, _ ByVal X As Long, ByVal Y As Long, _ ByVal cx As Long, ByVal cy As Long, _ ByVal wFlags As Long) As Long Public Declare Function GetClientRect Lib "user32" _ (ByVal hwnd As Long, lpRect As RECT) As Long |
Form Code |
The form shown appears straightforward, but there are
several frames utilized with their border style set to 0 in order to accommodate the option buttons. The following controls are required to
create the demo as shown:
If you need help, I've saved a monochrome control layout map form with the control names substituted for the captions (link opens a new browser window). |
|
Option Explicit Dim hHeader As Long 'constants for the option indices Private Const putBitmapLeft As Integer = 0 Private Const putBitmapRight As Integer = 1 Private Const putOverList As Integer = 0 Private Const putOverText As Integer = 1 Private Const putCaptionLeft As Integer = 0 Private Const putCaptionCentre As Integer = 1 Private Const putCaptionRight As Integer = 2 Private Const styleFlat = 0 Private Const styleHasButtons = 1 Private Const styleHottrack = 0 Private Const styleMoveable = 1 Private Sub chkIncludeBitmaps_Click() optBitmapPos(putBitmapLeft).Enabled = _ chkIncludeBitmaps.Value = 1 optBitmapPos(putBitmapRight).Enabled = _ chkIncludeBitmaps.Value = 1 End Sub Private Sub cmdEnd_Click() 'Free all resources associated with the header If IsWindow(hHeader) Then Call DestroyWindow(hHeader) End If Unload Me End Sub Private Sub cmdCreateSimpleHeader_Click() Dim ctrlWidth As Long Dim headerTotalButtons As Integer Dim headerHeight As Integer Dim nWidth As Long Dim i As Long Dim targetControl As Control 'retrieve the values from the text boxes headerTotalButtons = Val(txtNumHeaders) headerHeight = Val(txtHeaderHeight) 'create a header using the styles set from 'the form. Note that this creates the 'header but it is not yet visible. 'Return the handle to the new header (hHeader) hHeader = CreateHeader() 'if a handle exists ... If hHeader > 0 Then 'determine the control to position the header over 'Assume that the target is the listbox unless 'optPosOver(1) is on. Set targetControl = List1 If optPosOver(putOverText) Then Set targetControl = Text1 'move the header over the specified control '(targetcontrol), and returns the final width 'of the header. 'This displays the header in the correct position '(visible), but as the header as yet to have any 'header items set, the result is a gray panel 'invisible on a gray form. ctrlWidth = PositionHeader(hHeader, targetControl, headerHeight) 'calc the width of each control by dividing the 'size of the control by the number of header 'columns requested. nWidth = (targetControl.Width / Screen.TwipsPerPixelX) _ / headerTotalButtons 'For each, add a header item. 'This example uses the index to set both the 'header index AND the imagelist picture to 'assign, so assure that sufficient imagelist 'icons exist in your imagelist!! 'Setting the last param False causes the 'AddHeaderItem routine to ignore the imagelist 'item passed. For i = 0 To headerTotalButtons - 1 AddHeaderItem hHeader, _ i, _ nWidth, _ Trim$(txtHeaderCaption) & " " & CStr(i), _ i + 1, _ True Next End If 'disable most controls we can't set while running SetControlStates (hHeader = 0) End Sub Private Sub cmdDestroy_Click() If IsWindow(hHeader) Then Call DestroyWindow(hHeader) hHeader = False End If SetControlStates (hHeader = False) End Sub Private Sub cmdModify_Click() Dim nWidth As Long Dim i As Long Dim headerTotalButtons As Long headerTotalButtons = SendMessage(hHeader, _ HDM_GETITEMCOUNT, _ 0&, ByVal 0&) 'For each, add a header item. 'This example uses the index to set both the 'header index AND the imagelist picture to 'assign, so assure that sufficient imagelist 'icons exist in your imagelist!! 'Setting the last param False causes the 'AddHeaderItem routine to ignore the imagelist 'item passed. nWidth = (List1.Width / Screen.TwipsPerPixelX) _ / headerTotalButtons For i = 0 To headerTotalButtons - 1 Call SendMessage(hHeader, HDM_DELETEITEM, i, ByVal 0&) AddHeaderItem hHeader, _ i, _ nWidth, _ Trim$(txtHeaderCaption) & " " & CStr(i), _ i + 1, _ True Next End Sub Private Sub Form_Load() Dim r As Boolean Dim tmp As String 'perform some setup List1.AddItem "VBnet Home" List1.AddItem "Microsoft Home" List1.AddItem "MSDN Home" List1.AddItem "Visual Basic Home" List1.AddItem "Visual Studio Home" tmp = "VBnet Home" & vbCrLf tmp = tmp & "Microsoft Home" & vbCrLf tmp = tmp & "MSDN Home" & vbCrLf tmp = tmp & "Visual Basic Home" & vbCrLf tmp = tmp & "Visual Studio Home" Text1.Text = tmp 'initialize the common controls 'Note that the header class is initialized 'by passing the ICC_LISTVIEW_CLASSES flag If IsNewComctl32(ICC_LISTVIEW_CLASSES) = False Then tmp = "You do not have the latest comctrl32.dll" tmp = tmp & " file installed. All features of " tmp = tmp & "the common controls may not be available." MsgBox tmp End If 'finally, setup the controls optPosOver(0).Value = True chkIncludeBitmaps_Click SetControlStates hHeader = 0 End Sub Private Function CreateHeader() Dim dwHeaderStyles As Long 'create a variable with the selected header styles dwHeaderStyles = CreateHeaderStyles() 'Create the header using window style returned hHeader = CreateWindowEx(0, HEADER32_CLASS, vbNullString, _ dwHeaderStyles, _ 0, 0, 0, 0, _ hwnd, 0, _ App.hInstance, ByVal 0) 'even though it's a form-wide variable, return 'the handle as an indicator of success CreateHeader = hHeader End Function Private Function PositionHeader(hHeader As Long, _ ctrl As Control, _ headerHeight As Integer) As Long 'pass a control to this routine, and the header 'will position itself over it at the height specified. Dim rc As RECT Dim hFont As Long Dim hOldFont As Long rc.Left = ctrl.Left / Screen.TwipsPerPixelX 'offset the header by 2 (headerHeight - 2) to 'align the header exactly with the top of the 'control container. rc.Top = (ctrl.Top - ((headerHeight - 2) * Screen.TwipsPerPixelX)) _ / Screen.TwipsPerPixelY rc.Right = (ctrl.Width) / Screen.TwipsPerPixelX rc.Bottom = headerHeight 'move the header into position Call SetWindowPos(hHeader, 0&, _ rc.Left, rc.Top, _ rc.Right, rc.Bottom, _ SWP_SHOWWINDOW) 'if these next three lines aren't used, the 'header appears to use fixedsys bold fonts!! hFont = GetStockObject(DEFAULT_GUI_FONT) hOldFont = SelectObject(hHeader, hFont) Call SendMessage(hHeader, WM_SETFONT, hFont, ByVal True) 'and return the size of the control PositionHeader = (ctrl.Width) / Screen.TwipsPerPixelX End Function Private Function CreateHeaderStyles() As Long Dim style As Long 'these are the minimum requirements... style = WS_CHILD Or HDS_HORZ If optAppearance(styleHasButtons) Then style = style Or HDS_BUTTONS If chkHDStyles(styleHottrack) Then style = style Or HDS_HOTTRACK If chkHDStyles(styleMoveable) Then style = style Or HDS_DRAGDROP CreateHeaderStyles = style End Function Private Sub AddHeaderItem(hHeader As Long, itemNo As Long, _ nItemWidth As Long, _ sCaption As String, _ nImageNo As Long, _ useImage As Boolean) Dim HDI As HD_ITEM With HDI .mask = CreateHeaderItemMask() .fmt = CreateHeaderItemFormats() .cxy = nItemWidth .pszText = sCaption .cchTextMax = Len(HDI.pszText) If useImage Then .hbm = ImageList1.ListImages(nImageNo).Picture End If End With Call SendMessage(hHeader, HDM_INSERTITEM, itemNo, HDI) End Sub Private Function CreateHeaderItemFormats() As Long Dim fmt As Long fmt = HDF_STRING Or HDF_LEFT 'image, text or both? If chkImageOnly.Value = False Then fmt = fmt Or HDF_STRING End If 'caption alignment If optPosition(putCaptionLeft).Value Then fmt = fmt Or HDF_LEFT If optPosition(putCaptionCentre).Value Then fmt = fmt Or HDF_RIGHT If optPosition(putCaptionRight).Value Then fmt = fmt Or HDF_CENTER 'bitmaps If chkIncludeBitmaps.Value = 1 Then If optBitmapPos(putBitmapLeft) Then fmt = fmt Or HDF_BITMAP Else fmt = fmt Or HDF_BITMAP_ON_RIGHT End If End If CreateHeaderItemFormats = fmt End Function Private Function CreateHeaderItemMask() As Long Dim headerMask As Long 'basic mask headerMask = HDI_FORMAT Or HDI_HEIGHT Or HDI_WIDTH 'image, text or both? If chkImageOnly.Value = False Then headerMask = headerMask Or HDI_TEXT End If 'bitmaps If chkIncludeBitmaps.Value = 1 Then headerMask = headerMask Or HDI_BITMAP End If CreateHeaderItemMask = headerMask End Function Private Sub SetControlStates(state As Boolean) cmdCreateSimpleHeader.Enabled = state cmdDestroy.Enabled = Not state cmdModify.Enabled = Not state optPosOver(putOverList).Enabled = state optPosOver(putOverText).Enabled = state optAppearance(styleFlat).Enabled = state optAppearance(styleHasButtons).Enabled = state chkHDStyles(styleHottrack).Enabled = state chkHDStyles(styleMoveable).Enabled = state End Sub |
Comments |
Save and run the project. Set the No of Columns to
add to the new header, their height (18-21 is a good start), and some text to display as the header caption. In addition, specify whether to
display the header over the list or textbox .. you can add any other control type as well, and simply set the targetControl to point to this
control.
In the Appearance area, set the default styles for the header. You can accept the default formats in the Header Format Options frames, and can change these while the header exists. Clicking the Create button creates the header with the attributes selected. Once created, you can see other combinations of attributes by setting and clicking the Modify button. Modify is a bit of a misnomer .. in actuality I'm using the HDM_DELETEITEM and HDM_INSERTITEM messages to destroy and recreate a header. You could also use the HDM_SETITEM message, but you will first need to retrieve the current state using HDM_GETITEM, flip the appropriate flags, and then call SendMessage with _SETITEM to set the new attributes. You can delete the header using the Destroy button, reset the style flags, and see the new behaviour. The code detailed here applies the same attributes to all header items, that is, all left, all right, all with images etc. You can of course selectively set each header item's format individually at either the HDM_INSERTITEM time or using HDM_SETITEM as mentioned above. To exit, be sure to use the cmdEnd button and not the IDE stop button to conserve Windows resources. |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |