UI Theme MahApps.Metro

WPF Views can be themed to look way more interesting than the default look. To achieve this you can use the UI Theme MahApps.Metro.

Preview

MahApps Preview

Quick Start

Theres a quick start guide on from MahApps: Wiki QuickStart or Guides QuickStart.

Install the Nuget Package

MahApps Preview

Add ressources in App.xaml

MahApps Preview

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Application x:Class="WpfApplication.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>
        <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
        <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

Change View to MahApps Window

Change the Window to the mah:MetroWindowand add the xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" as namespace:

MahApps Preview

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<mah:MetroWindow x:Class="HelloWorldWPF.View.HelloWorldView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
                 xmlns:local="clr-namespace:HelloWorldWPF.View"
                 xmlns:vm="clr-namespace:HelloWorldWPF.ViewModel"
                 mc:Ignorable="d"
                 Title="HelloWorldView" Height="200" Width="300"
                 WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <vm:HelloWorldViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="20" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>
        <Label Grid.Column="1"
               Grid.Row="1"
               x:Name="label"
               FontSize="30"
               Margin="0"
               Content="{Binding HelloString}"
               HorizontalAlignment="Center"
               VerticalAlignment="Center" />
        <Label Grid.Column="1"
               Grid.Row="2"
               x:Name="labelTheme"
               FontSize="20"
               Margin="0 5 0 5"
               Content="{Binding ActiveTheme}"
               HorizontalAlignment="Center"
               VerticalAlignment="Center" />
        <UniformGrid Grid.Column="1" Grid.Row="3" Columns="2">
            <Button Grid.Column="1"
                    Height="30"
                    Content="Swap Themes"
                    x:Name="btnSwapUiTheme"
                    Click="btnSwapUiTheme_Click" />
            <Button Grid.Column="1"
                    Height="30"
                    Content="Do something"
                    x:Name="btnDoSomething"
                    Command="{Binding CmdDoSomething}" />
        </UniformGrid>
    </Grid>
</mah:MetroWindow>

Add the inheritance of the MetroWindow class in the Code-behind (optional, not always necessary):

MahApps Preview

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using ControlzEx.Theming;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using System;
using System.Collections.Generic;
using System.Windows;
namespace HelloWorldWPF.View
{
    /// <summary>
    /// Interaktionslogik für HelloWorldView.xaml
    /// </summary>
    public partial class HelloWorldView : MetroWindow
    {
        #region Fields
        /// <summary>
        /// Dictionary with keyValue pairs for MahApp.Metro Themes. 
        /// </summary>
        private Dictionary<int, string> themes = new Dictionary<int, string>()
            {
                {1, "Red"},
                {2, "Green"},
                {3, "Blue"},
                {4, "Purple"},
                {5, "Orange"},
                {6, "Lime"},
                {7, "Emerald"},
                {8, "Teal"},
                {9, "Cyan"},
                {10, "Cobalt"},
                {12, "Indigo"},
                {13, "Violet"},
                {14, "Pink"},
                {15, "Magenta"},
                {16, "Crimson"},
                {17, "Amber"},
                {18, "Yellow"},
                {19, "Brown"},
                {20, "Sienna"},
                {21, "Olive"},
                {22, "Steel"},
                {23, "Mauve"},
                {24, "Taupe"}
            };
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Constructor inits the View and sets the MahApp.Metro Theme with synced Themes for all Windows.
        /// </summary>
        public HelloWorldView()
        {
            InitializeComponent();
            ThemeManager.Current.ChangeTheme(this, "Dark.Blue");
            ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode;
            ThemeManager.Current.SyncTheme();
        }
        #endregion Constructor
        
        #region Methods
        /// <summary>
        /// Click event randomizes the MahApp.Metro Theme with a random key for the themes dictionary.
        /// </summary>
        private void btnSwapUiTheme_Click(object sender, RoutedEventArgs e)
        {
            Random random = new Random();
            int themeKey = random.Next(1, themes.Count);
            if (themes.ContainsKey(themeKey))
            {
                ThemeManager.Current.ChangeTheme(this, $"Dark.{themes[themeKey]}");
                labelTheme.Content = $"Dark.{themes[themeKey]}";
            }
            else
            {
                this.ShowMessageAsync("Error", $"Random themeKey {themeKey} was not in the themepack!");
            }
        }
        #endregion Methods
    }
}

some test with a ViewModel

MahApps Preview

ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
using ControlzEx.Theming;
using HelloWorldWPF.Commands;
using HelloWorldWPF.Model;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace HelloWorldWPF.ViewModel
{
    public class HelloWorldViewModel : INotifyPropertyChanged
    {
        #region Fields
        private string helloString;
        private string activeTheme;
        private ClientCommand cmdDoSomething;   // use custom implementation of ClientCommand
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion Fields

        #region Properties
        public string HelloString
        {
            get
            {
                return helloString;
            }
            set
            {
                helloString = value;
                OnPropertyChanged();
            }
        }
        public string ActiveTheme
        {
            get
            {
                return activeTheme;
            }
            set
            {
                activeTheme = value;
                OnPropertyChanged();
            }
        }
        public ICommand CmdDoSomething
        {
            get
            {
                if (cmdDoSomething == null)
                {
                    cmdDoSomething = new ClientCommand(CmdDoSomethingExecute);
                }
                return cmdDoSomething;
            }
        }
        #endregion Properties

        #region Constructor
        /// <summary>
        /// Constructor with object of the model and init of the properties.
        /// </summary>
        public HelloWorldViewModel()
        {
            HelloWorldModel helloWorldModel = new HelloWorldModel();
            helloString = helloWorldModel.ImportantInfo;
            ActiveTheme = ThemeManager.Current.DetectTheme().Name;
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Raises OnPropertychangedEvent when property changes.
        /// </summary>
        /// <param name="name">String representing the property name</param>
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        /// <summary>
        /// Command execution simulation with some code that changes a property and the UI shown value.
        /// </summary>
        /// <param name="commandParameter"></param>
        private void CmdDoSomethingExecute(object commandParameter)
        {
            ActiveTheme = $"changed: {DateTime.Now.ToString("dd.MM.yyyy-HH:mm:ss")}";
        }
        #endregion Methods
    }
}

Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System.Collections.Generic;
using System.Linq;
namespace HelloWorldWPF.Model
{
    public class HelloWorldModel
    {
        #region Fields
        private List<string> repositoryData;
        #endregion Fields
        #region Properties
        public string ImportantInfo
        {
            get
            {
                return ConcatenateData(repositoryData);
            }
        }
        #endregion Properties

        #region Constructor
        /// <summary>
        /// Constructor for the model simulates a repository data access ...
        /// </summary>
        public HelloWorldModel()
        {
            repositoryData = GetData();
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Simulates data retrieval from a repository
        /// </summary>
        /// <returns>List of strings</returns>
        private List<string> GetData()
        {
            repositoryData = new List<string>()
            {
                "Hello",
                "world"
            };
            return repositoryData;
        }
        /// <summary>
        /// Concatenate the information from the list into a fully formed sentence.
        /// </summary>
        /// <returns>A string</returns>
        private string ConcatenateData(List<string> dataList)
        {
            string importantInfo = dataList.ElementAt(0) + ", " + dataList.ElementAt(1) + "!";
            return importantInfo;
        }
        #endregion Methods
    }
}

ClientCommand class

The ClientCommand class enables binding methods from the VieModel into the View. This represents a reusable base class to setup the needed functionality and adds the possibility to deactivate the command in configured situations. You can disable the bound UI element with this as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Windows.Input;
namespace HelloWorldWPF.Commands
{
    public class ClientCommand : ICommand
    {
        #region Fields
        private readonly Predicate<object> _CanExecutePredicate;
        private readonly Action<object> _ExecuteAction;
        #endregion Fields

        #region Constructors
        public ClientCommand(Action<object> executeAction)
        {
            _ExecuteAction = executeAction;
        }
        public ClientCommand(Action<object> executeAction, Predicate<object> canExecutePredicate)
        {
            _ExecuteAction = executeAction;
            _CanExecutePredicate = canExecutePredicate;
        }
        #endregion Constructors

        #region Methods
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }
        public bool CanExecute(object parameter)
        {
            return _CanExecutePredicate == null || _CanExecutePredicate(parameter);
        }
        public void Execute(object parameter)
        {
            _ExecuteAction(parameter);
        }
        #endregion Methods
    }
}