WPF implementation of theme replacement simple demo

Source: Internet
Author: User
WPF implementation of theme replacement simple demo

The theme change function is mainly three points of knowledge:

    1. Dynamic Resources ( DynamicResource )
    2. INotifyPropertyChanged Interface
    3. Binding interface elements to the data model ( ViewModelinMVVM )

Demo Code Address: GITHUB

Let's head straight to the subject.

I. Preparation of theme Resources

In the project (How to build the project will not say, Baidu on more is) the following new folder Themes, the theme resources are placed here, I simply implemented the two theme Light/dark, the theme contains only the background color of a property.

1. Themes
    1. Theme.Dark.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                    xmlns:local="clr-namespace:ModernUI.Example.Theme.Themes">    <Color x:Key="WindowBackgroundColor">#333</Color></ResourceDictionary>
    1. Theme.Light.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                    xmlns:local="clr-namespace:ModernUI.Example.Theme.Themes">    <Color x:Key="WindowBackgroundColor">#ffffff</Color></ResourceDictionary>

Then add a default theme in the App.xaml of the program
Different meanings of resources are best separated into separate files, and finally merge into the App.xaml inside, so easy to manage and search.

    1. App.xaml
<Application x:Class="ModernUI.Example.Theme.App"             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"             xmlns:local="clr-namespace:ModernUI.Example.Theme"             StartupUri="MainWindow.xaml">    <Application.Resources>        <ResourceDictionary>            <ResourceDictionary.MergedDictionaries>                <ResourceDictionary Source="Themes/Theme.Light.xaml"/>            </ResourceDictionary.MergedDictionaries>        </ResourceDictionary>    </Application.Resources></Application>
Second, the implementation of the view model (ViewModel)

On the interface I imitate Modernui and use the ComboBox control to replace the theme, so this way we need to implement a view model to be bound by the ComboBox.

Create a new folder Prensentation, storing all the data model class files

1. Notifypropertychanged class

The notifypropertychanged class implements the INotifyPropertyChanged interface, which is the base class for all view models and is primarily used to implement data binding capabilities.

abstract class NotifyPropertyChanged : INotifyPropertyChanged{    public event PropertyChangedEventHandler PropertyChanged;    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "")    {        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));    }}

In this case, a [Callermembername] Attribute is used, which is a new feature in. NET 4.5 that enables the automatic filling of formal parameters, which are later called in Properties onpropertychanged method does not have to input parameters, which is more conducive to refactoring, not because after changing the property name, forget to change the onpropertychanged input parameter and cause a bug. Refer to C # in Depth (fifth edition) section 16.2 for details

2. Displayable class

displayable The data used to implement the interface rendering, the string shown on theComboBox Item is the DisplayName property

class Displayable : NotifyPropertyChanged{    private string _displayName { get; set; }    /// <summary>    /// name to display on ui    /// </summary>    public string DisplayName    {        get => _displayName;        set        {            if (_displayName != value)            {                _displayName = value;                OnPropertyChanged();            }        }    }}
3. Link class

The Link class inherits from displayable and is used primarily to save the subject name (DisplayName)displayed on the interface, as well as the path to the theme resource (Source)

class Link : Displayable{    private Uri _source = null;    /// <summary>    /// resource uri    /// </summary>    public Uri Source    {        get => _source;        set        {            _source = value;            OnPropertyChanged();        }    }}
4. Linkcollection class

Linkcollection inherits from observablecollection<link>, is bound by the ItemsSource of the ComboBox , When the elements in the collection change, the Items in theComboBox also change.

class LinkCollection : ObservableCollection<Link>{    /// <summary>    /// Initializes a new instance of the <see cref="LinkCollection"/> class.    /// </summary>    public LinkCollection()    {    }    /// <summary>    /// Initializes a new instance of the <see cref="LinkCollection"/> class that contains specified links.    /// </summary>    /// <param name="links">The links that are copied to this collection.</param>    public LinkCollection(IEnumerable<Link> links)    {        if (links == null)        {            throw new ArgumentNullException("links");        }        foreach (var link in links)        {            Add(link);        }    }}
5.ThemeManager class

The Thememanager class is used to manage the theme resources currently in use and is implemented using singleton mode (Singleton) .

Class thememanager:notifypropertychanged{#region singletion private static Thememanager _current = null;    private static readonly Object _lock = new Object (); public static Thememanager Current {get {if (_current = = null) {L  Ock (_lock) {if (_current = = null) {_current                    = new Thememanager ();        }}} return _current; }} #endregion//<summary>//Get current Theme resource dictionary///</summary>//&L t;returns></returns> private ResourceDictionary Getthemeresourcedictionary () {return (from Dictiona Ry in Application.Current.Resources.MergedDictionaries where dictionary. Contains ("Windowbackgroundcolor") Select dictionary).     FirstOrDefault (); }//<summary>/Get source URI of current theme resource//</summary>//<returns>resource uri</returns>        Private Uri Getthemesource () {var theme = Getthemeresourcedictionary ();        if (theme = = NULL) return null; Return theme.    Source; }///<summary>//Set the current theme source///</summary>//<param name= "source" >&lt        ;/param> public void Setthemesource (Uri source) {var oldtheme = getthemeresourcedictionary ();        var dictionaries = Application.Current.Resources.MergedDictionaries; Dictionaries.        ADD (new ResourceDictionary {Source = source}); if (oldtheme! = null) {dictionaries.        Remove (Oldtheme);        }}///<summary>//Current theme source//</summary> public Uri Themesource {        get = Getthemesource ();     set {if (value! = null) {           Setthemesource (value);            OnPropertyChanged (); }        }    }}
6. Settingsviewmodel Class

The Settingsviewmodel class is used to bind to the DataContext property of a ComboBox , and the Themes property is initialized in the constructor. and add our pre-defined theme resources.

Combobox.selecteditem-Settingsviewmodel.selectedtheme

Combobox.itemssource-Settingsviewmodel.themes

class SettingsViewModel : NotifyPropertyChanged{    public LinkCollection Themes { get; private set; }    private Link _selectedTheme = null;    public Link SelectedTheme    {        get => _selectedTheme;        set        {            if (value == null)                return;            if (_selectedTheme !=  value)                _selectedTheme = value;            ThemeManager.Current.ThemeSource = value.Source;            OnPropertyChanged();        }    }    public SettingsViewModel()    {        Themes = new LinkCollection()        {            new Link { DisplayName = "Light", Source = new Uri(@"Themes/Theme.Light.xaml" , UriKind.Relative) } ,            new Link { DisplayName = "Dark", Source = new Uri(@"Themes/Theme.Dark.xaml" , UriKind.Relative) }        };        SelectedTheme = Themes.FirstOrDefault(dcts => dcts.Source.Equals(ThemeManager.Current.ThemeSource));    }}
Iii. Implementation View 1.mainwindwo.xaml

The main window uses the Border control to control the background color,Border background.color points to the dynamic resource Windowbackgroundcolor , This windowbackgroundcolor is the Key to the Color that we have defined in the topic resource, because we need to change the theme dynamically, so we need to implement it with DynamicResource .

Border Background animation is a simple way to change the Color property of a SolidColorBrush .

A ComboBox control is bound to three properties:

    1. Itemssource= "{Binding Themes}", Settingsviewmodel.themes
    2. Selecteditem= "{Binding selectedtheme, mode=twoway}", Settingsviewmodel.selectedtheme
    3. Displaymemberpath= "DisplayName", SettingsViewModel.SelectedTheme.DisplayName
<window ...> <Grid> <border x:name= "Border" > <Border.Background> <solidcolorbrush x:name= "Windowbackground" color= "{DynamicResource windowbackgroundcolor}"/> </Bord Er.                    background> <Border.Resources> <storyboard x:key= "Borderbackcoloranimation" > <coloranimation storyboard.targetname= "Windowbackground" Storyboard.targetp Roperty= "Color" to= "{DynamicResource Windowbackgroundcolor}" Durat            ion= "0:0:0.5" autoreverse= "False" > </ColorAnimation> </Storyboard>  </Border.Resources> <combobox x:name= "Themecombobox" verticalalignment= "Top"                       Horizontalalignment= "left" margin= "30,10,0,0" width= "" Displaymemberpath= "DisplayName" ItemssourCe= "{Binding Themes}" selecteditem= "{Binding selectedtheme, Mode=twoway}" > </com Bobox> </Border> </Grid></Window>
2. MainWindow.cs

The backend code references the combobox.datacontext to Settingsviewmodel for data binding, while listening ThemeManager.Current.PropertyChanged event, triggering background animation

public partial class MainWindow : Window{    private Storyboard _backcolorStopyboard = null;    public MainWindow()    {        InitializeComponent();        ThemeComboBox.DataContext = new Presentation.SettingsViewModel();        Presentation.ThemeManager.Current.PropertyChanged += AppearanceManager_PropertyChanged;    }    private void AppearanceManager_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)    {        if (_backcolorStopyboard != null)        {            _backcolorStopyboard.Begin();        }    }    public override void OnApplyTemplate()    {        base.OnApplyTemplate();        if (Border != null)        {            _backcolorStopyboard = Border.Resources["BorderBackcolorAnimation"] as Storyboard;        }    }}
Iv. Summary

Key points:

    1. Binding ComboBox (view layer) ItemsSource and SelectedItem two properties to Settingsviewmodel (ViewModel layer)
    2. After the SelectedItem of the ComboBox is changed, Thememanager is triggered to replace the theme resource currently in use (themesource property)
    3. The view model needs to implement the INotifyPropertyChanged interface to notify the WPF framework that the properties have been changed
    4. Use the DynamicResource reference to change the resources to achieve theme replacement.

In addition to write more verbose, mainly to oneself back to review to see ... There is no market for WPF these days, and it is estimated that no one can see it. O (╥﹏╥) o

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.