|
-
Apr 23rd, 2010, 11:41 AM
#1
[VB6] Themes - Frames
Those that are familiar with VB, themes and controls on frames may find this information useful.

Background: Using manifest files, we can have many VB controls themed, even the frame control. However, controls that contain transparency do not render correctly in frame controls. Shown in the "after" & "before" images above.
Solutions:
1. The widely accepted solution is don't use frames to host controls that have these problems, mainly: command buttons & option buttons.
2. Subclass the frame and trap/respond to a single message.
In the image above, you can see black where the themed control is partially transparent. The reason why they paint black is because the Frame does not respond to WM_PRINTCLIENT messages. If we respond for the frame, all is good.
The problem with using Frames with themes and only hosting controls that appear to draw fine is that it isn't future-proof. What happens if the new operating system themes checkboxes transparently? themes combo/listboxes with rounded corners? What used to look good in XP now doesn't in the new operating system. Subclassing helps ensure app looks good with future themes.
Assuming you are familiar with subclassing, here would be a workable/resuable example. Just start subclassing your frame on form load. It is recommended to unsubclass it on form unload but is not absolutely necessary. Subclassing can cause crashes in IDE and the following should only be activated before compiling to be completely safe; use caution when subclassing in uncompiled projects.
This code must be placed in a bas module, not a form, not a class.
Code:
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function CallWindowProc Lib "user32.dll" 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 GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function GetProp Lib "user32.dll" Alias "GetPropA" (ByVal hWnd As Long, ByVal lpString As String) As Long
Private Declare Function SetProp Lib "user32.dll" Alias "SetPropA" (ByVal hWnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
Private Declare Function DefWindowProc Lib "user32.dll" Alias "DefWindowProcA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Const WM_PRINTCLIENT As Long = &H318
Private Const WM_PAINT As Long = &HF&
Private Const GWL_WNDPROC As Long = -4
Private Const WM_DESTROY As Long = &H2
Public Sub SubclassFrame(FramehWnd As Long, ReleaseSubclass As Boolean)
Dim prevProc As Long
prevProc = GetProp(FramehWnd, "scPproc")
If ReleaseSubclass Then
If prevProc Then
SetWindowLong FramehWnd, GWL_WNDPROC, prevProc
SetProp FramehWnd, "scPproc", 0&
End If
ElseIf prevProc = 0& Then
SetProp FramehWnd, "scPproc", GetWindowLong(FramehWnd, GWL_WNDPROC)
SetWindowLong FramehWnd, GWL_WNDPROC, AddressOf WndProc_Frame
End If
End Sub
Private Function WndProc_Frame(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim prevProc As Long
prevProc = GetProp(hWnd, "scPproc")
If prevProc = 0& Then
WndProc_Frame = DefWindowProc(hWnd, uMsg, wParam, lParam)
ElseIf uMsg = WM_PRINTCLIENT Then
SendMessage hWnd, WM_PAINT, wParam, ByVal 0&
Else
If uMsg = WM_DESTROY Then SubclassFrame hWnd, True
WndProc_Frame = CallWindowProc(prevProc, hWnd, uMsg, wParam, lParam)
End If
End Function
Edited: Note that the frame issue isn't just with frames, it applies to forms, pictureboxes and any VB container really, but not exactly in the same way. Don't know if you ever noticed, but add an option button on your form that overlaps an image. Is the option button bkg transparent? It is in the example above. Add a command button over the image. Are the corners round and bkg transparent? It is in the above example. Adding command buttons and option buttons on a dark form background also highlights the problem. I don't know when WM_PRINTCLIENT was first implemented in Windows, but my guess is after VB was designed and VB service packs didn't appear to have implemented it (correctly or at all). You can use the same principle above for the form itself or a picturebox control too. If subclassing a form, definitely recommend unsubclassing it on form unload.
P.S. Regarding transparent bkg on the option button; not sure if a pure API option button is also transparent -- haven't tested it.
Edited: There are 2 ways to included manifests with a compiled application. In this other project of mine, description and creation of manifests, both ways, can be found.
Last edited by LaVolpe; Apr 23rd, 2010 at 03:55 PM.
Reason: tweaked the subclass routine to make it more fool-proof
-
Apr 23rd, 2010, 02:53 PM
#2
Re: [VB6] Themes - Frames
Looks cool thanks for shareing with us
-
Apr 27th, 2010, 12:25 AM
#3
New Member
Re: [VB6] Themes - Frames
for controls into the frames...to make it change with themes normally, it's very simple you can adding with picturebox into frame and then in the picturebox you add that controls such as combobox, checkbox and button, and then compile it, you will see...the button and checkbox is change with normally...that's all..
-
Apr 27th, 2010, 08:02 AM
#4
Re: [VB6] Themes - Frames
 Originally Posted by Bayu Malmsteen
for controls into the frames...to make it change with themes normally, it's very simple you can adding with picturebox into frame and then in the picturebox you add that controls such as combobox, checkbox and button, and then compile it, you will see...the button and checkbox is change with normally...that's all..
Sure, you've also just added an unnecessary window to your application. But that should be listed as Option #3 in my first post.
Edited:
But the problems still remain. Make your picturebox backcolor black and add a button, compile, apply manifest and see that the button does not show blended rounded corners. Subclassing is the correct method in my opinion, but still not perfect. Even responding to the WM_PRINTCLIENT message for the container, it doesn't draw perfectly and this I believe is a flaw in the way common controls library tries to theme VB intrinsic controls.
Last edited by LaVolpe; Apr 27th, 2010 at 03:03 PM.
-
Jun 12th, 2010, 12:13 PM
#5
New Member
Re: [VB6] Themes - Frames
Nice code LaVolpe Thanks
-
Jun 13th, 2010, 08:41 AM
#6
Addicted Member
Re: [VB6] Themes - Frames
Thanks for nice code, but if I want to subclass a picture box the same way, what exactly in the code above would change or the same code would work "as is"?
-
Jun 14th, 2010, 08:07 AM
#7
Re: [VB6] Themes - Frames
7edm: the code can be tweaked to subclass any window. Obviously you will want to modify the WndProc_Frame routine to view/modify whatever messages are of interest. Please post additional questions in the appropriate forum (VB6, .Net, etc).
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|