|
1 |
| -<!-- default badges list --> |
2 |
| - |
3 |
| -[](https://supportcenter.devexpress.com/ticket/details/T845557) |
4 |
| -[](https://docs.devexpress.com/GeneralInformation/403183) |
5 |
| -[](#does-this-example-address-your-development-requirementsobjectives) |
6 |
| -<!-- default badges end --> |
7 |
| -# How to implement a Theme Switcher in Blazor applications |
| 1 | +# Implement a Theme Switcher in Blazor Applications |
8 | 2 |
|
9 |
| -This example demonstrates how to add a Theme Switcher to your application. The switcher in this example is the same as in [DevExpress Blazor Demos](https://demos.devexpress.com/blazor/). It allows users to switch between [DevExpress built-in themes](https://docs.devexpress.com/Blazor/401523/common-concepts/customize-appearance/themes) and external Bootstrap themes (the default theme and [free Bootswatch options](https://bootswatch.com/)). |
| 3 | +This example demonstrates how to add a Theme Switcher to your application. Users can switch between DevExpress Fluent and Classic themes and external Bootstrap themes. This example uses the [DxResourceManager.RegisterTheme(ITheme)](https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxResourceManager.RegisterTheme(DevExpress.Blazor.ITheme)) method to apply a theme at application startup and the [IThemeChangeService.SetTheme()](https://docs.devexpress.com/Blazor/DevExpress.Blazor.IThemeChangeService.SetTheme(DevExpress.Blazor.ITheme)) method to change the theme at runtime. |
10 | 4 |
|
11 | 5 |
|
12 | 6 | 
|
13 | 7 |
|
14 |
| -The example's solution targets .NET 8, but you can also integrate this Theme Switcher in projects that target .NET 6 and .NET 7. |
| 8 | +## Configure Available Themes |
15 | 9 |
|
16 |
| -## Add a Theme Switcher to an Application |
| 10 | +The theme switcher includes the following themes: |
| 11 | + |
| 12 | +* DevExpress Fluent (Light Blue and Dark Blue) |
| 13 | +* DevExpress Classic (Blazing Berry, Blazing Dark, Purple, and Office White) |
| 14 | +* [Bootstrap External ](https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css) |
| 15 | + |
| 16 | + |
| 17 | +Create a `Themes.cs` file and configure themes: |
17 | 18 |
|
18 |
| -Follow the steps below to add a Theme Switcher into your application: |
19 | 19 |
|
20 |
| -1. Copy this example's [ThemeSwitcher](./CS/switcher/switcher/ThemeSwitcher) folder to your project. |
21 |
| -2. In the *_Imports.razor* file, import the `switcher.ThemeSwitcher` namespace and files located in the *ThemeSwitcher* folder: |
| 20 | +1. For Classic themes, choose a theme from the built-in DevExpress Blazor [Themes](https://docs.devexpress.com/Blazor/DevExpress.Blazor.Themes) collection: |
22 | 21 |
|
23 | 22 | ```cs
|
24 |
| - @using <YourProjectName>.ThemeSwitcher |
25 |
| - @using switcher.ThemeSwitcher |
| 23 | + public static readonly ITheme BlazingBerry = Themes.BlazingBerry; |
| 24 | + public static readonly ITheme BlazingDark = Themes.BlazingDark; |
| 25 | + public static readonly ITheme Purple = Themes.Purple; |
| 26 | + public static readonly ITheme OfficeWhite = Themes.OfficeWhite; |
26 | 27 | ```
|
| 28 | +1. For Fluent themes, call the [Clone()](https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxThemeBase-1.Clone(System.Action--0-)) method to add theme stylesheets and change theme mode: |
27 | 29 |
|
28 |
| -3. Copy this example's [switcher-resources](./CS/switcher/switcher/wwwroot/switcher-resources) folder to your application's *wwwroot* folder. The *switcher-resources* folder has the following structure: |
| 30 | + ```cs |
| 31 | + public static readonly ITheme FluentLight = Themes.Fluent.Clone(props => { |
| 32 | + props.AddFilePaths("css/theme-fluent.css"); |
| 33 | + }); |
| 34 | + public static readonly ITheme FluentDark = Themes.Fluent.Clone(props => { |
| 35 | + props.Mode = ThemeMode.Dark; |
| 36 | + props.AddFilePaths("css/theme-fluent.css"); |
| 37 | + }); |
| 38 | + ``` |
| 39 | +1. For Bootstrap themes, call the [Clone()](https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxThemeBase-1.Clone(System.Action--0-)) method to add a Bootstrap theme stylesheet. Use the same approach if you want to apply your own stylesheets. |
29 | 40 |
|
30 |
| - * **css/themes** |
31 |
| - Includes nested folders whose names correspond to external Bootstrap themes. Each nested folder stores an external theme's stylesheet (*bootstrap.min.css*). Note that built-in DevExpress themes are added to the application with DevExpress Blazor components and stored separately in the **DevExpress.Blazor.Themes** NuGet package. |
| 41 | + ```cs |
| 42 | + public static readonly ITheme BootstrapDefault = Themes.BootstrapExternal.Clone(props => { |
| 43 | + props.Name = "Bootstrap"; |
| 44 | + props. AddFilePaths( "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"); |
| 45 | + props.AddFilePaths("css/theme-bs.css"); |
| 46 | + }); |
| 47 | + ``` |
| 48 | +1. Create a list of themes: |
32 | 49 |
|
33 |
| - * **css/themes.css** |
34 |
| - Contains CSS rules used to draw colored squares for each theme in the Theme Switcher. |
35 |
| - * **css/theme-switcher.css** |
36 |
| - Contains CSS rules that define the Theme Switcher's appearance and behavior. |
37 |
| - * **theme-controller.js** |
38 |
| - Contains functions that add and remove links to theme stylesheets. |
39 |
| - * **theme.svg** |
40 |
| - An icon displayed in the Theme Switcher. |
41 |
| - |
42 |
| -4. In the layout file, use the [HttpContextAccessor](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httpcontextaccessor?view=aspnetcore-8.0) class to obtain the current theme from cookies: |
43 |
| - ```html |
44 |
| - @using Microsoft.AspNetCore.Mvc.ViewFeatures |
45 |
| - @inject IFileVersionProvider FileVersionProvider |
46 |
| - @inject ThemeService Themes |
47 |
| - @inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor |
48 |
| - @{ |
49 |
| - string? InitialThemeName = HttpContextAccessor.HttpContext?.Request.Cookies["ActiveTheme"]; |
50 |
| - Themes.SetActiveThemeByName(InitialThemeName); |
51 |
| - var bsTheme = Themes.GetBootstrapThemeCssUrl(Themes.ActiveTheme); |
52 |
| - var dxTheme = Themes.GetThemeCssUrl(Themes.ActiveTheme); |
53 |
| - var hlTheme = Themes.GetHighlightJSThemeCssUrl(Themes.ActiveTheme); |
54 |
| - |
55 |
| - string AppendVersion(string path) => FileVersionProvider.AddFileVersionToPath("/", path); |
| 50 | + |
| 51 | + ```cs |
| 52 | + public enum MyTheme { |
| 53 | + Fluent_Light, |
| 54 | + Fluent_Dark, |
| 55 | + |
| 56 | + Blazing_Berry, |
| 57 | + Blazing_Dark, |
| 58 | + Purple, |
| 59 | + Office_White, |
| 60 | + |
| 61 | + Bootstrap |
56 | 62 | }
|
57 | 63 | ```
|
58 |
| -5. In the `head` section of the layout file, replace a link to a theme stylesheet with the following code: |
59 |
| - ```html |
60 |
| - <head> |
61 |
| - @if (!string.IsNullOrEmpty(bsTheme)) { |
62 |
| - <link rel="stylesheet" href="@AppendVersion(bsTheme)" bs-theme-link /> |
63 |
| - } |
64 |
| - @if (!string.IsNullOrEmpty(dxTheme)) { |
65 |
| - <link rel="stylesheet" href="@AppendVersion(dxTheme)" dx-theme-link /> |
66 |
| - } |
67 |
| - @if (!string.IsNullOrEmpty(hlTheme)) { |
68 |
| - <link rel="stylesheet" href="@hlTheme" hl-theme-link /> |
69 |
| - } |
70 |
| - </head> |
71 |
| - ``` |
72 |
| -6. Register the Theme Switcher's styles in the `head` section of the layout file: |
73 |
| - ```html |
74 |
| - <head> |
75 |
| - <link href="switcher-resources/css/theme-switcher.css" rel="stylesheet" /> |
76 |
| - <link href="switcher-resources/css/themes.css" rel="stylesheet" /> |
77 |
| - @* ... *@ |
78 |
| - </head> |
79 |
| - ``` |
80 |
| -7. Add the following `div` element to the `body` section of the layout file: |
81 |
| - ```html |
82 |
| - <body> |
83 |
| - <div id="switcher-container" data-permanent></div> |
84 |
| - @* ... *@ |
85 |
| - </body> |
| 64 | + |
| 65 | +## Add a Theme Switcher to an Application |
| 66 | + |
| 67 | +Follow the steps below to add a Theme Switcher to your application: |
| 68 | + |
| 69 | +1. Copy this example's [ThemeSwitcher](./CS/switcher/switcher/Components/ThemeSwitcher) folder to your project. |
| 70 | + |
| 71 | +2. Copy the example's [switcher-resources](./CS/switcher/switcher/wwwroot/switcher-resources) folder to your application's *wwwroot* folder. The *switcher-resources* folder has the following structure: |
| 72 | + |
| 73 | + * **js/cookies-manager.js** |
| 74 | + Contains a function that stores the theme in a cookie variable. |
| 75 | + * **theme-switcher.css** |
| 76 | + Contains CSS rules that define the Theme Switcher's appearance and behavior. |
| 77 | + |
| 78 | +3. Add the following services to your application (copy the corresponding files): |
| 79 | + |
| 80 | + * [ThemeService.cs](./CS/switcher/switcher/Services/ThemesService.cs) |
| 81 | + Implements [IThemeChangeService](https://docs.devexpress.com/Blazor/DevExpress.Blazor.IThemeChangeService) to switch themes at runtime and uses the [SetTheme()](https://docs.devexpress.com/Blazor/DevExpress.Blazor.IThemeChangeService.SetTheme(DevExpress.Blazor.ITheme)) method to apply the selected theme. |
| 82 | + * [Themes.cs](./CS/switcher/switcher/Services/Themes.cs) |
| 83 | + Creates a list of themes for the theme switcher using the built-in DevExpres Blazor [Themes](https://docs.devexpress.com/Blazor/DevExpress.Blazor.Themes) collection (for Classic themes) and the [Clone()](https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxThemeBase-1.Clone(System.Action--0-)) method for Fluent and Bootstrap themes. |
| 84 | + * [CookiesService.cs](./CS/switcher/switcher/Services/CookiesService.cs) |
| 85 | + Manages cookies. |
| 86 | + |
| 87 | +2. In the [_Imports.razor](./CS/switcher/switcher/Components/_Imports.razor) file, import `{ProjectName}.Components.ThemeSwitcher` and `{ProjectName}.Services` namespaces: |
| 88 | + |
| 89 | + ```cs |
| 90 | + @using {ProjectName}.Components.ThemeSwitcher |
| 91 | + @using {ProjectName}.Services |
86 | 92 | ```
|
87 |
| -8. Register `Mvc` and `ThemeService` in the `Program.cs` file: |
| 93 | + |
| 94 | +5. Register `ThemesService` and `CookiesService` in the [Program.cs](./CS/switcher/switcher/Program.cs#L13-L16) file. Ensure that this file also contains `Mvc` and `HttpContextAccessor` services: |
| 95 | + |
88 | 96 | ```cs
|
89 | 97 | builder.Services.AddMvc();
|
90 |
| - builder.Services.AddScoped<ThemeService>(); |
| 98 | + builder.Services.AddHttpContextAccessor(); |
| 99 | + builder.Services.AddScoped<ThemesService>(); |
| 100 | + builder.Services.AddTransient<CookiesService>(); |
91 | 101 | ```
|
92 |
| -9. Declare the Theme Switcher component in the *MainLayout.razor* file: |
93 |
| - ```razor |
94 |
| - <ThemeSwitcher /> |
95 |
| - ``` |
96 |
| -10. *For .NET 6 and .NET 7 applications.* Remove the `@rendermode InteractiveServer` directive from [ThemeSwitcher.razor](./CS/switcher/switcher/ThemeSwitcher/ThemeSwitcher.razor#L2), [ThemeSwitcherContainer.razor](./CS/switcher/switcher/ThemeSwitcher/ThemeSwitcherContainer.razor#L4), and [ThemeSwitcherItem.razor](./CS/switcher/switcher/ThemeSwitcher/ThemeSwitcherItem.razor#L2) files. |
97 | 102 |
|
98 |
| -## Add Themes to the Theme Switcher |
| 103 | +6. Add the following code to the [App.razor](./CS/switcher/switcher/Components/App.razor) file: |
99 | 104 |
|
100 |
| -Follow the steps below to add an external Bootstrap theme to the Theme Switcher: |
| 105 | + * Inject services with the [[Inject] attribute](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.injectattribute): |
101 | 106 |
|
102 |
| -1. In the **wwwroot/css/themes** folder, create a new folder for this theme. The folder and theme names should match. |
| 107 | + ```html |
| 108 | + @inject IHttpContextAccessor HttpContextAccessor |
| 109 | + @inject ThemesService ThemesService |
| 110 | + ``` |
| 111 | + |
| 112 | + * Add script and stylesheet links to the file's `<head>` section and call the [DxResourceManager.RegisterTheme(ITheme)](https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxResourceManager.RegisterTheme(DevExpress.Blazor.ITheme)) method to apply a theme on application startup: |
103 | 113 |
|
104 |
| -2. Add the theme's stylesheet (*bootstrap.min.css*) to the newly created folder. |
| 114 | + ```html |
| 115 | + <head> |
| 116 | + @* ... *@ |
| 117 | + <script src=@AppendVersion("switcher-resources/js/cookies-manager.js")></script> |
| 118 | + <link href=@AppendVersion("switcher-resources/theme-switcher.css") rel="stylesheet" /> |
105 | 119 |
|
106 |
| -3. Add the following CSS rule to the *wwwroot/css/themes.css* file: |
| 120 | + @DxResourceManager.RegisterTheme(InitialTheme) |
| 121 | + @* ... *@ |
| 122 | + </head> |
| 123 | + ``` |
107 | 124 |
|
108 |
| - ```css |
109 |
| - .blazor-themes a.<your-theme-name>:before { |
110 |
| - background: <theme-main-color>; |
111 |
| - } |
112 |
| - ``` |
| 125 | + * Obtain the theme from cookies during component initialization: |
113 | 126 |
|
114 |
| -4. In *ThemeService.cs*, add the theme name to the **Bootstrap Themes** theme set: |
| 127 | + ```razor |
| 128 | + @code { |
| 129 | + private ITheme InitialTheme; |
| 130 | + protected override void OnInitialized() { |
| 131 | + InitialTheme = ThemesService.GetThemeFromCookies(HttpContextAccessor); |
| 132 | + } |
| 133 | + } |
| 134 | + ``` |
115 | 135 |
|
| 136 | +7. Declare the Theme Switcher component in the [MainLayout.razor](./CS/switcher/switcher/Components/Layout/MainLayout.razor#L22) file: |
116 | 137 |
|
117 |
| - ```cs |
118 |
| - private static List<ThemeSet> CreateSets(ThemeService config) { |
119 |
| - return new List<ThemeSet>() { |
120 |
| - new ThemeSet("DevExpress Themes", NEW_BLAZOR_THEMES), |
121 |
| - new ThemeSet("Bootstrap Themes", "<your-theme-name>", "default", "default-dark", "cerulean") |
122 |
| - }; |
123 |
| - } |
124 |
| - ``` |
| 138 | + ```razor |
| 139 | + <Drawer> |
| 140 | + @* ... *@ |
| 141 | + <ThemeSwitcher /> |
| 142 | + @* ... *@ |
| 143 | + </Drawer> |
| 144 | + ``` |
125 | 145 |
|
126 |
| -## Remove Themes from the Theme Switcher |
| 146 | +## Add Stylesheets to a Theme (Apply Styles to Non-DevExpress UI Elements) |
127 | 147 |
|
128 |
| -Follow the steps below to remove a built-in DevExpress or external Bootstrap theme from the Theme Switcher: |
129 | 148 |
|
130 |
| -1. Open *ThemeService.cs* and remove the theme name from the **DevExpress Themes** or **Bootstrap Themes** theme set: |
| 149 | +Our DevExpress Blazor themes affect DevExpress components only. To apply theme-specific styles to non-DevExpress elements or the entire application, add external stylesheets to the theme using its `AddFilePaths()` method: |
131 | 150 |
|
132 |
| - ```cs |
133 |
| - private static List<ThemeSet> CreateSets(ThemeService config) { |
134 |
| - return new List<ThemeSet>() { |
135 |
| - new ThemeSet("DevExpress Themes", NEW_BLAZOR_THEMES), |
136 |
| - new ThemeSet("Bootstrap Themes", /*"<your-theme-name>"*/, "default", "default-dark", "cerulean") |
137 |
| - }; |
138 |
| - } |
139 |
| - ``` |
140 | 151 |
|
141 |
| -2. Remove the CSS rule that corresponds to this theme from the *wwwroot/css/themes.css* file. |
| 152 | +> Bootstrap themes require external theme-specific stylesheets. Once you register a Bootstrap theme, call the `Clone()` method and add the stylesheet using theme properties. |
142 | 153 |
|
143 |
| - ```css |
144 |
| - /* .blazor-themes a.<your-theme-name>:before { |
145 |
| - background: <theme-main-color>; |
146 |
| - }*/ |
147 |
| - ``` |
148 | 154 |
|
149 |
| -3. *For an external Bootstrap theme.* Delete the *wwwroot/css/themes/\<your-theme-name\>* folder. |
| 155 | +```cs |
| 156 | +public static readonly ITheme BootstrapDefault = Themes.BootstrapExternal.Clone(props => { |
| 157 | + props.Name = "Bootstrap"; |
| 158 | + // Links a Bootstrap theme stylesheet |
| 159 | + props. AddFilePaths( "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"); |
| 160 | + // Links a custom stylesheet |
| 161 | + props.AddFilePaths("css/theme-bs.css"); |
| 162 | +}); |
| 163 | +``` |
| 164 | + |
| 165 | +## Change Bootstrap Theme Color Modes |
| 166 | + |
| 167 | +If you want to use dark Bootstrap themes, implement custom logic that applies a `data-bs-theme` attribute to the root <html> element: |
| 168 | + |
| 169 | +* `data-bs-theme="light"` for light themes |
| 170 | +* `data-bs-theme="dark"` for dark themes |
| 171 | + |
| 172 | +Refer to the following article for more information: [Color Modes](https://getbootstrap.com/docs/5.3/customize/color-modes/). |
150 | 173 |
|
151 | 174 | ## Files to Review
|
152 | 175 |
|
153 |
| -* [ThemeSwitcher](./CS/switcher/switcher/ThemeSwitcher) (folder) |
| 176 | +* [ThemeSwitcher](./CS/switcher/switcher/Components/ThemeSwitcher) (folder) |
154 | 177 | * [switcher-resources](./CS/switcher/switcher/wwwroot/switcher-resources) (folder)
|
155 |
| -* [App.razor](./CS/switcher/switcher/App.razor) |
156 |
| -* [MainLayout.razor](./CS/switcher/switcher/Layout/MainLayout.razor) |
| 178 | +* [Services](./CS/switcher/switcher/Services) (folder) |
| 179 | +* [App.razor](./CS/switcher/switcher/Components/App.razor) |
| 180 | +* [MainLayout.razor](./CS/switcher/switcher/Components/Layout/MainLayout.razor) |
157 | 181 | * [Program.cs](./CS/switcher/switcher/Program.cs)
|
158 | 182 |
|
159 | 183 | ## Documentation
|
160 | 184 |
|
161 | 185 | * [Themes](https://docs.devexpress.com/Blazor/401523/common-concepts/themes)
|
162 | 186 | <!-- feedback -->
|
163 |
| -## Does this example address your development requirements/objectives? |
164 |
| - |
165 |
| -[<img src="https://www.devexpress.com/support/examples/i/yes-button.svg"/>](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=yes) [<img src="https://www.devexpress.com/support/examples/i/no-button.svg"/>](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=no) |
166 |
| -
|
| 187 | +## Does this example address your development requirements/objectives? |
| 188 | + |
| 189 | +[<img src="https://www.devexpress.com/support/examples/i/yes-button.svg"/>](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=yes) [<img src="https://www.devexpress.com/support/examples/i/no-button.svg"/>](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=no) |
| 190 | +
|
167 | 191 | (you will be redirected to DevExpress.com to submit your response)
|
168 | 192 | <!-- feedback end -->
|
0 commit comments