Skip to main content
All docs
V23.2

How To: Create a Custom Blazor Application Template

  • 5 minutes to read

This article explains how to create a custom Application Window Template in an XAF ASP.NET Core Blazor application.

XAF applications created in the Solution Wizard use a DxAccordion or DxTreeView navigation component. The custom template described in this topic uses the DxMenu navigation component.

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

Implementation Details

  1. In the Solution Explorer, navigate to the YourSolutionName.Blazor.Server project and create the Templates folder.
  2. Create a custom Action control that displays a custom navigation component. Right-click the Templates folder, select Add | Class… option from the context menu, and set the name of the new class to CustomShowNavigationItemActionControl.cs. Implement the ISingleChoiceActionControl interface in the newly created class. Set the wrapped Action’s ActionId property to ShowNavigationItem.

    using DevExpress.ExpressApp.Actions;
    using DevExpress.ExpressApp.Templates;
    using DevExpress.ExpressApp.Templates.ActionControls;
    using Microsoft.AspNetCore.Components;
    
    namespace YourSolutionName.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 the DxMenu component. Right-click the Templates folder, select the Add | Razor Component option from the context menu, and set the new component’s name to CustomShowNavigationItemActionControlComponent.razor. Replace the autogenerated 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 the Add DevExpress Item | New Item… option from the context menu. In the invoked Template Gallery, navigate to the XAF ASP.NET Core Blazor Templates section and select the Application Window Template item. Name it CustomApplicationWindowTemplate.cs and click Add Item.

  5. In the CustomApplicationWindowTemplate class, replace the built-in ShowNavigationItemActionControl with the newly created CustomShowNavigationItemActionControl:

    using DevExpress.Blazor;
    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;
    
    namespace YourSolutionName.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("SaveOptions", ToolbarItemAlignment.Right, isDropDown: true,
                defaultActionId: "SaveAndNew", autoChangeDefaultAction: true);
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Save), ToolbarItemAlignment.Right);
                Toolbar.AddActionContainer("Close");
                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(nameof(PredefinedCategory.Export));
                Toolbar.AddActionContainer("Diagnostic");
                Toolbar.AddActionContainer(nameof(PredefinedCategory.Unspecified));
    
                HeaderToolbar = new DxToolbarAdapter(new DxToolbarModel() {
                    ItemRenderStyleMode = ToolbarRenderStyleMode.Plain,
                    TitleTemplate = content => ViewCaptionComponent.Creator(this),
                    CssClass = "pe-2"
                });
                HeaderToolbar.ImageSize = 18;
                HeaderToolbar.AddActionContainer("QuickAccess", ToolbarItemAlignment.Right);
                HeaderToolbar.AddActionContainer("Notifications", ToolbarItemAlignment.Right);
                HeaderToolbar.AddActionContainer("Diagnostic", ToolbarItemAlignment.Right);
            }
            protected override IEnumerable<IActionControlContainer> GetActionControlContainers() {
                return Toolbar.ActionContainers.Union(HeaderToolbar.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; }
            public DxToolbarAdapter HeaderToolbar { get; }
    
            void ISupportActionsToolbarVisibility.SetVisible(bool isVisible) => IsActionsToolbarVisible = isVisible;
        }
    }
    
  6. Modify the CustomApplicationWindowTemplateComponent.razor component according to your business requirements. You can see an example in the code sample below:

    @* 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 id="main-window-template-component" class="app h-100 d-flex flex-column">
        <div class="header d-flex flex-row shadow-sm navbar-dark flex-nowrap">
            <div class="header-left-side d-flex align-items-center ps-2">
                @FrameTemplate.ShowNavigationItemActionControl.GetComponentContent(@<ViewCaptionComponent WindowCaption="@FrameTemplate" />)
            </div>
            <div class="header-right-side w-100 overflow-hidden d-flex align-items-center ps-4">
                <SizeModeContainer>
                    @FrameTemplate.HeaderToolbar.GetComponentContent()
                </SizeModeContainer>
                <div class="d-flex ms-auto">
                    @FrameTemplate.AccountComponent.GetComponentContent()
                    <SettingsComponent />
                </div>
            </div>
        </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:

    using DevExpress.ExpressApp;
    using DevExpress.ExpressApp.Blazor;
    using DevExpress.ExpressApp.SystemModule;
    using DevExpress.ExpressApp.Templates;
    using YourSolutionName.Blazor.Server.Templates;
    
    namespace YourSolutionName.Blazor.Server;
    
    public class YourSolutionNameBlazorApplication : BlazorApplication {
        // ...
        protected override IFrameTemplate CreateDefaultTemplate(TemplateContext context) {
            if (context == TemplateContext.ApplicationWindow) {
                return new CustomApplicationWindowTemplate() { AboutInfoString = AboutInfo.Instance.GetAboutInfoString(this) };
            }
            return base.CreateDefaultTemplate(context);
        }
    }
    
  8. Run the application. Now XAF renders the navigation control as a drop-down menu.

    XAF ASP.NET Core Blazor Application With a Custom Window Template, DevExpress

See Also