Theming in WealthLab Extensions
WealthLab 9 supports application-wide theming, including Light and Dark modes. Because WealthLab's desktop interface is built with WPF, extensions can participate in the active Theme by using the Theme resources and controls supplied by WealthLab.WPF. The WealthLab theming framework is designed so that additional Themes can be supported in the future.
Accessing the Host (WL9) Environment
The IHost interface provides access to the current WealthLab environment. Extensions can use it to retrieve application-level information and services, such as the location of the user's WealthLab data folder or the DataSets defined by the user.
You can access the current IHost instance from anywhere in your extension through the WLHost singleton and its Instance property. For example, the following code retrieves the path to the user's WealthLab data folder:
string folder = WLHost.Instance.DataFolder;
Use WLHost.Instance whenever your extension needs access to functionality exposed by the IHost interface.
Theme Resources
WealthLab defines Theme-specific resources in XAML resource dictionaries. When building WPF extensions, especially custom Windows and UserControls, use these Theme resources instead of hard-coding colors wherever practical. Reference Theme resources using WPF DynamicResource bindings so the UI automatically reflects the currently selected Theme. For example:
Background="{DynamicResource BrownBackground}"
When the user changes Themes, the resource resolves to the value defined by the newly selected Theme.
Common Theme Resources
The following resources are available for use by WealthLab extensions.
Window Colors
<SolidColorBrush x:Key="BackgroundColour" />
<SolidColorBrush x:Key="WindowBorderColour" />
<SolidColorBrush x:Key="WindowTitleColour" />
WealthLab-Specific Colors
<SolidColorBrush x:Key="MDITitleBackground" />
<SolidColorBrush x:Key="SelectorBackground" />
<SolidColorBrush x:Key="BuildingBlock" />
<SolidColorBrush x:Key="BuildingBlockBuy" />
<SolidColorBrush x:Key="BuildingBlockSell" />
<SolidColorBrush x:Key="BuildingBlockShort" />
<SolidColorBrush x:Key="BuildingBlockCover" />
<SolidColorBrush x:Key="GreenButtonBackground" />
<SolidColorBrush x:Key="GreenButtonBorder" />
<SolidColorBrush x:Key="RedButtonBackground" />
Panel Background Colors
<SolidColorBrush x:Key="GreenBackground" />
<SolidColorBrush x:Key="RedBackground" />
<SolidColorBrush x:Key="YellowBackground" />
<SolidColorBrush x:Key="BlueBackground" />
<SolidColorBrush x:Key="ConstructorBackground" />
<SolidColorBrush x:Key="OrangeBackground" />
<SolidColorBrush x:Key="VioletBackground" />
<SolidColorBrush x:Key="BrownBackground" />
<SolidColorBrush x:Key="BlueGrayBackground" />
<SolidColorBrush x:Key="WealthSignalsBackground" />
Text Colors
<SolidColorBrush x:Key="RedText" />
<SolidColorBrush x:Key="GreenText" />
<SolidColorBrush x:Key="BlueText" />
Building Block Colors
<SolidColorBrush x:Key="BBBackground" />
<SolidColorBrush x:Key="BBLongEntry" />
<SolidColorBrush x:Key="BBLongExit" />
<SolidColorBrush x:Key="BBShortEntry" />
<SolidColorBrush x:Key="BBShortExit" />
<SolidColorBrush x:Key="BBCondition" />
<SolidColorBrush x:Key="BBQualifier" />
Standard UI Colors
<SolidColorBrush x:Key="TransparentBrush" />
<SolidColorBrush x:Key="ContainerBackground" />
<SolidColorBrush x:Key="ContainerBorder" />
<SolidColorBrush x:Key="ControlDefaultForeground" />
<SolidColorBrush x:Key="ControlMOSelectForeground" />
<SolidColorBrush x:Key="ControlDarkerBackground" />
<SolidColorBrush x:Key="ControlDarkerBorderBrush" />
<SolidColorBrush x:Key="ControlDefaultBackground" />
<SolidColorBrush x:Key="ControlDefaultBorderBrush" />
<SolidColorBrush x:Key="ControlBrightDefaultBackground" />
<SolidColorBrush x:Key="ControlBrightDefaultBorderBrush" />
<SolidColorBrush x:Key="ControlDisabledBackground" />
<SolidColorBrush x:Key="ControlDisabledBorderBrush" />
<SolidColorBrush x:Key="ControlMouseOverBackground" />
<SolidColorBrush x:Key="ControlMouseOverBorderBrush" />
<SolidColorBrush x:Key="ControlSelectedBackground" />
<SolidColorBrush x:Key="ControlSelectedBorderBrush" />
<SolidColorBrush x:Key="ControlSelectedMouseOverBackground" />
<SolidColorBrush x:Key="ControlSelectedMouseOverBorderBrush" />
<SolidColorBrush x:Key="ControlGlythColour" />
<SolidColorBrush x:Key="ControlMouseOverGlythColour" />
<SolidColorBrush x:Key="ControlSelectedGlythColour" />
<SolidColorBrush x:Key="ControlDisabledGlythColour" />
The standard foreground text color is:
{DynamicResource ControlDefaultForeground}
Toolbars generally use:
{DynamicResource ContainerBackground}
for their background.
Determining the Current Theme
In C# Code
The IWLClientHost interface in WealthLab.WPF exposes information about the Theme associated with a WealthLab window. Use:
WLClientHost.Instance.IsDarkTheme
to determine whether the active Theme is Dark. For example:
bool isDark =
WLClientHost.Instance.IsDarkTheme;
Detecting Theme Changes
You can subscribe to Theme changes through the EventRouter class in WealthLab.Core. Subscribe to the event:
ThemeChanged
The event parameter is a bool indicating whether the newly selected Theme is Dark. Use this when your extension performs Theme-sensitive work that cannot be handled entirely through DynamicResource bindings.
In XAML
The Theme resources also expose a Boolean resource named:
IsDark
You can bind to it using DynamicResource. For example:
IsReversed="{DynamicResource IsDark}"
This is useful for controls such as WLImage that can alter their appearance for the Dark Theme.
Example: Theme-Aware Image
The following example uses WLImage in a DataTemplate and automatically reverses the image when Dark Theme is active:
<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 Glyphs in Configurable Components
Many WealthLab extension types ultimately derive from Configurable. Configurable exposes the GlyphResource property used to represent the component in the WealthLab user interface. By default, WealthLab can reverse the glyph when the Dark Theme is active. If your glyph is designed to work correctly in both Light and Dark Themes without modification, override:
public virtual bool DisableGlyphReverse
and return true. For example:
public override bool DisableGlyphReverse =>
true;
WLImage
WLImage, defined in WealthLab.WPF, derives from the WPF Image control and adds Theme-aware image handling.
BaseSource
public ImageSource BaseSource
Gets or sets the source image. WLImage can automatically display a reversed version when required by the current Theme.
DisableReverse
public bool DisableReverse
Set this property to true to disable automatic image reversal.
IsReversed
public bool IsReversed
Gets or sets whether the image is currently displayed in its reversed state. This property can also be bound to the Theme's IsDark DynamicResource.
WLButton
WLButton, defined in WealthLab.WPF, derives from WPF's Button class and provides additional WealthLab functionality, including Theme-aware image handling.
DisplayState
public WLButtonDisplayState DisplayState
Controls what the button displays. Supported values include:
- Text
- Image
- ImageAndText
Text
public string Text
Contains the text displayed by the button when DisplayState includes Text.
Image
public ImageSource Image
Contains the image displayed when DisplayState includes Image. WLButton uses WLImage internally to provide Theme-aware image handling.
DisableReverse
public bool DisableReverse
Set this property to true if the button's image should not be automatically reversed when using a Dark Theme.
Theming Custom Windows
When creating a custom WealthLab window, derive from the DialogWindow class in WealthLab.WPF instead of directly from WPF's Window class. DialogWindow contains the integration required to participate in WealthLab theming. Apply the standard WealthLab window style using:
Style="{DynamicResource CustomToolWindowStyle}"
Use DynamicResource bindings for the Window's Theme-sensitive colors. For example:
Background="{DynamicResource ContainerBackground}"
Sizing DialogWindow
WPF normally uses the Window SizeToContent property to automatically size a Window around its contents. For a DialogWindow, use:
DesignSizeToContent
instead. This avoids WPF sizing problems that can occur when a custom Window style and SizeToContent are used together.
Context Menus
Use WLContextMenu from WealthLab.WPF instead of the standard WPF ContextMenu when creating context menus in a WealthLab extension.
<wpf:WLContextMenu>
...
</wpf:WLContextMenu>
WLContextMenu includes the integration required to behave consistently with WealthLab Themes.
Dynamically Created UI
UI elements created dynamically at runtime may occasionally need the current Theme explicitly reapplied. The IWLClientHost interface provides:
void RefreshTheme(
DependencyObject dobj)
Call RefreshTheme to apply the current Theme to the supplied DependencyObject and its descendants. For example:
MyClientHost.RefreshTheme(this);
This can be useful after significantly changing or rebuilding the visual tree of a dynamically generated interface. In many cases, normal DynamicResource bindings update automatically when the Theme changes, so call RefreshTheme only when dynamically created or replaced elements do not pick up the current Theme correctly.