diff --git a/Readme.md b/Readme.md index 615859e..e6349c7 100644 --- a/Readme.md +++ b/Readme.md @@ -1,168 +1,192 @@ - -![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/227836631/25.1.3%2B) -[![](https://img.shields.io/badge/Open_in_DevExpress_Support_Center-FF7200?style=flat-square&logo=DevExpress&logoColor=white)](https://supportcenter.devexpress.com/ticket/details/T845557) -[![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183) -[![](https://img.shields.io/badge/💬_Leave_Feedback-feecdd?style=flat-square)](#does-this-example-address-your-development-requirementsobjectives) - -# How to implement a Theme Switcher in Blazor applications +# Implement a Theme Switcher in Blazor Applications -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/)). +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. ![Blazor - Theme Switcher](images/blazor-theme-switcher.png) -The example's solution targets .NET 8, but you can also integrate this Theme Switcher in projects that target .NET 6 and .NET 7. +## Configure Available Themes -## Add a Theme Switcher to an Application +The theme switcher includes the following themes: + +* DevExpress Fluent (Light Blue and Dark Blue) +* DevExpress Classic (Blazing Berry, Blazing Dark, Purple, and Office White) +* [Bootstrap External](https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css) + + +Create a `Themes.cs` file and configure themes: -Follow the steps below to add a Theme Switcher into your application: -1. Copy this example's [ThemeSwitcher](./CS/switcher/switcher/ThemeSwitcher) folder to your project. -2. In the *_Imports.razor* file, import the `switcher.ThemeSwitcher` namespace and files located in the *ThemeSwitcher* folder: +1. For Classic themes, choose a theme from the built-in DevExpress Blazor [Themes](https://docs.devexpress.com/Blazor/DevExpress.Blazor.Themes) collection: ```cs - @using .ThemeSwitcher - @using switcher.ThemeSwitcher + public static readonly ITheme BlazingBerry = Themes.BlazingBerry; + public static readonly ITheme BlazingDark = Themes.BlazingDark; + public static readonly ITheme Purple = Themes.Purple; + public static readonly ITheme OfficeWhite = Themes.OfficeWhite; ``` +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: -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: + ```cs + public static readonly ITheme FluentLight = Themes.Fluent.Clone(props => { + props.AddFilePaths("css/theme-fluent.css"); + }); + public static readonly ITheme FluentDark = Themes.Fluent.Clone(props => { + props.Mode = ThemeMode.Dark; + props.AddFilePaths("css/theme-fluent.css"); + }); + ``` +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. - * **css/themes** - 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. + ```cs + public static readonly ITheme BootstrapDefault = Themes.BootstrapExternal.Clone(props => { + props.Name = "Bootstrap"; + props.AddFilePaths("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"); + props.AddFilePaths("css/theme-bs.css"); + }); + ``` +1. Create a list of themes: - * **css/themes.css** - Contains CSS rules used to draw colored squares for each theme in the Theme Switcher. - * **css/theme-switcher.css** - Contains CSS rules that define the Theme Switcher's appearance and behavior. - * **theme-controller.js** - Contains functions that add and remove links to theme stylesheets. - * **theme.svg** - An icon displayed in the Theme Switcher. - -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: - ```html - @using Microsoft.AspNetCore.Mvc.ViewFeatures - @inject IFileVersionProvider FileVersionProvider - @inject ThemeService Themes - @inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor - @{ - string? InitialThemeName = HttpContextAccessor.HttpContext?.Request.Cookies["ActiveTheme"]; - Themes.SetActiveThemeByName(InitialThemeName); - var bsTheme = Themes.GetBootstrapThemeCssUrl(Themes.ActiveTheme); - var dxTheme = Themes.GetThemeCssUrl(Themes.ActiveTheme); - var hlTheme = Themes.GetHighlightJSThemeCssUrl(Themes.ActiveTheme); - - string AppendVersion(string path) => FileVersionProvider.AddFileVersionToPath("/", path); + + ```cs + public enum MyTheme { + Fluent_Light, + Fluent_Dark, + + Blazing_Berry, + Blazing_Dark, + Purple, + Office_White, + + Bootstrap } ``` -5. In the `head` section of the layout file, replace a link to a theme stylesheet with the following code: - ```html - - @if (!string.IsNullOrEmpty(bsTheme)) { - - } - @if (!string.IsNullOrEmpty(dxTheme)) { - - } - @if (!string.IsNullOrEmpty(hlTheme)) { - - } - - ``` -6. Register the Theme Switcher's styles in the `head` section of the layout file: - ```html - - - - @* ... *@ - - ``` -7. Add the following `div` element to the `body` section of the layout file: - ```html - -
- @* ... *@ - + +## Add a Theme Switcher to an Application + +Follow the steps below to add a Theme Switcher to your application: + +1. Copy this example's [ThemeSwitcher](./CS/switcher/switcher/Components/ThemeSwitcher) folder to your project. + +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: + + * **js/cookies-manager.js** + Contains a function that stores the theme in a cookie variable. + * **theme-switcher.css** + Contains CSS rules that define the Theme Switcher's appearance and behavior. + +3. Add the following services to your application (copy the corresponding files): + + * [ThemeService.cs](./CS/switcher/switcher/Services/ThemesService.cs) + 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. + * [Themes.cs](./CS/switcher/switcher/Services/Themes.cs) + 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. + * [CookiesService.cs](./CS/switcher/switcher/Services/CookiesService.cs) + Manages cookies. + +2. In the [_Imports.razor](./CS/switcher/switcher/Components/_Imports.razor) file, import `{ProjectName}.Components.ThemeSwitcher` and `{ProjectName}.Services` namespaces: + + ```cs + @using {ProjectName}.Components.ThemeSwitcher + @using {ProjectName}.Services ``` -8. Register `Mvc` and `ThemeService` in the `Program.cs` file: + +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: + ```cs builder.Services.AddMvc(); - builder.Services.AddScoped(); + builder.Services.AddHttpContextAccessor(); + builder.Services.AddScoped(); + builder.Services.AddTransient(); ``` -9. Declare the Theme Switcher component in the *MainLayout.razor* file: - ```razor - - ``` -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. -## Add Themes to the Theme Switcher +6. Add the following code to the [App.razor](./CS/switcher/switcher/Components/App.razor) file: -Follow the steps below to add an external Bootstrap theme to the Theme Switcher: + * Inject services with the [[Inject] attribute](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.injectattribute): -1. In the **wwwroot/css/themes** folder, create a new folder for this theme. The folder and theme names should match. + ```html + @inject IHttpContextAccessor HttpContextAccessor + @inject ThemesService ThemesService + ``` + + * Add script and stylesheet links to the file's `` 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: -2. Add the theme's stylesheet (*bootstrap.min.css*) to the newly created folder. + ```html + + @* ... *@ + + -3. Add the following CSS rule to the *wwwroot/css/themes.css* file: + @DxResourceManager.RegisterTheme(InitialTheme) + @* ... *@ + + ``` - ```css - .blazor-themes a.:before { - background: ; - } - ``` + * Obtain the theme from cookies during component initialization: -4. In *ThemeService.cs*, add the theme name to the **Bootstrap Themes** theme set: + ```razor + @code { + private ITheme InitialTheme; + protected override void OnInitialized() { + InitialTheme = ThemesService.GetThemeFromCookies(HttpContextAccessor); + } + } + ``` +7. Declare the Theme Switcher component in the [MainLayout.razor](./CS/switcher/switcher/Components/Layout/MainLayout.razor#L22) file: - ```cs - private static List CreateSets(ThemeService config) { - return new List() { - new ThemeSet("DevExpress Themes", NEW_BLAZOR_THEMES), - new ThemeSet("Bootstrap Themes", "", "default", "default-dark", "cerulean") - }; - } - ``` + ```razor + + @* ... *@ + + @* ... *@ + + ``` -## Remove Themes from the Theme Switcher +## Add Stylesheets to a Theme (Apply Styles to Non-DevExpress UI Elements) -Follow the steps below to remove a built-in DevExpress or external Bootstrap theme from the Theme Switcher: -1. Open *ThemeService.cs* and remove the theme name from the **DevExpress Themes** or **Bootstrap Themes** theme set: +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: - ```cs - private static List CreateSets(ThemeService config) { - return new List() { - new ThemeSet("DevExpress Themes", NEW_BLAZOR_THEMES), - new ThemeSet("Bootstrap Themes", /*""*/, "default", "default-dark", "cerulean") - }; - } - ``` -2. Remove the CSS rule that corresponds to this theme from the *wwwroot/css/themes.css* file. +> Bootstrap themes require external theme-specific stylesheets. Once you register a Bootstrap theme, call the `Clone()` method and add the stylesheet using theme properties. - ```css - /* .blazor-themes a.:before { - background: ; - }*/ - ``` -3. *For an external Bootstrap theme.* Delete the *wwwroot/css/themes/\* folder. +```cs +public static readonly ITheme BootstrapDefault = Themes.BootstrapExternal.Clone(props => { + props.Name = "Bootstrap"; + // Links a Bootstrap theme stylesheet + props.AddFilePaths("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"); + // Links a custom stylesheet + props.AddFilePaths("css/theme-bs.css"); +}); +``` + +## Change Bootstrap Theme Color Modes + +If you want to use dark Bootstrap themes, implement custom logic that applies a `data-bs-theme` attribute to the root element: + +* `data-bs-theme="light"` for light themes +* `data-bs-theme="dark"` for dark themes + +Refer to the following article for more information: [Color Modes](https://getbootstrap.com/docs/5.3/customize/color-modes/). ## Files to Review -* [ThemeSwitcher](./CS/switcher/switcher/ThemeSwitcher) (folder) +* [ThemeSwitcher](./CS/switcher/switcher/Components/ThemeSwitcher) (folder) * [switcher-resources](./CS/switcher/switcher/wwwroot/switcher-resources) (folder) -* [App.razor](./CS/switcher/switcher/App.razor) -* [MainLayout.razor](./CS/switcher/switcher/Layout/MainLayout.razor) +* [Services](./CS/switcher/switcher/Services) (folder) +* [App.razor](./CS/switcher/switcher/Components/App.razor) +* [MainLayout.razor](./CS/switcher/switcher/Components/Layout/MainLayout.razor) * [Program.cs](./CS/switcher/switcher/Program.cs) ## Documentation * [Themes](https://docs.devexpress.com/Blazor/401523/common-concepts/themes) -## Does this example address your development requirements/objectives? - -[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=no) - +## Does this example address your development requirements/objectives? + +[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=blazor-theme-switcher&~~~was_helpful=no) + (you will be redirected to DevExpress.com to submit your response) diff --git a/images/blazor-theme-switcher.png b/images/blazor-theme-switcher.png index ded223f..ab931f9 100644 Binary files a/images/blazor-theme-switcher.png and b/images/blazor-theme-switcher.png differ