An app launcher meant to launch Microsoft Office 2010, 2013, 2016, 2019 desktop software/apps, including ones from Office 365. Not associated with Microsoft.
Apps using PortableThemeEngine can specify multiple things before having it apply a theme as detailed below:
Property ThemeEngine.UseSafeColorValidation(): Boolean; defaults to True. Determines whether fast or safe color validation is performed when assigning theme colors.
Fast color validation: UseSafeColorValidation() = False. Works like TE1.x where invalid colors rely on a Try...Catch block to be set to the default in case they're invalid. This tends to be really fast (often around 10 ms [depending on the amount of forms to theme] or less to re-apply) if the colors are valid, but it can be a lot slower than safe color validation if a color or multiple colors are invalid (up to 100 ms or more).
Safe color validation: UseSafeColorValidation() = True. Default in TE2.x/PortableThemeEngine. Checks whether the color is a valid HTML color, and if it's not a valid HTML color, then it checks if it's in the systems colors list, before assigning the theme colors. If it's neither a valid HTML color nor a valid system color, the default color will be used instead. Tends to be about the same speed whether a color is valid or not (often around 40 ms depending on the number of forms to apply it to, which is only about 10 ms slower than the original theme applying code in TE1.x).
Description from source:
Click to show
' ' libportablethemeengine.vb:
'
' Safe color validation uses regex to make sure the color
' that's being applied to a given control is a valid HTML color.
' If it's not a valid HTML color, it's looked up in the system colors
' list. Disabling safe color validation enables fast color validation,
' which relies on an exception handler to make sure colors are valid like TE1.x.
' See more details in TE2DotXLoader.GetThemeColor.
'
' ' TE2DotXLoader.vb:
' ' GetThemeColor() function
' ' When safe color validation is enabled:
'
' If the calling application wants to use safe color
' validation, then we'll use it.
' During testing, it seemed to generally be around
' 20-40 ms to re-apply a theme, and somewhere between
' 60-90 ms (rarely as high as 140 ms or more) to apply
' a theme on startup. These numbers are based on
' UXL Launcher version 3.3, and it was temporarily
' redirected to use PortableThemeEngine instead
' of the built-in one. These numbers can vary
' depending on the theme. For example, the Default
' theme generally seemed to be around 20-30 ms to re-apply.
'
' ' If the color in the theme isn't "LiteralNothing" and the
' ' color is valid whether as an HTML color or as a system color:
'
' If the color is a valid HTML or system color
' and it's not LiteralNothing, return the color.
' One situation where this could be confusing is
' if the theme has something like #3 instead of
' #363636. In this case, a color with alpha
' transparency will be returned, and it might be
' confusing. May be a good idea to make sure it has
' a length of 3 numbers or 6 numbers.
'
' ' When it's disabled:
'
' If the calling application wants to use fast
' color validation, rely on a try...catch block.
' Generally fast color validation is around 10 ms to
' re-apply a theme, and about 40 ms to apply a theme
' for the first time. These numbers are from testing
' PortableThemeEngine with UXL Launcher version 3.3.
' Themes with colors that are invalid (such as ones
' using "LiteralNothing" like the Default theme)
' will take a lot longer to re-apply, sometimes upwards
' of 60 ms or more. This is due to running into the
' exception handler.
' Themes that run into the exception handler can take
' over 100 ms to apply on startup, sometimes taking more
' than 140 ms.
Property ThemeEngine.UseFullTE1DotXCompatibilityMode(): Boolean; defaults to False. Determines whether theme files that say they support TE1.x/UXL Launcher Theme Engine (or don't say what they support, if the UseThemeEngineVersion node is missing) will be applied in a way that UXL Launcher Theme Engine would expect. For example, if the theme file doesn't support TE1.03 or greater, theme files won't be applied to forms other than ones named aaformMainWindow. Please don't set this to True or unexpected problems may occur if your application isn't UXL Launcher.
Description from source:
' Enabling TE1.x full compatibility mode causes forms not named "aaformMainWindow"
' to not be themed if the theme file doesn't support TE1.03 or greater.
' Default colors will be applied to forms of other names.
' Other TE1.x-related features that UXL Launcher relied on will also be enabled,
' but as of April 14, 2020, this is the only one.
' This is to allow TE2.x to eventually replace TE1.x in UXL Launcher.
' Please don't enable this unless you absolutely have to.
' The default is loose compatibility mode.
' Loose compatibility mode will have theme colors
' applied to any forms passed into the TE1.x shim and is what's
' recommended for most applications.
Property ThemeEngine.ShowThemeEngineDebuggingOutput(): Boolean; defaults to False. Determines whether theme engine debugging information is written to the Visual Studio Immediate Window or another debugger output area. This often contains things like theme file information and theme engine error handler output. May be useful for theme designers to set this to True to see if there are any issues with the theme file during debugging.
Property ThemeEngine.AllowCustomThemes(): Boolean; defaults to True. Determines whether the theme engine is allowed to load custom themes from the disk. May be useful for companies that want to have specific themes be used and block loading external themes.
Property ThemeEngine.MatchWindows10ThemeSettings(): Boolean; defaults to False. Determines whether the theme engine should match the Windows 10 light/dark theme settings as set in the Settings app for default app mode if the calling app uses the ThemeEngine.SelectTheme() sub.
If set to True: If the user wants to use the light theme for apps, the Default theme will be applied. If the user wants to use the dark theme for apps, the TenDark theme will be applied. If the Registry key value this feature uses is missing, the Default theme will be applied.
If set to False: Standard theme selection will occur if the calling app uses ThemeEngine.SelectTheme(). Using the ThemeEngine.LoadThemeFromXML() sub will bypass both this theme selection procedure and the Windows 10 theme-matching property.
Switching between light and dark themes when the user changes the default app mode is not automatic; the calling app has to watch the Registry key value itself.
Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme is the Registry key value that's being looked at.
There's now an application called PortableThemeEngineDebugger which can be used to test the theme engine with various settings. It allows pasting in a path to a theme file and specifying if it's a custom theme, as well as if custom themes are allowed. If the theme in the theme path textbox is specified to be a custom theme, it'll apply as a custom theme if the checkbox to allow custom themes is checked. If the checkbox to allow custom themes is unchecked, a message will be written in the Immediate Window and the Default theme will be applied instead. This app also allows builtin themes to be applied if the text in the theme path textbox matches one of the theme files in PortableThemeEngine, either in the format of "NameTheme" or "Name". One example is "TenDarkTheme" or "TenDark".
Applications using PortableThemeEngine can have ContextMenuStrip components themed to match the ToolStripMenuItem controls by passing the form's components container along to the theme engine as described below:
Directly loading an XML document and applying it to a form and its components (which is what the ContextMenuStrip is a part of): ThemeEngine.LoadThemeFromXML(My.Resources.ReturnOfNightTheme_XML, Me, Me.components)
Selecting a builtin theme: ThemeEngine.SelectTheme("TenDark", Me, Me.components)
Using a custom theme (using a custom theme is specified by passing "(Custom)" as the theme name; sometimes there are cases outside theme engines that someone might want to do custom things instead of what society forces everyone to be, forced to choose between one option or the other): ThemeEngine.SelectTheme("(Custom)", Me, Me.components, "C:\theme.xml")
Please note that the Me.components container is private to each form's class by default, so it may be necessary to place a shared sub/function in each form's class you're going to have context menus on, then call each of the subs/functions from one place in another class or wherever you want to call them from.
Example code of how to do this a somewhat-easy way:
' To allow theming components on multiple forms, something like this
' would need to be done:
'
' Inside sub UXL_Launcher.aaformMainWindow.Load:
' Private Sub Me.Load()
' ...
' UXLLauncher_ThemeEngine.RunThemeEngine(Me, Me.components)
' forceOptionsWindowTab.RunThemeEngine()
' ...
' End Sub
'
' Inside the Options window load sub:
' Friend Sub RunThemeEngine()
' UXLLauncher_ThemeEngine.RunThemeEngine(Me, Me.components)
' End Sub
'
' Inside the UXLLauncher_ThemeEngine class:
' Friend Shared Sub RunThemeEngine(formToApplyTo As Form, componentsToApplyTo As ComponentModel.IContainer)
' libportablethemeengine.themeenginemain.LoadTheme("C:\Users\drewn\Documents\0GitHub\UXL-Launcher\UXL-Launcher\VB Code-behind\Themes\TE2DotX_TenDarkTheme_XML.xml", formToApplyTo, componentsToApplyTo)
' End Sub
'
Context menus that are a part of things like TextBoxes don't get themed since they're separate from the ContextMenuStrip component.
Support for theming panels has been added to PortableThemeEngine, just like how it's supported in UXL Launcher Theme Engine.
Default statusbar and statuslabel properties are applied to forms that aren't named aaformMainWindow if the theme file doesn't support TE1.03. Also applies to PortableThemeEngine if TE1.x full compatibility mode is enabled.
There's now an output message for System.ArgumentException exceptions when running the theme engine.
In TE2.x, controls are checked against a list to make sure that the theme engine version they were first supported in is the same as, or older than, the version the theme file supports. This is not being backported to TE1.x as it's a lot of work.
Changed:
In TE2.x, properties are used instead of local variables when assigning theme colors and loading in the theme files except for when getting theme info, since that could mess with the currently-loaded theme. This change is not being backported to TE1.x.
Instead of saying that a theme has malformed XML, now it says that a theme has invalid XML in case the theme engine runs into a System.Xml.XmlException exception.
TE2.x uses XML attributes instead of node InnerText for most of the properties. See TE2DotX_TenDarkTheme_XML.xml for an example of what properties are using XML attributes in TE2.x. Any missing attributes will use the default theme's value. TE1.x themes won't use XML attributes.
themeenginemain.userTheme variable is now ThemeProperties.themeFileNameToLoad in TE2.x.
themeenginemain class is now ThemeEngine in TE2.x.
In TE2.x, the theme loading code is now separate from the theme applying code, which is related to using properties for theme colors.
Several parts of the TE2.x theme loader uses Select Case statements instead of If...Else statements to reduce the number of times the XML document is read.
MenuStrip controls that are in forms passed into TE2.x now have their renderer set to the one used by PortableThemeEngine automatically. This doesn't yet include toolbars, so for now, toolbars will have to have their renderer set manually in the calling app by something like toolbar.Renderer = libportablethemeengine.ThemeProperties.toolstripProRenderer before calling the theme engine. This is not officially supported, and things might crash if there's anything besides buttons on the toolbar (as that's the only kind of toolbar control that's been tested).
Assigning colors to the toolstrip pro renderer is done outside the part where colors are applied to controls in TE2.x.
Various comments and debugging output stuff have had their text changed in TE2.x to reflect differences between TE2.x and TE1.x.
Some subs have been updated to be easier to use in TE2.x, mainly the ones in the ThemeEngine class. A lot of those subs have also been made Private or Friend as well, since they're not useful outside the theme engine.
Button FlatStyle BorderColors are now checked in TE1.03 and TE2.x to make sure they're not transparent before trying to apply them. If they are, then the default color will be applied depending on what version of the theme engine the file supports. This may speed up applying themes slightly. In case there's an issue when applying the color, there's still a Try...Catch block to handle it.
TE2.x now uses the Version class instead of Decimals for figuring out the theme engine version that the theme wants to use. This has the added effect of TE1.x-style version numbers being converted to TE2.x-style version numbers, where the leading zero gets removed like so: 1.03 --> 1.3. These versions are effectively identical. This will not be backported due to it being a lot of work to change.
Fixed:
Strings in the UseThemeEngineVersion node in the example format of "1.03..34" no longer crash UXL Launcher when a theme with that kind of a string is selected in the Options window as of TE1.03. This change also exists in TE2.x's GetThemeFileInfo function for any applications using PortableThemeEngine. Missed this when fixing #126.
TextBox BackColors are now set to the default value in case a transparent color is specified in the theme file. This change applies to both TE1.03 and TE2.x.
There are many changes to the theme engine in this pr, and I'm really glad this is finally ready to be merged.
Closes #172, closes #171, closes #170, closes #168, closes #167, and closes #164.
Added:
Apps using PortableThemeEngine can specify multiple things before having it apply a theme as detailed below:
ThemeEngine.UseSafeColorValidation()
: Boolean; defaults toTrue
. Determines whether fast or safe color validation is performed when assigning theme colors.UseSafeColorValidation() = False
. Works like TE1.x where invalid colors rely on a Try...Catch block to be set to the default in case they're invalid. This tends to be really fast (often around 10 ms [depending on the amount of forms to theme] or less to re-apply) if the colors are valid, but it can be a lot slower than safe color validation if a color or multiple colors are invalid (up to 100 ms or more).UseSafeColorValidation() = True
. Default in TE2.x/PortableThemeEngine. Checks whether the color is a valid HTML color, and if it's not a valid HTML color, then it checks if it's in the systems colors list, before assigning the theme colors. If it's neither a valid HTML color nor a valid system color, the default color will be used instead. Tends to be about the same speed whether a color is valid or not (often around 40 ms depending on the number of forms to apply it to, which is only about 10 ms slower than the original theme applying code in TE1.x).Click to show
ThemeEngine.UseFullTE1DotXCompatibilityMode()
: Boolean; defaults toFalse
. Determines whether theme files that say they support TE1.x/UXL Launcher Theme Engine (or don't say what they support, if theUseThemeEngineVersion
node is missing) will be applied in a way that UXL Launcher Theme Engine would expect. For example, if the theme file doesn't support TE1.03 or greater, theme files won't be applied to forms other than ones namedaaformMainWindow
. Please don't set this toTrue
or unexpected problems may occur if your application isn't UXL Launcher.ThemeEngine.ShowThemeEngineDebuggingOutput()
: Boolean; defaults toFalse
. Determines whether theme engine debugging information is written to the Visual Studio Immediate Window or another debugger output area. This often contains things like theme file information and theme engine error handler output. May be useful for theme designers to set this toTrue
to see if there are any issues with the theme file during debugging.ThemeEngine.AllowCustomThemes()
: Boolean; defaults toTrue
. Determines whether the theme engine is allowed to load custom themes from the disk. May be useful for companies that want to have specific themes be used and block loading external themes.ThemeEngine.MatchWindows10ThemeSettings()
: Boolean; defaults toFalse
. Determines whether the theme engine should match the Windows 10 light/dark theme settings as set in the Settings app for default app mode if the calling app uses theThemeEngine.SelectTheme()
sub.True
: If the user wants to use the light theme for apps, the Default theme will be applied. If the user wants to use the dark theme for apps, the TenDark theme will be applied. If the Registry key value this feature uses is missing, the Default theme will be applied.False
: Standard theme selection will occur if the calling app usesThemeEngine.SelectTheme()
. Using theThemeEngine.LoadThemeFromXML()
sub will bypass both this theme selection procedure and the Windows 10 theme-matching property.Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme
is the Registry key value that's being looked at.PortableThemeEngineDebugger
which can be used to test the theme engine with various settings. It allows pasting in a path to a theme file and specifying if it's a custom theme, as well as if custom themes are allowed. If the theme in the theme path textbox is specified to be a custom theme, it'll apply as a custom theme if the checkbox to allow custom themes is checked. If the checkbox to allow custom themes is unchecked, a message will be written in the Immediate Window and the Default theme will be applied instead. This app also allows builtin themes to be applied if the text in the theme path textbox matches one of the theme files in PortableThemeEngine, either in the format of "NameTheme" or "Name". One example is "TenDarkTheme" or "TenDark".ContextMenuStrip
components themed to match theToolStripMenuItem
controls by passing the form'scomponents
container along to the theme engine as described below:ThemeEngine.LoadThemeFromXML(My.Resources.ReturnOfNightTheme_XML, Me, Me.components)
ThemeEngine.SelectTheme("TenDark", Me, Me.components)
ThemeEngine.SelectTheme("(Custom)", Me, Me.components, "C:\theme.xml")
Me.components
container is private to each form's class by default, so it may be necessary to place a shared sub/function in each form's class you're going to have context menus on, then call each of the subs/functions from one place in another class or wherever you want to call them from.TextBox
es don't get themed since they're separate from theContextMenuStrip
component.aaformMainWindow
if the theme file doesn't support TE1.03. Also applies to PortableThemeEngine if TE1.x full compatibility mode is enabled.System.ArgumentException
exceptions when running the theme engine.Changed:
System.Xml.XmlException
exception.TE2DotX_TenDarkTheme_XML.xml
for an example of what properties are using XML attributes in TE2.x. Any missing attributes will use the default theme's value. TE1.x themes won't use XML attributes.themeenginemain.userTheme
variable is nowThemeProperties.themeFileNameToLoad
in TE2.x.themeenginemain
class is nowThemeEngine
in TE2.x.Select Case
statements instead ofIf...Else
statements to reduce the number of times the XML document is read.MenuStrip
controls that are in forms passed into TE2.x now have their renderer set to the one used by PortableThemeEngine automatically. This doesn't yet include toolbars, so for now, toolbars will have to have their renderer set manually in the calling app by something liketoolbar.Renderer = libportablethemeengine.ThemeProperties.toolstripProRenderer
before calling the theme engine. This is not officially supported, and things might crash if there's anything besides buttons on the toolbar (as that's the only kind of toolbar control that's been tested).ThemeEngine
class. A lot of those subs have also been madePrivate
orFriend
as well, since they're not useful outside the theme engine.Button FlatStyle BorderColor
s are now checked in TE1.03 and TE2.x to make sure they're not transparent before trying to apply them. If they are, then the default color will be applied depending on what version of the theme engine the file supports. This may speed up applying themes slightly. In case there's an issue when applying the color, there's still aTry...Catch
block to handle it.Version
class instead ofDecimal
s for figuring out the theme engine version that the theme wants to use. This has the added effect of TE1.x-style version numbers being converted to TE2.x-style version numbers, where the leading zero gets removed like so:1.03 --> 1.3
. These versions are effectively identical. This will not be backported due to it being a lot of work to change.Fixed:
UseThemeEngineVersion
node in the example format of "1.03..34" no longer crash UXL Launcher when a theme with that kind of a string is selected in the Options window as of TE1.03. This change also exists in TE2.x'sGetThemeFileInfo
function for any applications using PortableThemeEngine. Missed this when fixing #126.TextBox BackColor
s are now set to the default value in case a transparent color is specified in the theme file. This change applies to both TE1.03 and TE2.x.