Using Visual Styles with Owner-Drawn Controls |
Drawing Controls with Visual Styles
You can choose whether or not to display owner-drawn controls using the available visual styles, and if you choose to apply visual styles, you can do so selectively.
First your application should determine whether visual styles are available, by calling IsAppThemed. If visual styles are not available, use fallback code to draw the control.
If visual styles are available, you can use visual-styles functions such as DrawThemeText to render your control. Note that DrawThemeTextEx enables you to customize the appearance of text, retaining some properties of the theme font while modifying others.
To draw a control in the current visual style
The following example code demonstrates one way to draw a button control in the current visual style.
C++
HTHEME hTheme = NULL;
hTheme = OpenThemeData(hwndButton, L"Button"); ... DrawMyControl(hDC, hwndButton, hTheme, iState); ... if (hTheme) { CloseTheme(hTheme); }
void DrawMyControl(HDC hDC, HWND hwndButton, HTHEME hTheme, int iState) { RECT rc, rcContent; TCHAR szButtonText[255]; HRESULT hr; size_t cch;
GetWindowRect(hwndButton, &rc); GetWindowText(hwndButton, szButtonText, (sizeof(szButtonText)/sizeof(szButtonText[0])+1)); hr = StringCchLength(szButtonText, (sizeof(szButtonText)/sizeof(szButtonText[0])), &cch); if (hTheme) { hr = DrawThemeBackground(hTheme, hDC, BP_BUTTON, iState, &rc, 0); // Always check your result codes.
hr = GetThemeBackgroundContentRect(hTheme, hDC, BP_BUTTON, iState, &rc, &rcContent); hr = DrawThemeText(hTheme, hDC, BP_BUTTON, iState, szButtonText, cch, DT_CENTER | DT_VCENTER | DT_SINGLELINE, 0, &rcContent); } else { // Draw the control without using visual styles. } }
PowerBASIC
LOCAL hTheme AS DWORD LOCAL bstrClassList AS STRING
bstrClassList = UCODE$("Button") hTheme = OpenThemeData(hwndButton, STRPTR(bstrClassList)) ... DrawMyControl(hDC, hwndButton, hTheme, iState) ... IF hTheme THEN CloseTheme(hTheme) END IF
SUB DrawMyControl(BYVAL hDC AS DWORD, BYVAL hwndButton AS DWORD, BYVAL hTheme AS DWORD, BYVAL iState AS LONG)
LOCAL rc, rcContent AS RECT LOCAL strButtonText AS STRING LOCAL hr AS LONG
GetWindowRect(hwndButton, rc) strButtonText = SPACE$(255) GetWindowText(hwndButton, BYVAL STRPTR(strButtonText), _ (LEN(strButtonText)) strButtonText = UCODE$(strButtonText) IF hTheme THEN hr = DrawThemeBackground(hTheme, hDC, %BP_BUTTON, _ iState, rc, 0) ' // Always check your result codes. hr = GetThemeBackgroundContentRect(hTheme, hDC, _ %BP_BUTTON, iState, rc, rcContent) hr = DrawThemeText(hTheme, hDC, %BP_BUTTON, iState, _ STRPTR(strButtonText), LEN(strButtonText), _ %DT_CENTER OR %DT_VCENTER OR %DT_SINGLELINE, _ 0, rcContent) ELSE ' // Draw the control without using visual styles. END IF END SUB
The following example code is in the WM_PAINT message handler for a subclassed button control. The text for the control is drawn in the visual styles font, but the color is application-defined depending on the state of the control.
C++
// textColor is a COLORREF whose value has been set according to whether the button is "hot". // paint is the PAINTSTRUCT whose members are filled in by BeginPaint. HTHEME theme = OpenThemeData(hWnd, L"button"); if (theme) { DTTOPTS opts = { 0 }; opts.dwSize = sizeof(opts); opts.crText = textColor; opts.dwFlags |= DTT_TEXTCOLOR; WCHAR caption[255]; size_t cch; GetWindowText(hWnd, caption, 255); StringCchLength(caption, 255, &cch); DrawThemeTextEx(theme, paint.hdc, BP_PUSHBUTTON, CBS_UNCHECKEDNORMAL, caption, cch, DT_CENTER | DT_VCENTER | DT_SINGLELINE, &paint.rcPaint, &opts); CloseThemeData(theme); } else { // Draw the control without using visual styles. }
PowerBASIC
' // textColor is a COLORREF whose value has been set according to whether the button is "hot". ' // paint is the PAINTSTRUCT whose members are filled in by BeginPaint.
LOCAL theme AS DWORD LOCAL bstrClassList AS STRING bstrClassList = UCODE$("button")
theme = OpenThemeData(hWnd, STRPTR(bstrClassList)) IF theme THEN LOCAL opts AS DTTOPTS opts.dwSize = SIZEOF(opts) opts.crText = textColor opts.dwFlags = %DTT_TEXTCOLOR LOCAL caption AS STRING caption = SPACE$(255) GetWindowText(hWnd, caption, 255) caption = UCODE$(caption) DrawThemeTextEx(theme, paint.hdc, %BP_PUSHBUTTON, %CBS_UNCHECKEDNORMAL, _ STRPTR(caption), LEN(caption), %DT_CENTER OR DT_VCENTER OR %DT_SINGLELINE, _ paint.rcPaint, opts) CloseThemeData(theme) ELSE ' // Draw the control without using visual styles. END IF
You can use parts from other controls and render each part separately. For example, for a calendar control that consists of a grid, you can treat each square formed by the grid as a toolbar button, by obtaining the theme handle as follows:
C++ OpenThemeData(hwnd, L"Toolbar");
PowerBASIC LOCAL bstrClassList AS STRING bstrClassList = UCODE$("Toolbar") OpenThemeData(hwnd, STRPTR(bstrClassList))
You can mix and match control parts by calling OpenThemeData multiple times for a given control and using the appropriate theme handle to draw different parts. In some visual styles, however, certain parts might not be compatible with other parts.
Another approach to rendering controls in the active visual style is to use the system colors. Most system colors are set when a visual style file is applied.
Responding to Theme Changes
When your control receives a WM_THEMECHANGED message and is holding a global handle to the theme, it should do the following:
C++ //This code sample illustrates the two calls. case WM_THEMECHANGED: CloseThemeData (g_hTheme); g_hTheme = OpenThemeData (hwnd, L"MyClassName");
PowerBASIC ' // This code sample illustrates the two calls. CASE %WM_THEMECHANGED CloseThemeData(g_hTheme) LOCAL bstrClassList AS STRING bstrClassList = UCODE$("MyClassName") g_hTheme = OpenThemeData(hwnd, STRPTR(bstrClassList))
|