Click here to Skip to main content
15,890,438 members
Articles / Desktop Programming / WPF
Article

Dynamically Update Styles for Multiple Windows/User Controls - WPF

Rate me:
Please Sign up or sign in to vote.
4.25/5 (4 votes)
25 May 2010CPOL2 min read 26.8K   538   8  
Describes a technique for updating styles for multiple windows at once dynamically based on a property setting. This technique uses data triggers. It is demonstrated by creating two windows that support a day and night mode.

Introduction

The purpose of this article is to demonstrate how to update the style for multiple windows at once by changing a single property value. It does this by using data triggers and an abstract base view model. This article will probably be helpful for someone who wants to make style changes to multiple windows/controls by just updating one property. This whole concept is demonstrated by creating two windows that implement a day and night mode. This concept can be used for many other implementations, such as creating an onscreen keyboard that needs to update another window.

Background

This article makes use of the MVVM (Model View View Model) Framework. It also uses Autofac's IoC containers and a routed command. It will be helpful to have a basic understanding of these concepts.

The Property Class

The UI has the ability to change the appearance based on different configurations and property settings. There is a class AppInfo that contains different properties. There are components on WPF windows that bind to these properties indirectly (meaning – the WPF window is bound to a property in its respective view model class, and the property gets updated by registering to a DayNightModeChanged event that gets raised from the AppInfo class). This allows the WPF pages to update dynamically whenever the property changes. This is done by using DataTriggers in the XAML code.

I will demonstrate this concept using the Day/Night mode functionality.

Goal: When the boolean property DayMode in the AppInfo class changes, the UI shall change its appearance.

The following class contains the property that we wish to monitor (DayMode).

C#
public class AppInfo
{
    private bool _dayMode = true;

    public bool DayMode
    {
        get
        {
            return _dayMode;
        }
        set
        {
            _dayMode = value;
            DayNightModeChanged(value);
        }
    }

    // all windows that want to monitor
    // the DayMode property need to subscribe to this action
    public Action<bool> DayNightModeChanged;
}

When the DayMode property is set, we raise the event DayNightModeChanged.

The Base Class

All View Models (Window1ViewModel and Window2ViewModel) implement an abstract class ViewModelBase.

C#
public class HeaderViewModel : ViewModelBase
public class LoginViewModel : ViewModelBase

ViewModelBase implements INotifyPropertyChanged so that the WPF pages will know when the DayMode property has changed.

C#
public abstract class ViewModelBase : DependencyObject, INotifyPropertyChanged, IDisposable

In the constructor of the ViewModelBase class, we register for the DayNightModeChanged event.

C#
public ViewModelBase()
{
    // without this condition the designer has trouble loading
    // the window. Not necessary for run time,
    // but is nice so that you can see your changes in the designer.
    if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
    {
        IocContainer.Resolve<AppInfo>().DayNightModeChanged += 
                     new Action<bool>(DayNightMode_Changed);
    }
}

// this handles the action so that we can update the DayMode property in this class. 
// the DayMode property in this class is the one that the windows are bound to.
private void DayNightMode_Changed(bool result)
{
    DayMode = result;
}

When DayMode is set in ViewModelBase, we raise the OnPropertyChanged event, passing in the string “DayMode”.

C#
public bool DayMode
{
    get { return (bool)GetValue(DayModeProperty); }
    set 
    { 
        SetValue(DayModeProperty, value);
        OnPropertyChanged("DayMode");
    }
}

// this is to make use of the UpdateSourceTrigger attribute in the xaml code.
protected virtual void OnPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

Given that all Views implement ViewModelBase, all Views that bind to DayMode will receive the property change. The following demonstrates the binding.

The Binding

I have a User Control named Header. The DataContext of Header is set to HeaderViewModel (remembering that HeaderViewModel implements ViewModelBase). During that assignment, we also assign DayMode = true. This is used during design time only, so that the change in appearance can be observed.

HTML
<!--Design time data-->
<UserControl.DataContext>
    <ViewModels:HeaderViewModel DayMode="True"></ViewModels:HeaderViewModel>
</UserControl.DataContext>

I set up the style of the Grid in my Header using DataTriggers...

XML
<Grid x:Name="myGrid">
    <Grid.Style>
        <Style TargetType="{x:Type Grid}">
            <Style.Triggers>
                <DataTrigger 
                        Binding="{Binding Path=DayMode, UpdateSourceTrigger=PropertyChanged}" 
                        Value="false">
                    <Setter Property="Background">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint=".5,0" EndPoint=".5, 1">
                                <GradientStop Color="Black" Offset="0.0" />
                                <GradientStop Color="#6F6F6F" Offset="0.95" />
                                <GradientStop Color="Black" Offset="1.0" />
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=DayMode, 
                                       UpdateSourceTrigger=PropertyChanged}" Value="true">
                    <Setter Property="Background">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint=".5,0" EndPoint=".5, 1">
                                <GradientStop Color="#ECECEC" Offset="0.0" />
                                <GradientStop Color="#CCCCCC" Offset="0.95" />
                                <GradientStop Color="Black" Offset="1.0" />
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
</Grid>

Notice how there are two DataTriggers bound to DayMode. They both get updated by the PropertyChanged event. One checks for true and one for false. The grid takes on the style properties in their respective DataTriggers depending on the value of DayMode.

Conclusion

By setting up more properties in this way, we can dynamically update the appearance of the UI with very little code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer The IMS Company
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --