Clone our Wealth-Lab 8 Extension Demo project on GitHub to get a head start in developing your own Extensions!

Theming in WL8

The version 8 major release of WealthLab introduced theming, allowing you to run the software in either a Light or a Dark mode. WealthLab's UI platform, Windows WPF, supports theming up to a point, but we needed to add several custom components and integration points so theming could work smoothly.

It's worth noting that even though WL8 ships with only 2 Themes, Light and Dark, the framework supports the ability for us to add additional Themes in the future if demand warrants.

Theme Resources for Windows

The two Themes are defined as XAML files in WealthLab8, LightTheme.xaml and DarkTheme.xaml, in the Themes folder. When you build WL8 UI extensions, particularly new Windows, you should use the resources defined in these Theme files to specific colors. You'll use DynamicResource bindings to return various elements from the currently selected Theme.

For example, if you want your Window to use the standard "brown" background color that the Themes define, use the following in your Window's XAML:

Background="{DynamicResource BrownBackground}"

A list of the Theme-defined colors you can draw upon is presented below, drawn from the LightTheme.xaml file. Some notes on how WL8 uses some of these colors:

  • The standard text color is ControlDefaultForeground.
  • Toolbars use the ContainerBackground as background color.
   <!--Window Colors. Background, Border and TitleBar colours.-->
    <SolidColorBrush x:Key="BackgroundColour"  Color="#FFF8F8F8" />
    <SolidColorBrush x:Key="WindowBorderColour"  Color="#FFE0E0E0" />
    <SolidColorBrush x:Key="WindowTitleColour"  Color="WhiteSmoke" />
    
    <!--WL8 colors-->
    <SolidColorBrush x:Key="MDITitleBackground" Color="LightSteelBlue"/>
    <SolidColorBrush x:Key="SelectorBackground" Color="DarkSlateGray"/>
    <SolidColorBrush x:Key="BuildingBlock" Color="#f9e8d2"/>
    <SolidColorBrush x:Key="BuildingBlockBuy" Color="#cce6ff"/>
    <SolidColorBrush x:Key="BuildingBlockSell" Color="#ffcccc"/>
    <SolidColorBrush x:Key="BuildingBlockShort" Color="#ccffcc"/>
    <SolidColorBrush x:Key="BuildingBlockCover" Color="#ffe6cc"/>
    <SolidColorBrush x:Key="GreenButtonBackground" Color="PaleGreen"/>
    <SolidColorBrush x:Key="GreenButtonBorder" Color="#132013"/>
    <SolidColorBrush x:Key="RedButtonBackground" Color="MistyRose"/>

    <!--various panel background colors-->
    <SolidColorBrush x:Key="GreenBackground" Color="#dfecdf"/>
    <SolidColorBrush x:Key="RedBackground" Color="#eddede"/>
    <SolidColorBrush x:Key="YellowBackground" Color="#f1ecda"/>
    <SolidColorBrush x:Key="BlueBackground" Color="#d6edf5"/>
    <SolidColorBrush x:Key="ConstructorBackground" Color="LightYellow"/>
    <SolidColorBrush x:Key="OrangeBackground" Color="#ffe6cc"/>
    <SolidColorBrush x:Key="VioletBackground" Color="#e6ddee"/>
    <SolidColorBrush x:Key="BrownBackground" Color="#ecdfdf"/>
    <SolidColorBrush x:Key="BlueGrayBackground" Color="#e0e0eb"/>
    <SolidColorBrush x:Key="WealthSignalsBackground" Color="#e4e6e7"/>
    
    <!--foreground text colors-->
    <SolidColorBrush x:Key="RedText" Color="Red"/>
    <SolidColorBrush x:Key="GreenText" Color="Green"/>
    <SolidColorBrush x:Key="BlueText" Color="Blue"/>

    <!--Building Block colors-->
    <SolidColorBrush x:Key="BBBackground" Color="WhiteSmoke"/>
    <SolidColorBrush x:Key="BBLongEntry" Color="#d4d4f7"/>
    <SolidColorBrush x:Key="BBLongExit" Color="MistyRose"/>
    <SolidColorBrush x:Key="BBShortEntry" Color="#d4f7e0"/>
    <SolidColorBrush x:Key="BBShortExit" Color="#f7e9d4"/>
    <SolidColorBrush x:Key="BBCondition" Color="#f7f5ed"/>
    <SolidColorBrush x:Key="BBQualifier" Color="#ebe6d3"/>

    <!-- Standard UI element colors -->
    <SolidColorBrush x:Key="TransparentBrush" Color="Transparent"/>
    <SolidColorBrush x:Key="ContainerBackground" Color="WhiteSmoke"/>
    <SolidColorBrush x:Key="ContainerBorder" Color="#FFE8E8E8"/>
    <SolidColorBrush x:Key="ControlDefaultForeground"  Color="#FF040404" />
    <SolidColorBrush x:Key="ControlMOSelectForeground"  Color="#FF040404" />

    <SolidColorBrush x:Key="ControlDarkerBackground"             Color="#FFC7C7C7"/>
    <SolidColorBrush x:Key="ControlDarkerBorderBrush"            Color="#FFBEBEBE"/>
    <SolidColorBrush x:Key="ControlDefaultBackground"            Color="#FFE1E1E1"/>
    <SolidColorBrush x:Key="ControlDefaultBorderBrush"           Color="#FFAFAFAF"/>
    <SolidColorBrush x:Key="ControlBrightDefaultBackground"      Color="#FFCDCDCD"/>
    <SolidColorBrush x:Key="ControlBrightDefaultBorderBrush"     Color="#FFAFAFAF"/>
    <SolidColorBrush x:Key="ControlDisabledBackground"           Color="#FFB4B4B4"/>
    <SolidColorBrush x:Key="ControlDisabledBorderBrush"          Color="#FF8C8C8C"/>
    <SolidColorBrush x:Key="ControlMouseOverBackground"          Color="#FFD2D2D2"/>
    <SolidColorBrush x:Key="ControlMouseOverBorderBrush"         Color="#FFBEBEBE"/>
    <SolidColorBrush x:Key="ControlSelectedBackground"           Color="#FFCCEBFF"/>
    <SolidColorBrush x:Key="ControlSelectedBorderBrush"          Color="#FFCCCCCC"/>
    <SolidColorBrush x:Key="ControlSelectedMouseOverBackground"  Color="Gainsboro"/>
    <SolidColorBrush x:Key="ControlSelectedMouseOverBorderBrush" Color="#FFB9B9B9"/>
    <SolidColorBrush x:Key="ControlGlythColour"                  Color="#FF0A0A0A"/>
    <SolidColorBrush x:Key="ControlMouseOverGlythColour"         Color="#FF212121"/>
    <SolidColorBrush x:Key="ControlSelectedGlythColour"          Color="#FF191919"/>
    <SolidColorBrush x:Key="ControlDisabledGlythColour"          Color="#FF666666"/>

Determining the Theme

If you ever need to determine if the current Theme is Light or Dark in code, you can use the IsDarkTheme property of the IWLClientHost interface, defined in WealthLab.WPF. Each main Window in WL8 is backed by a IWLClientHost instance, which can be accessed using the WLClientHost.Instance static property.

You can also "subscribe" to notifications of Theme changes using the EventRouter class in WealthLab.Core. Subscribe to the event named "ThemeChanged." The event parameter is a bool which indicates whether the new Theme is Dark.

You can determine if the current Theme is Dark in XAML using the IsDark DynamicResource that is also defined in the Theme files. IsDark is defined as a System.Boolean.

IsDark XAML Example

Below a WLImage (see below) is used in a DataTemplate for the ListView item. It uses the IsDark property to reverse the image if the currently selected Theme is Dark.

<GridViewColumn.CellTemplate>
	<DataTemplate>
	    <StackPanel Orientation="Horizontal">
	        <wpf:WLImage Width="14" Height="14" Margin="0,1,2,0" BaseSource="../Images/NSF.png" IsReversed="{DynamicResource IsDark}" Visibility="{Binding NSF, Converter={StaticResource boolVis}}" ToolTip="NSF Position"/>
	        <Image Width="14" Height="14" Margin="0,0,4,0" Source="{Binding PositionType, Converter={StaticResource PosTypeToImage}}"/>
	        <TextBlock Text="{Binding PositionType}" Foreground="{Binding Profit, Converter={StaticResource ProfitBrush}}"/>
	    </StackPanel>
	</DataTemplate>
</GridViewColumn.CellTemplate>

Reversible Icon Images in WL8 Framework Objects

The Configurable class is used throughout WL8 as a base class for numerous extensions, such as data providers and brokers. Configurable has a GlyphResource property that returns the glyph icon used to represent an item in the WL8 user interface.

By default, WL8 will reverse the image in the UI when a Dark Theme is selected. This isn't always preferable, and if you want to disable this in a class derived from Configurable, override DisableGlyphReverse and return true.

WLImage

The WLImage class in WealthLab.WPF is derived from the WPF Image and adds a few properties related to theming.

public ImageSource BaseSource

Returns the ImageSource of this Image, and automatically returns a reversed image version if the currently selected Theme is Dark.

public bool DisableReverse 

Disables the automatic reversing of ImageSource for Dark Theme.

public bool IsReversed

Determines or changes whether the ImageSource is currently in the reversed state.

WLButton

The WLButton class defined in WealthLab.WPF is derived from the WPF Button class, and adds some properties to support theming.

public WLButtonDisplayState DisplayState

Determines the button's display state. Possible values are DisplayState.Text, DisplayState.Image, or DisplayState.ImageAndText.

public string Text

The text of the button, visible if DisplayState is set to DisplayState.Text or DisplayState.ImageAndText.

public ImageSource Image 

The image of the button, displayed if DisplayState is DisplayState.Image or DisplayState.ImageAndText. Behind the scenes, WLButton uses the WLImage control to back the image.

public bool DisableReverse

Set to true to disable to automatic reversal of the button's Image in Dark Themes.

Theming Window Classes

To create a custom Window for WL8, use the DialogWindow class defined in WealthLab.WPF instead of the basic Window class. DialogWindow contains logic that supports WL8 theming.

You should set the Style of the DialogWindow in XAML as such:

Style="{DynamicResource CustomToolWindowStyle}"

Set the background color of the Window in XAML using the DynamicResource method described above.

Automatically sizing a Window based on its contents is normally done using the Window SizeToContent property. For DialogWindow derived Windows, use the new DesignSizeToContent property instead. This property overcomes a glitch in WPF that occurs when a custom Window style is applied and SizeToContent is also specified.

Context Menus

Context menus posed a challenge with theming in WL8, so we developed the WLContextMenu class in WealthLab.WPF to overcome this. Simply use the WLContextMenu instead of the WPF ContextMenu in your UI to ensure the context menu behaves well with theming.

Dynamically Loaded UI

If you create any UI elements dynamically you may need to "refresh" the UI so the current Theme can be applied to the new elements. IWLClientHost provides the RefreshTheme method for this purpose:

void RefreshTheme(DependencyObject dobj)

Call RefreshTheme to ensure that the UI element specified in dobj (which can be the base DialogWindow itself) and all of its UI descendants have the correct Theme applied.

In the example below, NeuroLab calls RefreshTheme from one of its ChildWindow derived classes to ensure that the current Theme is applied once the Neural Network architecture has changed. Note that MyClientHost is a property of ChildWindow, and returns the IWLClientHost instance that is associated with the ChildWindow's main WL8 Window.

MyClientHost.RefreshTheme(this);