Skip to main content
A newer version of this page is available. .
All docs
V21.2

How To: Create a Custom Blazor Application Template

  • 4 minutes to read

This article explains how to change the built-in navigation system (uses a DxTreeView component) with a DxMenu component:

Custom Window Template

To do this, we create and use a custom Application Window Template.

View Example: XAF Blazor - How to create a custom template

Implementation Details

  1. Create the “Templates” folder in the MySolution.Blazor.Server project. This folder is used to store a custom template in this example.
  2. Create a custom Action control that displays a custom navigation component. To do this, right-click the “Templates” folder, select Add | Class…, and set a new class name to “CustomShowNavigationItemActionControl.cs”. Implement the ISingleChoiceActionControl interface in the newly created class. Set the wrapped Action’s ActionId property to “ShowNavigationItem”.

    // File: CustomShowNavigationItemActionControl.cs
    using DevExpress.ExpressApp.Actions;
    using DevExpress.ExpressApp.Templates;
    using DevExpress.ExpressApp.Templates.ActionControls;
    using Microsoft.AspNetCore.Components;
    using System;
    using System.Collections.Generic;
    
    namespace XafBlazorCustomTemplateSample.Blazor.Server.Templates {
        public class CustomShowNavigationItemActionControl : ISingleChoiceActionControl {
            private ChoiceActionItemCollection choiceActionItems;
            private EventHandler<SingleChoiceActionControlExecuteEventArgs> execute;
            string IActionControl.ActionId => "ShowNavigationItem";
            object IActionControl.NativeControl => this;
            public IEnumerable<ChoiceActionItem> Items => choiceActionItems;
            // The CustomShowNavigationItemActionControlComponent is added in the next step.
            public RenderFragment GetComponentContent(RenderFragment titleTemplate) => CustomShowNavigationItemActionControlComponent.Create(titleTemplate, this);
            void ISingleChoiceActionControl.SetChoiceActionItems(ChoiceActionItemCollection choiceActionItems) => this.choiceActionItems = choiceActionItems;
            public void DoExecute(ChoiceActionItem choiceActionItem) {
                execute?.Invoke(this, choiceActionItem == null ? new SingleChoiceActionControlExecuteEventArgs() : new SingleChoiceActionControlExecuteEventArgs(choiceActionItem));
            }
            event EventHandler<SingleChoiceActionControlExecuteEventArgs> ISingleChoiceActionControl.Execute {
                add => execute += value;
                remove => execute -= value;
            }
    
            void IActionControl.SetCaption(string caption) { }
            void IActionControl.SetConfirmationMessage(string confirmationMessage) { }
            void IActionControl.SetEnabled(bool enabled) { }
            void IActionControl.SetImage(string imageName) { }
            void IActionControl.SetPaintStyle(ActionItemPaintStyle paintStyle) { }
            void ISingleChoiceActionControl.SetSelectedItem(ChoiceActionItem selectedItem) { }
            void IActionControl.SetShortcut(string shortcutString) { }
            void ISingleChoiceActionControl.SetShowItemsOnClick(bool value) { }
            void IActionControl.SetToolTip(string toolTip) { }
            void ISingleChoiceActionControl.Update(IDictionary<object, ChoiceActionItemChangesType> itemsChangedInfo) { }
            void IActionControl.SetVisible(bool visible) { }
            event EventHandler IActionControl.NativeControlDisposed { add { } remove { } }
        }
    }
    
  3. Create a custom Razor component that renders a DxMenu component. Right-click the Templates folder, select Add | Razor Component, and set its name to CustomShowNavigationItemActionControlComponent.razor. Replace the file content with the following code:

    @* File: CustomShowNavigationItemActionControlComponent.razor *@
    @using DevExpress.ExpressApp.Actions
    
    <DxMenu Data="@ActionControl.Items" ItemClick="@OnItemClick" HamburgerButtonPosition="MenuHamburgerButtonPosition.Left" CollapseItemsToHamburgerMenu="true">
        <TitleTemplate>
            @TitleTemplate
        </TitleTemplate>
        <DataMappings>
            <DxMenuDataMapping Text="@nameof(ChoiceActionItem.Caption)" Children="@nameof(ChoiceActionItem.Items)" />
        </DataMappings>
    </DxMenu>
    
    @code {
        public static RenderFragment Create(RenderFragment titleTemplate, CustomShowNavigationItemActionControl actionControl) =>
            @<CustomShowNavigationItemActionControlComponent TitleTemplate="@titleTemplate" ActionControl="@actionControl" />;
        [Parameter]
        public RenderFragment TitleTemplate { get; set; }
        [Parameter]
        public CustomShowNavigationItemActionControl ActionControl { get; set; }
        private void OnItemClick(MenuItemClickEventArgs e) => ActionControl.DoExecute((ChoiceActionItem)e.ItemInfo.Data);
    }
    
  4. Create a new Application Window Template. Right-click the Templates folder and select Add DevExpress Item | New Item… to open the Template Gallery. In the XAF ASP.NET Core Blazor Templates section, select the Application Window Template item, rename it “CustomApplicationWindowTemplate”, and click Add Item.

  5. Replace the built-in ShowNavigationItemActionControl with the newly created CustomShowNavigationItemActionControl in the newly created CustomApplicationWindowTemplate class:

    // File: CustomApplicationWindowTemplate.cs
    using DevExpress.ExpressApp;
    using DevExpress.ExpressApp.Blazor.Components.Models;
    using DevExpress.ExpressApp.Blazor.Templates;
    using DevExpress.ExpressApp.Blazor.Templates.Navigation.ActionControls;
    using DevExpress.ExpressApp.Blazor.Templates.Security.ActionControls;
    using DevExpress.ExpressApp.Blazor.Templates.Toolbar.ActionControls;
    using DevExpress.ExpressApp.Templates;
    using DevExpress.ExpressApp.Templates.ActionControls;
    using DevExpress.Persistent.Base;
    using Microsoft.AspNetCore.Components;
    using System.Collections.Generic;
    
    namespace XafBlazorCustomTemplateSample.Blazor.Server.Templates {
        public class CustomApplicationWindowTemplate : WindowTemplateBase, ISupportActionsToolbarVisibility, ISelectionDependencyToolbar {
            public CustomApplicationWindowTemplate() {
                NavigateBackActionControl = new NavigateBackActionControl();
                AddActionControl(NavigateBackActionControl);
                AccountComponent = new AccountComponentAdapter();
                AddActionControls(AccountComponent.ActionControls);
                ShowNavigationItemActionControl = new CustomShowNavigationItemActionControl();
                AddActionControl(ShowNavigationItemActionControl);
    
                IsActionsToolbarVisible = true;
                Toolbar = new DxToolbarAdapter(new DxToolbarModel());
                Toolbar.AddActionContainer(nameof(PredefinedCategory.ObjectsCreation));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Save));
                Toolbar.AddActionContainer("Close");
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Export));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.UndoRedo));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Edit));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.RecordEdit));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.RecordsNavigation));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.View));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Reports));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Search));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Filters));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.FullTextSearch));
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Tools));
                Toolbar.AddActionContainer("Diagnostic");
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Unspecified));
            }
            protected override IEnumerable<IActionControlContainer> GetActionControlContainers() => Toolbar.ActionContainers;
            protected override RenderFragment CreateComponent() => CustomApplicationWindowTemplateComponent.Create(this);
            protected override void BeginUpdate() {
                base.BeginUpdate();
                ((ISupportUpdate)Toolbar).BeginUpdate();
            }
            protected override void EndUpdate() {
                ((ISupportUpdate)Toolbar).EndUpdate();
                base.EndUpdate();
            }
            public bool IsActionsToolbarVisible { get; private set; }
            public NavigateBackActionControl NavigateBackActionControl { get; }
            public AccountComponentAdapter AccountComponent { get; }
            public CustomShowNavigationItemActionControl ShowNavigationItemActionControl { get; }
            public DxToolbarAdapter Toolbar { get; }
            public string AboutInfoString { get; set; }
            void ISupportActionsToolbarVisibility.SetVisible(bool isVisible) => IsActionsToolbarVisible = isVisible;
        }
    }
    
  6. Modify the CustomApplicationWindowTemplateComponent.razor component according to your business requirements. For example, you can edit it as follows:

    @* File: CustomApplicationWindowTemplateComponent.razor *@
    @using DevExpress.ExpressApp
    @using DevExpress.ExpressApp.Blazor
    @using DevExpress.ExpressApp.Blazor.Components
    @using DevExpress.ExpressApp.Blazor.Templates
    @using Microsoft.JSInterop
    
    @inherits FrameTemplateComponentBase<CustomApplicationWindowTemplate>
    
    <div class="app h-100 d-flex flex-column">
        <div class="d-flex shadow-sm">
            @FrameTemplate.ShowNavigationItemActionControl.GetComponentContent(@<ViewCaptionComponent WindowCaption="@FrameTemplate" />         )
            @FrameTemplate.AccountComponent.GetComponentContent()
            <SettingsComponent />
        </div>
        <div class="main xaf-flex-auto overflow-hidden d-flex flex-column">
            <SizeModeContainer>
                @if(FrameTemplate.IsActionsToolbarVisible && @FrameTemplate.Toolbar.ContainsVisibleActionControl()) {
                    <div class="main-toolbar py-3 px-2 px-sm-3">@FrameTemplate.Toolbar.GetComponentContent()</div>
                }
                <div class="main-content xaf-flex-auto overflow-auto pb-3 px-2 px-sm-3 d-flex flex-column">
                    <div class="xaf-flex-auto">
                        <ViewSiteComponent View="@FrameTemplate.View" />
                    </div>
                    <div class="about-info text-muted mt-3">
                        @((MarkupString)FrameTemplate.AboutInfoString)
                    </div>
                </div>
            </SizeModeContainer>
        </div>
    </div>
    
    @code {
        public static RenderFragment Create(CustomApplicationWindowTemplate applicationWindowTemplate) =>
        @<CustomApplicationWindowTemplateComponent FrameTemplate="@applicationWindowTemplate" />;
    }
    
  7. Override the CreateDefaultTemplate method in the MySolution.Blazor.Server/BlazorApplication.cs file to use an instance of the new template:

    // File: BlazorApplication.cs
    ...
    using DevExpress.ExpressApp.Templates;
    using MySolution.Blazor.Server.Templates;
    
    namespace MySolution.Blazor.Server {
        public partial class MySolutionBlazorApplication : BlazorApplication {
            protected override IFrameTemplate CreateDefaultTemplate(TemplateContext context) {
                if (context == TemplateContext.ApplicationWindow) {
                    return new CustomApplicationWindowTemplate() { AboutInfoString = AboutInfo.Instance.GetAboutInfoString(this) };
                }
                return base.CreateDefaultTemplate(context);
            }
    ...