Skip to main content

.NET Styling Themes

Introduction

When developing desktop applications in .NET, creating an attractive and consistent visual appearance is crucial for user experience. .NET provides robust mechanisms for styling and theming that allow developers to customize the look and feel of their applications without changing the underlying functionality.

In this tutorial, we'll explore how to work with styling themes in .NET desktop applications, focusing on the two main UI frameworks: Windows Presentation Foundation (WPF) and Windows Forms. You'll learn how to create, apply, and dynamically switch between themes to enhance your applications' visual appeal and usability.

Understanding Styling in .NET Desktop Applications

Windows Presentation Foundation (WPF)

WPF offers a powerful styling system based on resources, styles, and templates. Its styling capabilities are significantly more advanced than Windows Forms, providing:

  • Resource Dictionaries - collections of reusable resources
  • Styles - for consistent appearance across multiple controls
  • Templates - for complete control over how elements render
  • Triggers - for changing appearance based on events or conditions

Windows Forms

Windows Forms has more limited built-in styling capabilities compared to WPF but still offers:

  • Properties - for customizing individual control appearances
  • Custom painting - through event handlers
  • Third-party component libraries - to enhance styling options

Basic Styling in WPF

Creating and Applying Styles

In WPF, styles are defined using XAML and can be applied to specific controls or control types.

xml
<Window x:Class="WpfThemeDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF Theme Demo" Height="350" Width="500">
<Window.Resources>
<!-- Style targeting all buttons -->
<Style TargetType="Button">
<Setter Property="Background" Value="#3498db"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="5"
BorderThickness="1"
BorderBrush="#2980b9">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#2980b9"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#1c6ea4"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>

<StackPanel>
<Button Content="Styled Button 1"/>
<Button Content="Styled Button 2"/>
<Button Content="Styled Button 3"/>
</StackPanel>
</Window>

This code creates a style for all buttons in the window, giving them a blue appearance with rounded corners and hover effects.

Creating Resource Dictionaries

For better organization and reusability, styles should be placed in resource dictionaries.

xml
<!-- LightTheme.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Colors -->
<Color x:Key="PrimaryColor">#3498db</Color>
<Color x:Key="PrimaryDarkColor">#2980b9</Color>
<Color x:Key="BackgroundColor">#ffffff</Color>
<Color x:Key="TextColor">#333333</Color>

<!-- Brushes -->
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<SolidColorBrush x:Key="PrimaryDarkBrush" Color="{StaticResource PrimaryDarkColor}"/>
<SolidColorBrush x:Key="BackgroundBrush" Color="{StaticResource BackgroundColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>

<!-- Button Style -->
<Style x:Key="DefaultButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5"/>
<!-- Template omitted for brevity -->
</Style>
</ResourceDictionary>

Then, create a DarkTheme.xaml with different colors but the same structure:

xml
<!-- DarkTheme.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Colors -->
<Color x:Key="PrimaryColor">#673ab7</Color>
<Color x:Key="PrimaryDarkColor">#512da8</Color>
<Color x:Key="BackgroundColor">#212121</Color>
<Color x:Key="TextColor">#ffffff</Color>

<!-- Brushes -->
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<SolidColorBrush x:Key="PrimaryDarkBrush" Color="{StaticResource PrimaryDarkColor}"/>
<SolidColorBrush x:Key="BackgroundBrush" Color="{StaticResource BackgroundColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>

<!-- Button Style -->
<Style x:Key="DefaultButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5"/>
<!-- Template omitted for brevity -->
</Style>
</ResourceDictionary>

Creating a Theme Manager for WPF

Let's create a theme manager class that allows dynamic switching between themes:

csharp
using System;
using System.Windows;

namespace WpfThemeDemo
{
public enum ThemeType
{
Light,
Dark
}

public class ThemeManager
{
private static ResourceDictionary _currentTheme;

public static void ApplyTheme(ThemeType theme)
{
string themePath = theme == ThemeType.Light
? "Themes/LightTheme.xaml"
: "Themes/DarkTheme.xaml";

_currentTheme = new ResourceDictionary
{
Source = new Uri(themePath, UriKind.RelativeOrAbsolute)
};

// Remove the current theme if it exists
if (Application.Current.Resources.MergedDictionaries.Count > 0)
{
Application.Current.Resources.MergedDictionaries.RemoveAt(0);
}

// Add the new theme
Application.Current.Resources.MergedDictionaries.Insert(0, _currentTheme);
}
}
}

Usage example:

csharp
// Apply the light theme when the application starts
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ThemeManager.ApplyTheme(ThemeType.Light);
}

// Button click handler to toggle themes
private void ToggleThemeButton_Click(object sender, RoutedEventArgs e)
{
// Determine current theme and toggle to the other
var currentTheme = Application.Current.Resources.MergedDictionaries[0].Source.ToString().Contains("Light")
? ThemeType.Dark
: ThemeType.Light;

ThemeManager.ApplyTheme(currentTheme);
}

Styling in Windows Forms

Windows Forms has more limited styling capabilities, but we can still create custom themes:

csharp
using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsThemeDemo
{
public enum ThemeType
{
Light,
Dark
}

public static class FormThemeManager
{
// Theme colors
private static class LightTheme
{
public static readonly Color PrimaryColor = Color.FromArgb(52, 152, 219); // #3498db
public static readonly Color SecondaryColor = Color.FromArgb(41, 128, 185); // #2980b9
public static readonly Color BackgroundColor = Color.White;
public static readonly Color TextColor = Color.FromArgb(51, 51, 51); // #333333
}

private static class DarkTheme
{
public static readonly Color PrimaryColor = Color.FromArgb(103, 58, 183); // #673ab7
public static readonly Color SecondaryColor = Color.FromArgb(81, 45, 168); // #512da8
public static readonly Color BackgroundColor = Color.FromArgb(33, 33, 33); // #212121
public static readonly Color TextColor = Color.White;
}

public static void ApplyTheme(Form form, ThemeType theme)
{
Color primaryColor, secondaryColor, backgroundColor, textColor;

// Set colors based on selected theme
if (theme == ThemeType.Light)
{
primaryColor = LightTheme.PrimaryColor;
secondaryColor = LightTheme.SecondaryColor;
backgroundColor = LightTheme.BackgroundColor;
textColor = LightTheme.TextColor;
}
else
{
primaryColor = DarkTheme.PrimaryColor;
secondaryColor = DarkTheme.SecondaryColor;
backgroundColor = DarkTheme.BackgroundColor;
textColor = DarkTheme.TextColor;
}

// Apply colors to the form
form.BackColor = backgroundColor;
form.ForeColor = textColor;

// Apply theme to all controls recursively
ApplyThemeToControls(form.Controls, primaryColor, secondaryColor, backgroundColor, textColor);
}

private static void ApplyThemeToControls(Control.ControlCollection controls,
Color primaryColor, Color secondaryColor, Color backgroundColor, Color textColor)
{
foreach (Control control in controls)
{
// Apply theme based on control type
if (control is Button button)
{
button.BackColor = primaryColor;
button.ForeColor = Color.White;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderColor = secondaryColor;
button.FlatAppearance.MouseOverBackColor = secondaryColor;
}
else if (control is TextBox textBox)
{
textBox.BackColor = backgroundColor;
textBox.ForeColor = textColor;
textBox.BorderStyle = BorderStyle.FixedSingle;
}
else if (control is Panel panel)
{
panel.BackColor = backgroundColor;
panel.ForeColor = textColor;
}
// Add more control types as needed

// Recursively apply theme to child controls
if (control.Controls.Count > 0)
{
ApplyThemeToControls(control.Controls, primaryColor, secondaryColor, backgroundColor, textColor);
}
}
}
}
}

Usage example:

csharp
public partial class MainForm : Form
{
private ThemeType _currentTheme = ThemeType.Light;

public MainForm()
{
InitializeComponent();

// Apply default theme
FormThemeManager.ApplyTheme(this, _currentTheme);
}

private void toggleThemeButton_Click(object sender, EventArgs e)
{
// Toggle theme
_currentTheme = _currentTheme == ThemeType.Light ? ThemeType.Dark : ThemeType.Light;

// Apply new theme
FormThemeManager.ApplyTheme(this, _currentTheme);
}
}

Practical Example: Creating a Themed Application

Let's create a simple application that demonstrates theme switching in WPF:

App.xaml

xml
<Application x:Class="ThemeDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Theme dictionaries will be loaded dynamically -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

MainWindow.xaml

xml
<Window x:Class="ThemeDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Theme Demo" Height="450" Width="800"
Background="{DynamicResource BackgroundBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel Grid.Row="0" Orientation="Horizontal" Background="{DynamicResource PrimaryBrush}" Padding="10">
<TextBlock Text="Theme Demo App" FontSize="20" Foreground="White" VerticalAlignment="Center"/>
<Button x:Name="ThemeToggleButton"
Content="Toggle Theme"
Click="ThemeToggleButton_Click"
HorizontalAlignment="Right"
Margin="20,0,0,0"
Style="{DynamicResource DefaultButtonStyle}"/>
</StackPanel>

<Grid Grid.Row="1" Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<TextBlock Grid.Row="0"
Text="Welcome to the Theme Demo"
FontSize="24"
Foreground="{DynamicResource TextBrush}"
Margin="0,0,0,20"/>

<TextBlock Grid.Row="1"
Text="This application demonstrates how to implement theme switching in WPF applications."
TextWrapping="Wrap"
Foreground="{DynamicResource TextBrush}"
Margin="0,0,0,20"/>

<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,20">
<Button Content="Primary Button"
Style="{DynamicResource DefaultButtonStyle}"/>
<Button Content="Secondary Button"
Margin="10,0,0,0"
Background="Transparent"
BorderBrush="{DynamicResource PrimaryBrush}"
Foreground="{DynamicResource PrimaryBrush}"
Padding="10,5"
BorderThickness="1"/>
</StackPanel>

<Border Grid.Row="3"
BorderBrush="{DynamicResource PrimaryBrush}"
BorderThickness="1"
Padding="15">
<TextBox Text="This is a themed text box."
Background="Transparent"
Foreground="{DynamicResource TextBrush}"
BorderThickness="0"/>
</Border>
</Grid>
</Grid>
</Window>

MainWindow.xaml.cs

csharp
using System.Windows;

namespace ThemeDemo
{
public partial class MainWindow : Window
{
private ThemeType _currentTheme = ThemeType.Light;

public MainWindow()
{
InitializeComponent();

// Initialize with Light theme
ThemeManager.ApplyTheme(ThemeType.Light);
}

private void ThemeToggleButton_Click(object sender, RoutedEventArgs e)
{
// Toggle between themes
_currentTheme = _currentTheme == ThemeType.Light ? ThemeType.Dark : ThemeType.Light;
ThemeManager.ApplyTheme(_currentTheme);
}
}
}

Advanced Theming Techniques

Dynamic Theme Switching with User Preferences

To remember the user's theme preference, we can use application settings:

csharp
// Add this to your ThemeManager class
public static void LoadSavedTheme()
{
// Use settings from app.config
ThemeType savedTheme = (ThemeType)Properties.Settings.Default.Theme;
ApplyTheme(savedTheme);
}

public static void SaveTheme(ThemeType theme)
{
Properties.Settings.Default.Theme = (int)theme;
Properties.Settings.Default.Save();
}

Theme Animations

You can create smooth transitions when switching themes:

csharp
private void AnimateThemeTransition(ThemeType newTheme)
{
var currentBackground = this.Background as SolidColorBrush;
var targetBackground = newTheme == ThemeType.Light
? new SolidColorBrush(Colors.White)
: new SolidColorBrush(Color.FromRgb(33, 33, 33));

var animation = new ColorAnimation
{
From = currentBackground.Color,
To = targetBackground.Color,
Duration = new Duration(TimeSpan.FromMilliseconds(300))
};

var backgroundBrush = new SolidColorBrush(currentBackground.Color);
this.Background = backgroundBrush;

backgroundBrush.BeginAnimation(SolidColorBrush.ColorProperty, animation);

// Apply the actual theme after animation
animation.Completed += (s, e) => ThemeManager.ApplyTheme(newTheme);
}

Summary

In this tutorial, we've explored how to create and implement styling themes in .NET desktop applications:

  1. WPF Styling Basics - We learned how to create and apply styles to WPF controls
  2. Resource Dictionaries - We organized styles into reusable resource dictionaries
  3. ThemeManager - We created a theme manager to switch between light and dark themes dynamically
  4. Windows Forms Theming - We developed a custom approach for theming Windows Forms applications
  5. Practical Example - We built a complete themed application with dynamic theme switching

Theming is essential for creating polished, professional-looking applications that provide a good user experience across different environments and user preferences.

Additional Resources

Exercises

  1. Create a custom theme with your favorite colors and apply it to a WPF application
  2. Implement a theme selector that allows users to choose from multiple themes
  3. Add system theme detection to automatically use light or dark theme based on OS settings
  4. Create a custom control template that changes its appearance based on the current theme
  5. Implement an animated theme transition that fades between themes


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)