WPF Style Swapping in MVVM

Spread the love

We delve into the ideas in our WPF projects today, as part of my new system implementations I had a simple problem.

One of the examples

I figured it should be possible, you must be able to easily adjust your style’s theme in your WPF application. Looking around, seeing it asked and answered on StackOverflow, I just couldn’t quite find a descriptive good answer.

Reading documentation I have a simple way I’ll share to introduce a simple MVVM style with adjustable styles.

You can take a look at the project, in Visual Studio 2019, for yourself.

The Way To Swap The Visual Style

Considering practices of MVVM use, it was an easy way to show it can be within work projects. I can step deeper into MVVM thoughts and my personal practices if anyone would like a little more information.

Hence, I will assume that you have some concept of MVVM for the implementation below. Just note, you will see we cut a few corners for simplicity in this example project.

Since we will stick to simplified examples, we have a minimal data design to use for ourselves.

    class DataItem
    {
        public string Name { get; set; }
        public string Details { get; set; }

        public DataItem(string name, string details)
        {
            Name = name;
            Details = details;
        }
    }

It should allow you to notice we cut a few corners from the MVVM practices for ourselves. This isn’t for showing off complex practices and data, but rather the simpler adjustments we want.

We will make ourselves a sample View Model for showing the use off.

    class DataViewModel
    {
        public List<DataItem> Items { get; set; }
        public ICommand ChangeStyle { get; set; }

        public DataViewModel()
        {
            Items = new List<DataItem>()
            {
                new DataItem("First", "First description."),
                new DataItem("Second", "Second description."),
                new DataItem("Third", "Third description.")
            };

            ChangeStyle = new CommandBase();
        }

        public DataView Window { get; set; }
    }

For those that practice MVVM for themselves you would know it’s easiest to implement a View Model Locator.

    class ViewModelLocator
    {
        public static ViewModelLocator Instance { get; set; }

        public ViewModelLocator()
        {
            Instance = this;
            _dataVM = new DataViewModel();
        }

        private DataViewModel _dataVM;
        public DataViewModel DataVM
        {
            get { return _dataVM; }
        }
    }

That requires a CommandBase, and this is where the example of the use comes, however, we first should share the view’s design:

<Window x:Class="TutMVVMBinding.MainWindow"
        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:local="clr-namespace:TutMVVMBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="480" Width="640">
    <Grid>
        <Frame x:Name="frmContent" />
    </Grid>
</Window>

You will note, we use the very minimal design for our Main Window. We can always add way more in the design of the window, for instance below is the main window style from one of my work projects.

Example

You will, no doubt, note there are elements towards the left of the window. My preference is to use Frames in the layout for the MVVM view locations. Since this only adds the example of MVVM practices we have a tiny amount of code to show this is used in the practices we would want.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var window = new DataView();
            frmContent.Content = window.Content;
            window.Close();
        }
    }

This leaves only one Window to show off so you can see the practice of the MVVM and the code adjustments to the views.

<Window x:Class="TutMVVMBinding.DataView"
        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:local="clr-namespace:TutMVVMBinding"
        mc:Ignorable="d"
        Title="DataView" Height="480" Width="640">
    <Grid DataContext="{Binding DataVM, Source={StaticResource VMLocator}}" Loaded="Grid_Loaded">
        <Grid.RowDefinitions>
            <RowDefinition Height="22" />
            <RowDefinition />
        </Grid.RowDefinitions>
        
        <Button x:Name="butStyle" Grid.Row="0" Content="Change Style" Command="{Binding ChangeStyle}"/>
        <ListBox x:Name="lstData" Grid.Row="1" ItemsSource="{Binding Items}" />
    </Grid>
</Window>

We bind to the DataVM in our VMLocator. You will notice how we don’t do much more at all. Our important code would lie in the CommandBase.

For simplicity we don’t do much in the command for ourselves, just show how we can use ICommands in this manner. Since in MVVM we use data stored within the view models it is just a very simple action for us to manage.

We just change the style that the view binds the element to.

    class CommandBase : ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        int _num = 0;
        public void Execute(object parameter)
        {
            var dataVM = ViewModelLocator.Instance.DataVM;

            ++_num;
            if (_num > 2) _num = 0; // Make sure num stays in range

            var style = (Style)dataVM.Window.FindResource("style" + _num);
            var lstData = dataVM.Window.lstData;
            lstData.ItemContainerStyle = style;
        }
    }

As you will note, when implementing your MVVM style project in WPF projects you’ll make folders and collections of code and views for yourself. We don’t delve too deep into that side of WPF use for ourselves, but rather the separate styles.

Withing App.xaml we would make the styles. You can see the code above shows there are 3 styles, we will just focus on the first.

    <Application.Resources>
        <local:ViewModelLocator x:Key="VMLocator" />

        <Style x:Key="style0" TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid Margin="5" Background="LightBlue" Height="48">
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Label Grid.Row="0" Content="{Binding Name}" />
                            <Label Grid.Row="1" Content="{Binding Details}" FontSize="8" HorizontalAlignment="Center" Foreground="DarkCyan"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

We make a reference to the VMLocator which we used within DataView.xaml.

<Grid DataContext="{Binding DataVM, Source={StaticResource VMLocator}}" Loaded="Grid_Loaded">

Then you’d also note we implemented a Grid_Loaded function for ourselves. Here, we just in the view model set the DataView for ourselves that we use.

        private void Grid_Loaded(object sender, RoutedEventArgs e)
        {
            ViewModelLocator.Instance.DataVM.Window = this;
        }

Essentially, when the view is created it lets the view model know what window it would be. You will see CommandBase takes this Window and adjust the lstData.ItemContainerStyle.

The view swaps from this:

First default style for data

To this:

First style

The main reason behind not showing the entire App.xaml before was just to point out this can be used to control your style within WPF applications. You could implement a data value in a configuration file that is within your app folders to assign a specific style.

<Application x:Class="TutMVVMBinding.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:TutMVVMBinding"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:ViewModelLocator x:Key="VMLocator" />

        <Style x:Key="style0" TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid Margin="5" Background="LightBlue" Height="48">
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Label Grid.Row="0" Content="{Binding Name}" />
                            <Label Grid.Row="1" Content="{Binding Details}" FontSize="8" HorizontalAlignment="Center" Foreground="DarkCyan"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="style1" TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid Margin="5" Background="LightGray" Height="54">
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Label Grid.Row="0" Content="{Binding Name}" FontWeight="Bold" HorizontalAlignment="Center"/>
                            <Label Grid.Row="1" Content="{Binding Details}" FontSize="8" HorizontalAlignment="Left" FontStyle="Italic" Foreground="DarkGreen"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="style2" TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid Margin="5" Background="Black" Height="32" HorizontalAlignment="Center" Width="200">
                            <Label Grid.Row="0" Content="{Binding Name}" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Orange"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Application.Resources>
</Application>

Here we go, the 3 styles we use in the app example you can download to test and see for yourself. This is just to easily show how you can control your style directly through code.

Second style.
Third Style

In the simplest way, this is just to share and show how you can use your C# code to adjust the visual style within your WPF application. Note, I even used less data in the second style, your data stays the exact same. Just, now you have more control on the visual elements you show to the user!

You could have a table in a database that stores the configuration of your application. Say perhaps:

1, 'visual style', 'style1'

This could be queried by your View Model and it can assign ‘style1‘ to the view whenever that view opens. Well, you would probably only query when your application launches for the styles, it’s just a way to share you can adjust your view using data storage easily.

I hope this is simple enough for anyone that would ever feel the need to have adjustable WPF views, and styles, in their projects!