Welcome to AddressOf.com Sign in | Join | Help

Windows XP Visual Styles (Themes) [How To / RANT]

Everyone who knows me will usually accuses me as being part of the 'collective'.  They would have you believe that I think Microsoft can do no wrong.  Well, here's my first official rant about how Microsoft can do something very, very, very wrong.

OK, great, they added EnableVisualStyles to .NET 1.1.  Cool.  But it has a couple of bugs (imagelists come to mind).  Fine, I can work around those.  Then comes the fact that visual styles take effect on the tab control... but wait, it doesn't paint the inner portion of the tab with the slight gradient that you would expect.  There's no built in way to correct this; unless you consider that you can sort of fake it by just setting the backcolor of the tab area to white.  That's not too bad, but now for the problem.  What if someone turns off themes?

I can use IsThemeActive from uxtheme.dll that would tell me whether or not visual styles is enabled.  But prior to that, I need to test that the OS version is greater than 5.1 and do a LoadLibrary to test that the uxtheme.dll exists.  Not a problem; pretty simple really.  So great, I can determine if Windows is themed.  But, what if I didn't use EnableVisualStyles()?  My application is still using the previous common control library.  Then comes in the IsAppThemed() method in uxtheme.dll.  Sounds like it's the one I want... but it appears to always return true when testing under WindowsXP that has themes enabled and no EnableVisualStyles() being used.  If the application is not using visual styles, why does this function return true? Grrrr.

So what exactly does EnableVisualStyles do?  Is there a way to determine from what it does whether visual styles are enabled for the running application?  Digging out Reflector and looking at the IL for EnableVisualStyles, it pretty much translates to the following VB.NET code:

Public Shared Sub EnableVisualStyles()
  useVisualStyles = True
End Sub

Further inspection finds that useVisualStyles is a private variable.  I'm guessing that the other methods use this private shared variable to then react accordingly when drawing the controls.

So Microsoft kinda forgot to implement the correct drawing behavior of the tab control.  I can forgive that.  But WHY is there no way to see if your application has called upon EnableVisualStyles?  How hard would it have been to make a read-only property that you could look at?  Why is this a problem.  Well, what if you make a reusable control that you want to have drawn differently depending on whether or not visual styles was enabled.  This control has no way of knowing whether EnableVisualStyles was called upon.  Grrr... sometimes developers (yes, even Microsoft) can be so short sighted.

As it stands, I still can't seem to find a solution for this.  As it stands, here's a fragment that will do most of the work.  Of course, you still have to know yourself if EnableVisualStyles() (or the .manifest file exists); knowing this as a limitation, here's the code:

Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal path As String) As IntPtr
Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal library As IntPtr, ByVal procName As String) As IntPtr
Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal library As IntPtr) As
Boolean
Private Declare Function IsThemeActive Lib "uxtheme.dll" () As
Boolean
Private Declare Function IsAppThemed Lib "uxtheme.dll" () As
Boolean

Private Function XPThemesEnabled() As
Boolean
 
Dim os As OperatingSystem = System.Environment.OSVersion
  If os.Platform = PlatformID.Win32NT _
    AndAlso (((os.Version.Major = 5) AndAlso (os.Version.Minor >= 1)) _
    OrElse (os.Version.Major > 5))
Then

    Dim
uxTheme As IntPtr = LoadLibrary("uxtheme.dll")
    If Not uxTheme.Equals(IntPtr.Zero)
Then
     
Dim handle As IntPtr = GetProcAddress(uxTheme, "IsThemeActive")
      If handle.Equals(IntPtr.Zero)
Then
       
' an error occurred, use GetLastError
      
Else
        
If IsThemeActive()
Then
         
Return IsAppThemed
        End
If
      
End
If
    
Else
     
' an error occurred, use GetLastError
   
End
If
   
FreeLibrary(uxTheme)
  End
If

 
Return
False

End
Function
Published Saturday, February 14, 2004 1:26 AM by CorySmith
Filed under: , ,

Comments

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Sunday, February 15, 2004 1:51 AM by milbertus
There was a post a while back on the DevMentor that describes what EnableVisualStyles() is doing behind the scenes. It is available at http://discuss.develop.com/archives/wa.exe?A2=ind0402a&L=dotnet-winforms&T=0&F=&S=&P=7092.

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Sunday, February 15, 2004 3:44 AM by Lonnie McCullough
IsAppThemed simply tells you whether or not the user has turned themes off for your application by running it in compatibility mode (implemented through the "Compatibilty" shell property page). It has nothing to do with EnableVisualStyles and knows nothing about that method which I would think just does an internal redirect to commctl v6 (though I don't really know) when it finally gets loaded.

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Sunday, February 15, 2004 3:49 AM by Lonnie McCullough
Hmmm...just a little more info: IsAppThemed will always return true even if commctl v6 is not loaded in the process because it doesn't matter to uxtheme if commctl is using its services or not...it still thinks that if the app wants to theme then it is ok to do so.

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Sunday, February 15, 2004 9:30 AM by Raghavendra Prabhu
Check out my blog posts on Visual Styles that answer some of your questions and also outline our (WinForms team) plans for expanding visual styles support in Whidbey: http://blogs.msdn.com/rprabhu/category/2838.aspx

Yes, I agree it is rather inconvenient that there isn't a property available that tells you whether comctl 6 is being used for rendering or not. We are ofcourse fixing this in Whidbey, but for now, you could p/invoke DllGetVersion to see what the version of comctl your app is linking to. That, along with IsAppThemed and IsThemeActive should help you figure out whether to render with themes.

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Sunday, February 15, 2004 4:54 PM by Cory Smith
Lonnie:

The v6 appears to be loaded since part of the windows app is using it (the windows border portion); so this can explain why it's always true if you are using visual styles in Windows XP.

Raghavendra:

Thanks for the information and the suggestion. I've posted a new comment on this with updated code for check to see if visual styles is in use.

# [How To] Check to see if Visual Styles are enabled

Sunday, February 15, 2004 4:54 PM by AddressOf.com

# How to determine if visual styles are enabled?

Sunday, February 15, 2004 9:40 PM by Cool Client Stuff

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Sunday, February 15, 2004 9:50 PM by phil
One thing that you <b>gotta</b> do is enable visual styles before creating ANY forms. Create a main entry point rather than a default form for your project.

System.Windows.Forms.Application.EnableVisualStyles()
' This is here to workaround known bug with VisualStyles.
' See http://blogs.gotdotnet.com/rprabhu/PermaLink.aspx/6b1a5495-c790-4e2f-972f-afb01f0164cf
' for more information about the root cause and why this fixes things...
' If you don't call DoEvents, Buttons and Tabs render properly but anything that
' has an ImageList does not. Given that the App's core images are actually all
' in PowerTOolManager and shared among various forms ... we've gotta do the DoEvents
' call. Apparently this isn't necessary in W3K servers.
System.Windows.Forms.Application.DoEvents()
System.Windows.Forms.Application.Run(New MainForm)

# re: Windows XP Visual Style does not work anymore

Saturday, October 23, 2004 6:08 PM by Srinivas Yelamanchili
Hi, After using Application.EnableVisualStyles in the Main() i could get visual theme to my form on compilation (i am not talking about setup project).
All the buttons and controls are set to 'system' properly.
After working on the code for few days, i noticed that my form doesn't anymore show any visual theme at all. The 'system' properly is set as it is and EnableVisualStyles code is in the original place.
I now also added Apllication.DoEvents().
Still, when i compile the code, the form doesn't show any XP visual theme. My XP theme is not changed. Other applications still show visual themes on their forms.

Can u tell me where could i have messed up in the code?
Thanks.

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Thursday, February 17, 2005 8:19 AM by horoscope
can i ask u a question about your blog themes

# re: Windows XP Visual Styles (Themes) [How To / RANT]

Thursday, March 10, 2005 12:29 AM by Kyle
I think somethings happening here that you didn't intend to happen.

Return IsAppThemed

Exits the procedure. The problem here is FreeLibrary(...) isn't being invoked in such a case. Meaning, once called if the os is XP+ (NT 5.1) and the theme dll was found then it doesn't ever call FreeLibary.

-Kyle

Anonymous comments are disabled