PDA

Click to See Complete Forum and Search --> : [VB6] Themes - Frames


LaVolpe
Apr 23rd, 2010, 11:41 AM
Those that are familiar with VB, themes and controls on frames may find this information useful.
77607
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.
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 (http://www.vbforums.com/showthread.php?t=606736), description and creation of manifests, both ways, can be found.

BenJones
Apr 23rd, 2010, 02:53 PM
Looks cool thanks for shareing with us

Bayu Malmsteen
Apr 27th, 2010, 12:25 AM
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..

LaVolpe
Apr 27th, 2010, 08:02 AM
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.

MrSmith.
Jun 12th, 2010, 12:13 PM
Nice code LaVolpe Thanks :)

7edm
Jun 13th, 2010, 08:41 AM
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"?

LaVolpe
Jun 14th, 2010, 08:07 AM
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).