Subclassing a Listbox to Display an Image in Background by Sanyam Aggarwal
This article assumes that you are familiar with subclassing-if you want to start off with subclassing read the artice previously submitted by me. Though this article user VB 6.0, concepts learned from this can easily be applied to .NET projects.
The key to subclassing a ListBox to display an Image in its background is WM_CTLCOLORLISTBOX event send by the Windows to the parent form of Listbox. By responding to this message, the parent window can set the text and background colors of the list box by using the given display device context handle.
The wParam parameter contains the handle to device context for the list box whereas lParam is the handle of the Listbox itself. If an application processes this message, it must return a handle to a brush. The system uses the brush to paint the background of the list box. This is our opportunity, we have to return the brush for painting the listbox so we can set a custom brush and operating system will use it to draw contents on the Listbox.
The SetBkMode function which we will be using in our example program sets the background mix mode of the specified device context. The background mix mode is used with text, hatched brushes, and pen styles that are not solid lines. We must set the background mode of the listbox to transparent otherwise results will not be pleasant, though the picture will be displayed correctly, the Items will appear against a white background thus spoiling the whole effect. Also we need the InvalidateRect function. The InvalidateRect function adds a rectangle to the specified window's update region. The update region represents the portion of the window's client area that must be redrawn. It accepts a handle to window whose region is to be updated and a rectangle structure which represents the regoin to add to update region. If we pass 0 as update region, whole regoin is caused to be updated. Alright, but why do we need this? Just to prevent flickering which occurs when the Listbox is scrolled.
Finally we need the CreatePatternBrush API function which creates a logical brush with the specified bitmap pattern. It return the handle to the Brush created. So if you want that you Listbox should have somePic.bmp as its background, all you need is to load this picture in ImageBox and get a handle to the Brush like this:
hBrush=CreatePatternBrush(Image1.picture.handle)
Thats it! You are now ready for the Code sample:
1)Open a new Standard EXE project. Add a listbox named List1. Also add an Image control Image1 and load in it the Image you want to use as the background for your ListBox.
2)Add a Bas Module and add the Following Code:
Option Explicit
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _ (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private 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
Private Declare Function InvalidateRect Lib "user32" (ByVal hwnd As Long, _ ByVal lpRect As Long, ByVal bErase As Long) As Long
Public Declare Function CreatePatternBrush Lib "gdi32" (ByVal hBitmap As Long) _ As Long
Private Declare Function SetBkMode Lib "gdi32" (ByVal hdc As Long, ByVal nBkMode As Long) _ As Long
Private Const WM_CTLCOLOREDIT = &H133 Private Const WM_CTLCOLORLISTBOX = &H134 Private Const WM_VSCROLL = &H115 Private Const WM_ERASEBKGND = &H14 Public Const GWL_WNDPROC = (-4)
Public hBrush As Long Public prevFuncPointer As Long Public prevListboxFuncPointer As Long
Public Function frmWndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, _ ByVal lParam As Long) As Long
If (uMsg = WM_CTLCOLORLISTBOX) And hBrush <> 0 Then ' Make the words print transparently SetBkMode wParam, 1
' Allow the original process to set text color, etc. from the lbx properties. CallWindowProc prevFuncPointer, hwnd, uMsg, wParam, lParam
' Return our custom brush instead of the default one frmWndProc = hBrush Else frmWndProc = CallWindowProc(prevFuncPointer, hwnd, uMsg, wParam, lParam) End If End Function
Public Function lbWndProc(ByVal hwnd As Long, ByVal uMsg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long
' Force the control to repaint itself every time the scroll message is received.
If uMsg = WM_VSCROLL Then ' Force windows to repaint the listbox InvalidateRect hwnd, 0, 0
' Invoke the default process lbWndProc = CallWindowProc(prevListboxFuncPointer, hwnd, uMsg, wParam, lParam)
ElseIf uMsg = WM_ERASEBKGND Then ' Don't return anything. Processing the default routine will actually cause ' flickering when' scrolling. lbWndProc = 1 Else ' Invoke the default process lbWndProc = CallWindowProc(prevListboxFuncPointer, hwnd, uMsg, wParam, lParam) End If
End Function
3)To the Form, add the following code:
Private Sub Form_Load() 'Add items to Listbox at RUN TIME ONLY! dim i as integer For i=0 to 100 list1.AddItem "Item# " & i Next ' create a brush from specified image.(Image1 must be there!) hBrush = CreatePatternBrush(Image1.Picture.Handle) ' ' Subclass the window prevFuncPointer = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf frmWndProc) prevListBoxFuncPointer = SetWindowLong(List1.hwnd, GWL_WNDPROC, AddressOf lbWndProc) End Sub
Private Sub Form_Unload(Cancel As Integer) ' Unsubclass (return the original processes) SetWindowLong Me.hwnd, GWL_WNDPROC, prevFuncPointer SetWindowLong List1.hwnd, GWL_WNDPROC, prevListBoxFuncPointer End Sub
CAUTION: When you use this technique to Display Images in BackGround, YOU MUST ADD ALL ELEMENTS AT RUNTIME OTHERWISE UNEXPECTED RESULTS MAY OCCUR!
Now with this Caution comes a Good news:
Wanted to Display Images in background of TextBoxes and RichTextBoxes so as to Customize their apperance?
If yes, you have already learnt so! Believe me the Technique is really the same, you just have to find it out. It is unbeleivable same!
So look for it!!
HINT: Look for WM_CTLCOLOREDIT in MSDN Documentation.
|
| Author: Kanya 27 May 2004 | Member Level: Bronze Points : 0 |
I am getting error for 1)'hwnd' is not a member of 'Images.Form1'. 2)for windows form I tried Imagelist/Picture box-giving error as Value of type 'System.IntPtr' cannot be converted to 'Long'.
|
| Author: sanyam aggarwal 31 May 2004 | Member Level: Bronze Points : 0 |
Use handle.ToInt32 Method wherever they need a handle to the Form.
Convert System.InPtr to long using System.InPtr32.ToInt32 and then pass them to API declarations.
Hope this will solve your problem.
|
| Author: Thangavel Rajan 21 Aug 2006 | Member Level: Bronze Points : 0 |
Hi,
While i am trying in VB.net, I am getting this error : 'AddressOf' expression cannot be converted to 'Long' because 'Long' is not a delegate type.
Thangavel
|