|
Contents
Introduction
Application Manifest
Correcting Toolbar Tooltips
Correcting Right-aligned SingleLineEdits
MultiLineEdit Borders
Tab Controls
Tab Pages
WinDLLVersion
Enable/Disable Visual Styles
Introduction
XP provides visual styles to define the appearance of controls
and windows.
With VO 2.7/a/b the only thing necessary for proper XP style
support is the inclusion of the application manifest resource. All
the other changes listed here are not required any more because I
have ensured that they are now part of VO 2.7.
There are two ways to include the application manifest in the
application: 1. use the "Application Manifest" step below or
2. import ManSrc.MEF from the VO 2.7 AppWiz directory.
If your application is not showing the XP styles you should check
that the manifest file (pointed to by the manifest resource) is
correct.
The best way to check if your system is configured correctly is to
create the DataAware Controls sample in the VO new application
gallery. Then include the XP application manifest in this sample and
run it. You should see the XP style controls.
However, if you just take your standard VO 2.5 app onto XP the
controls just look the same as they used to under Win9x/ME/2000. To
get the new look controls you need to make a change to your
application - you need to enable the XP visual styles. The table
below shows the difference in appearance of some of the controls. In
addition to the immediate visual difference, under XP many of the
controls also change their appearance when the mouse moves over
them.
| |
Controls without
XP Visual Style support |
Controls with XP
Visual Style support |
| Pushbutton |
 |
 |
Checkbox,
RadioButton
Slider |
 |
 |
| Toolbar button |
 |
 |
| Progress bar |
 |
 |
Application Manifest
Fortunately the process to do this is simple, and the same EXE
can be run on the older Windows platforms as well as XP. On the
older Windows the controls look as they always did, on XP they take
on the new look.
The first steps are:
- Create a manifest file. This file will be compiled into
your app as a resource and will tell XP that you want to use the
new common controls. Copy the following XML and put it into a
file - for our example call the file Application.man.
<?xml version="1.0"
encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="X86"
name="CompanyName.ProductName.YourApp"
type="win32"/>
<description>Your application description
here.</description>
<dependency>
<dependentAssembly>
<assemblyIdentitytype="win32" name="Microsoft.Windows.Common-Controls"
version="6.0.0.0" processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
</assembly>
You can change the "CompanyName.ProductName.YourApp"
and "Your application description here." to whatever
you want.
You can download a copy of application.man from here: http://www.piko.com.au/ftp/application.zip.
- Create some DEFINEs in your application:
DEFINE RT_MANIFEST := 24
DEFINE CREATEPROCESS_MANIFEST_RESOURCE_ID := 1
|
- Create a RESOURCE statement to bring your manifest file into
the EXE. For our example I have put the manifest file into the
VO executable directory. Note the following should appear as one
line in the VO source editor
RESOURCE CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST %executabledir%\application.man
|
- Compile and build your EXE. You will not see any difference on
non-XP Windows. But on XP you will now see the new common
controls, with the new styles, in use in your application.
Correcting Toolbar
ToolTips
The steps above go a long way towards providing the proper XP
look-and-feel. However there are some additional things that need to
be addressed. The first problem is that the application toolbar
loses its tooltips. This can be corrected by adding in one method,
one function and one structure. No other changes are required to
your existing code for this step. Just add the code below and the
toolbar tooltip problem is fixed.
STRUCT _winTOOLINFOXP ALIGN 1
MEMBER cbSize AS DWORD
MEMBER uFlags AS DWORD
MEMBER hwnd AS PTR
MEMBER uId AS DWORD
MEMBER rect IS _winRECT
MEMBER hinst AS PTR
MEMBER lpszText AS PSZ
MEMBER lParam AS LONG
|
METHOD ControlNotify(oControlNotifyEvent) CLASS AppWindow
IF oControlNotifyEvent:NotifyCode == TTN_NEEDTEXT
ProcessToolTip(SELF,oControlNotifyEvent)
ELSE
SUPER:ControlNotify(oControlNotifyEvent)
ENDIF
|
FUNCTION ProcessToolTip(oOwner,oControlNotifyEvent)
LOCAL nCode AS LONG
LOCAL lParam AS LONG
LOCAL oControl AS Control
LOCAL strucToolInfo IS _winTOOLINFOXP
LOCAL strucToolTip AS _winTOOLTIPTEXT
LOCAL strucNotify AS _winNMHDR
LOCAL cTipText AS STRING
LOCAL oWindow AS OBJECT
nCode := oControlNotifyEvent:NotifyCode
lParam := oControlNotifyEvent:lParam
oControl := oControlNotifyEvent:Control
strucNotify := PTR(_CAST, lParam)
strucToolInfo.cbSize := _SizeOf(_winTOOLINFOXP)
strucToolInfo.hwnd := strucNotify.hwndFrom
SendMessage(strucNotify.hwndFrom, TTM_GETCURRENTTOOL, 0, ;
LONG(_CAST, @strucToolInfo))
oWindow := __WCGetControlByHandle(strucToolInfo.hwnd)
IF (oWindow == NULL_OBJECT)
oWindow := __WCGetControlByHandle(GetParent(strucToolInfo.hwnd))
ENDIF
strucToolTip := PTR(_CAST, lParam)
IF IsInstanceOf(oWindow, #ToolBar)
cTipText := oWindow:GetTipText(strucToolTip.hdr.idFrom, ;
#MenuItemID)
IF IsMethod(oOwner, #StatusMessage) .and. ;
(oOwner:Menu != NULL_OBJECT)
Send(oOwner, #StatusMessage, ;
oOwner:Menu:Hyperlabel(strucToolTip.hdr.idFrom))
ENDIF
ELSEIF IsInstanceOf(oWindow, #TabControl)
cTipText := oWindow:GetTipText(;
oWindow:__GetSymbolFromIndex(strucToolTip.hdr.idFrom))
ENDIF
IF Empty(cTipText)
IF _And(strucToolTip.uFlags, TTF_IDISHWND) > 0
oWindow := __WCGetControlByHandle(strucToolTip.hdr.idFrom)
IF (oWindow == NULL_OBJECT)
oWindow := __WCGetControlByHandle(;
GetParent(strucToolTip.hdr.idFrom))
IF (oWindow == NULL_OBJECT)
oWindow := __WCGetControlByHandle(;
GetParent(GetParent(strucToolTip.hdr.idFrom)))
ENDIF
IF !IsInstanceOf(oWindow, #ComboBox) .and. ;
!IsInstanceOf(oWindow, #IPAddress)
oWindow := NULL_OBJECT
ENDIF
ENDIF
ELSE
oWindow := __WCGetControlByHandle(GetDlgItem(oOwner:handle(), ;
INT(_CAST, strucToolTip.hdr.idFrom)))
ENDIF
IF (oWindow != NULL_OBJECT)
cTipText := oWindow:ToolTipText
IF Empty(cTipText) .and. oWindow:UseHLForToolTip
cTipText := oWindow:Hyperlabel:Description
ENDIF
ENDIF
ENDIF
IF Empty(cTipText)
strucToolTip.lpszText := NULL_PSZ
ELSE
strucToolTip.lpszText := Cast2Psz(cTipText)
ENDIF
RETURN NIL
|
Correcting
Right-aligned SingleLineEdits
The usual way to achieve right aligned SingleLineEdit controls is
to make use of the ExAlignment property that is available on the
ExStyles tab in the VO Window Editor. This causes the control to be
created with the WS_EX_RIGHT style enabled. However under the XP
Visual Styles a SingleLineEdit needs to have the ES_RIGHT style
enabled to right align the text in the control. This style must be
applied at the time the control is created - it cannot be set later.
This means we need to change the window editor's configuration so
that the code generated will contain ES_RIGHT when we want it. This
is done by editing the CAVOWED.INF file in the CAVO25\BIN directory.
The steps are:
- Backup the CAVOWED.INF file
- Edit CAVOWED.INF in a text editor
- Search for [CONTROL:TEXTCONTROL:EDIT]
- Put this line after the other WindowStyles in that section:
WindowStyle19=Alignment,ES_LEFT:ES_CENTER:ES_RIGHT(EDITALIGN)
- Search for SingleLineStyles=
- Put ",Alignment" (without quotes) at the end of the
line
- Save CAVOWED.INF.
The next time you open the window editor you should see that
there is an Alignment property on the Styles property page for
SingleLineEdit controls, and you can choose Left, Center and Right
alignments. Use this property to obtain the correct alignment when
using XP Visual Styles.
MultiLineEdit
Borders
A MultiLineEdit (MLE) control can display an irregular bottom
border when used with the XP Visual Styles. This strange border seen
at the bottom of the control is actually the top of the control's
horizontal scroll bar. The VO SDK actually automatically sets the
WS_HSCROLL style for the MLE. However the code doesn't resize the
control, so the horizontal scroll bar sits at the bottom of the
control, out of sight under previous Windows versions but peeking
out under XP.
If you force the control to resize, e.g. call the __RescalCntlB()
method or change the control's dimensions, the scroll bar appears
under all Windows versions. __RescalCntlB() when called with the
current font of the control will actually leave it the same size it
was originally but by going through the process the scroll bar will
appear.
So if you want the WS_HSCROLL property for the MLE just put
something like this in the postinit of the window:
oDCMultiLineEdit1:__RescalCntlB(oDCMultiLineEdit1:font:handle())
|
Better still, put the resize into a sublcass of MultiLineEdit and
use that instead.
If you don't want WS_HSCROLL on the MLE, just put this into the
window postinit:
oDCMultiLineEdit1:setstyle(WS_HSCROLL,FALSE)
|
Tab Controls
Tab controls used to support various positions for the tabs: top,
bottom, left and right. Under XP and version 6 of the common
controls there is only one choice: the top. You can read about the
tab styles at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/tab/styles.asp.
For example, it says this about the vertical (left/right) style:
TCS_VERTICAL
Version 4.70. Tabs appear at the left side of the control, with tab
text displayed vertically. This style is valid only when used with
the
TCS_MULTILINE style. To make tabs appear on the right side of the
control, also use the TCS_RIGHT style. This style is not supported
if
you use ComCtl32.dll version 6.
Tab Pages
Tab pages by default do not conform to the XP visual styles -
they display "unthemed", as a standard window. Some code
is necessary to force the visual style to be used - you need to call
the function EnableThemeDialogTexture and pass it the handle of the
tab page that you want to display using visual styles. In the
example below the code has been put in the PostInit() of a tab page
called TabControl1_Page1. In your applications you might choose to
put similar code into a TabControl method, shown in the second
example below - you can then call this method from the PostInit() of
the window owning the TabControl. Note that we also have to test
whether or not visual styles are in use in the application, and this
is done by checking which version of the common controls is loaded.
This checking makes use of the WinDLLVersion class which is also
included below.
DEFINE
ETDT_DISABLE := 0x01
DEFINE ETDT_ENABLE :=
0x02
DEFINE ETDT_ENABLETAB =
0x06
DEFINE ETDT_USETABTEXTURE :=
0x04 |
METHOD
PostInit(oParent,uExtra)
CLASS TabControl1_Page1
LOCAL
hModule AS PTR
LOCAL
hFunc AS PTR
LOCAL h AS
PTR
LOCAL
dwFlags AS DWORD
// Check if the
app is
using the newer
// common
control for XP visual styles
IF
WinDLLVersion{"COMCTL32"}:major
>= 6
//
Get
the
theme DLL
hModule :=
LoadLibrary(PSZ("uxtheme.dll"))
IF
! hModule ==
NULL_PTR
//
Find the function we need
hFunc
:= GetProcAddress(hModule,PSZ("EnableThemeDialogTexture"))
IF
! hFunc ==
NULL_PTR
h
:= SELF:handle()
dwFlags
:= ETDT_ENABLETAB
//
Call the function
PCALL(hFunc,h,dwFlags)
ENDIF
ENDIF
ENDIF
RETURN NIL |
METHOD
ForcePageXPStyle() CLASS
TabControl
LOCAL
hModule AS PTR
LOCAL
hFunc AS PTR
LOCAL h AS
PTR
LOCAL
dwFlags AS DWORD
LOCAL
nCount AS DWORD
LOCAL i AS
DWORD
LOCAL
oPage AS OBJECT
// Check if the
app if using the newer
// common
control for XP visual styles
IF
WinDLLVersion{"COMCTL32"}:major
>= 6
//
Find the theme DLL
hModule :=
GetModuleHandle(PSZ("uxtheme.dll"))
IF
! hModule ==
NULL_PTR
//
Find the function we need
hFunc
:= GetProcAddress(hModule,PSZ("EnableThemeDialogTexture"))
IF
! hFunc ==
NULL_PTR
dwFlags
:= ETDT_ENABLETAB
nCount
:= SELF:tabcount
//
Go
through all tab pages
FOR
i := 1
UPTO nCount
oPage
:= SELF:GetTabPage(i)
h
:= oPage:handle()
//
Call the function
PCALL(hFunc,h,dwFlags)
NEXT
ENDIF
ENDIF
ENDIF |
WinDLLVersion
CLASS
WinDLLVersion
INSTANCE
DLLName AS STRING
PROTECT
MajorVersion AS DWORD
PROTECT
MinorVersion AS DWORD
PROTECT
Build AS DWORD
PROTECT
PlatformId AS DWORD |
ACCESS
Build CLASS WinDLLVersion
RETURN SELF:Build
ASSIGN DLLName(cDLL)
CLASS WinDLLVersion
LOCAL
hDLL AS PTR
LOCAL
hFunc AS PTR
LOCAL
pVersionInfo AS _WINDLLVERSIONINFO
SELF:DLLName
:= cDLL
IF !
(hDLL :=
GetModuleHandle(PSZ(cDLL)))
== NULL_PTR
IF
! (hFunc
:= GetProcAddress(hDLL,PSZ("DllGetVersion")))
== NULL_PTR
pVersionInfo
:= MemAlloc(_sizeof(_winDLLVERSIONINFO))
pVersionInfo.cbSize
:= _sizeof(_winDLLVERSIONINFO)
IF
PCALL(hFunc,pVersionInfo)
== 0
SELF:MajorVersion
:= pVersionInfo.dwMajorVersion
SELF:MinorVersion
:= pVersionInfo.dwMinorVersion
SELF:Build
:= pVersionInfo.dwBuildNumber
SELF:PlatformId
:= pVersionInfo.dwPlatformId
ENDIF
MemFree(pVersionInfo)
ENDIF
ENDIF
METHOD init(cDLL)
CLASS WinDLLVersion
SELF:DLLName
:= cDLL
ACCESS IsWinNT()
CLASS WinDLLVersion
RETURN SELF:PlatformId
== DLLVER_PLATFORM_NT
ACCESS Major CLASS
WinDLLVersion
RETURN SELF:MajorVersion
ACCESS Minor CLASS
WinDLLVersion
RETURN SELF:MinorVersion
ACCESS Platform CLASS
WinDLLVersion
RETURN SELF:PlatformId |
STRUCTURE
_winDLLVERSIONINFO
MEMBER
cbSize AS DWORD
MEMBER
dwMajorVersion AS DWORD
MEMBER
dwMinorVersion AS DWORD
MEMBER
dwBuildNumber AS DWORD
MEMBER
dwPlatformId AS DWORD
DEFINE DLLVER_PLATFORM_NT
:= 0x00000002
// Windows NT
DEFINE DLLVER_PLATFORM_WINDOWS
:= 0x00000001
// Windows 9x |
Enable/Disable
Visual Styles
There are two ways the user can override the XP visual styles for
an application:
1. Right click on the EXE. Select Properties. Go to the
Compatibility tab. One of the settings there is "Disable visual
styles".
2. From the All Programs menu select Accessories and run the Program
Compatibility Wizard.
If you want to programmatically control the visual style you can use
call the function SetThemeAppProperties. To disable visual styles
call it like this:
SetThemeAppProperties(_not(_or(STAP_ALLOW_NONCLIENT,STAP_ALLOW_CONTROLS,;
STAP_ALLOW_WEBCONTENT)))
|
To enable visual styles call it like this:
SetThemeAppProperties(_or(STAP_ALLOW_NONCLIENT,STAP_ALLOW_CONTROLS,;
STAP_ALLOW_WEBCONTENT))
|
The easiest thing to do is to call this at the start of the app
before any windows are used - this setting effects windows that are
created after the call.
The code for SetThemeAppProperties, and the DEFINEs, are below.
If you only want to turn off visual style for an individual
control you can use SetWindowTheme(). This function receives the
handle of the window/control, the name of the application, and a
string of class ids. To turn off the theme for the window the last
two parameters need to be empty Unicode strings. So to turn off the
theme for a tab control you would do something like this:
oUni := Unicode{""}
SetWindowTheme(SELF:oDCTabControl1:handle(),oUni:str,oUni:str)
|
The code for SetWindowTheme and Rod da Silva's Unicode class
below.
FUNCTION
SetThemeAppProperties(dwFlags AS
DWORD)
AS LOGIC
LOCAL
hModule AS PTR
LOCAL
hFunc AS PTR
LOCAL
lResult AS LOGIC
// Check if the
app if using the newer
// common
control for XP visual styles
IF
WinDLLVersion{"COMCTL32"}:major
>= 6
//
Find the theme DLL
hModule :=
LoadLibrary(PSZ("uxtheme.dll"))
IF
! hModule ==
NULL_PTR
//
Find the function we need
hFunc
:= GetProcAddress(hModule,PSZ("SetThemeAppProperties"))
IF
! hFunc ==
NULL_PTR
lResult
:= PCALL(hFunc,dwFlags)
== 0
ENDIF
ENDIF
ENDIF
RETURN
lResult |
DEFINE STAP_ALLOW_CONTROLS := 0x02
DEFINE STAP_ALLOW_NONCLIENT := 0x01
DEFINE STAP_ALLOW_WEBCONTENT := 0x04
|
FUNCTION SetWindowTheme(hWnd AS PTR, hAppName AS PTR, hIdList AS PTR) AS LONGINT
LOCAL hModule AS PTR
LOCAL hFunc AS PTR
LOCAL liReturn AS LONGINT
// Check if the app if using the newer
// common control for XP visual styles
IF WinDLLVersion{"COMCTL32"}:major >= 6
// Find the theme DLL
hModule := LoadLibrary(PSZ("uxtheme.dll"))
IF ! hModule == NULL_PTR
// Find the function we need
hFunc := GetProcAddress(hModule,PSZ("SetWindowTheme"))
IF ! hFunc == NULL_PTR
liReturn := LONG(_CAST,PCALL(hFunc,hWnd,hAppName,hIdList))
ENDIF
ENDIF
ENDIF
RETURN liReturn
|
CLASS Unicode
PROTECT pUnicodeBuffer AS PTR
PROTECT iSLen AS LONGINT
DECLARE ACCESS Str
DECLARE ACCESS SLen
METHOD Axit CLASS Unicode
MemFree( pUnicodeBuffer )
RETURN SELF
METHOD INIT( usAnsiStr ) CLASS Unicode
LOCAL iNumUnicodeChars AS INT
IF ! IsString( usAnsiStr )
usAnsiStr := NULL_STRING
ENDIF
// Allocate a buffer large enough for equivalent Unicode string
// At most twice as big (note: + 1 for trailing Nul terminator)
iNumUnicodeChars := 2 * (SLen( usAnsiStr ) + 1)
pUnicodeBuffer := MemAlloc( iNumUnicodeChars )
IF pUnicodeBuffer != NULL_PTR
MemClear ( pUnicodeBuffer, iNumUnicodeChars )
RegisterAxit( SELF ) // Register Axit to deallocate memory
// Convert usAnsiStr to Unicode
iSLen := MultiByteToWideChar( CP_ACP, 0, String2Psz(usAnsiStr), ;
-1, pUnicodeBuffer, iNumUnicodeChars )
ELSE // Memory allocation failed
//GenComError( ResultFromScode( ErrorCodes( ;
// FACILITY_WIN32, WIN32_E_OUTOFMEMORY ) ) )
ENDIF
RETURN SELF
ACCESS SLen AS LONGINT PASCAL CLASS Unicode
RETURN iSLen
ACCESS Str AS PTR PASCAL CLASS Unicode
RETURN pUnicodeBuffer
|
|