How to: Show a Custom Data-Bound Control in an XAF View (Blazor) - External Data
- 4 minutes to read
This topic demonstrates how to add a custom data-bound (data-aware) control to a View in a Blazor application. It also explains how to extend app navigation and add a shortcut to the new View.
This topic is based on the MainDemo Blazor Server
demo application that ships with XAF. You can find this demo in the following folder: %PUBLIC%\Documents\DevExpress Demos 24.1\Components\XAF\MainDemo.NET.EFCore\CS\MainDemo.Blazor.Server.
Create a Razor Component
Blazor apps are built using Razor components. In this example, we implement a custom Razor component based on a DxChart control that displays a graph of incomplete tasks.
To add this component to your project, follow the steps below:
- In the Solution Explorer, right-click your Blazor project’s name and select Add -> New Item from the ensuing context menu.
- Specify a component name (
TaskChartComponent.razor
). Add the following code to the created file.
@using MainDemo.Module.BusinessObjects; @using System.Drawing; @using DevExpress.Blazor; <DxChart T="DemoTask" Data="@DataSource" Width="100%"> <DxChartTitle Text="Tasks"> <DxChartSubTitle Text="Breakdown by department"></DxChartSubTitle> </DxChartTitle> @RenderSeries(Priority.Low) @RenderSeries(Priority.Normal) @RenderSeries(Priority.High) <DxChartLegend Position="RelativePosition.Outside" HorizontalAlignment="HorizontalAlignment.Center" VerticalAlignment="VerticalEdge.Bottom" /> <DxChartTooltip Enabled="true" Position="RelativePosition.Outside"> <div style="margin: 0.75rem"> <div class="fw-bold">@context.Point.Argument</div> <div>Priority: @context.Point.SeriesName</div> <div>Tasks in progress: @($"{context.Point.Value:N0}")</div> </div> </DxChartTooltip> </DxChart> @code { [Parameter] public IEnumerable<DemoTask> DataSource { get; set; } private RenderFragment RenderSeries(Priority priority) { var color = priority switch { Priority.Low => Color.FromArgb(0x3c, 0x7e, 0x38), Priority.Normal => Color.FromArgb(0xd6, 0x82, 0x29), Priority.High => Color.FromArgb(0xd3, 0x30, 0x3d), _ => throw new ArgumentOutOfRangeException() }; string name = $"{priority} priority"; return @<DxChartStackedBarSeries T="DemoTask" Name="@name" Color="@color" TArgument="string" ArgumentField="@(task => GetDepartment(task))" TValue="int" ValueField="@(task => task.Status == Module.BusinessObjects.TaskStatus.Completed ? 0 : 1)" SummaryMethod="Enumerable.Sum" Filter="@(task => task.Priority == priority)" />; } private string GetDepartment(DemoTask task) { return (task.AssignedTo as Employee)?.Department?.Title ?? "None"; } }
- In the Properties window, set this file’s
Build Action
toContent
.
Create a Control and Bind It to Data Using Object Space
Follow the steps below to create a custom control:
- Add a new file (
TaskChartControl.cs
) to your Blazor project. Add the following code to the created file.
using DevExpress.ExpressApp; using DevExpress.ExpressApp.Blazor; using DevExpress.ExpressApp.Blazor.Components; using DevExpress.ExpressApp.Blazor.Components.Models; using DevExpress.ExpressApp.Editors; using MainDemo.Module.BusinessObjects; using Microsoft.AspNetCore.Components; namespace MainDemo.Blazor.Server; public class TaskChartComponentModel : ComponentModelBase { public IEnumerable<DemoTask> DataSource { get => GetPropertyValue<IEnumerable<DemoTask>>(); set => SetPropertyValue(value); } public override Type ComponentType => typeof(TaskChartComponent); } public class TaskChartControl : IComplexControl, IComponentContentHolder { private RenderFragment componentContent; private TaskChartComponentModel componentModel = new(); private IObjectSpace objectSpace; public RenderFragment ComponentContent { get { componentContent ??= ComponentModelObserver.Create(componentModel, componentModel.GetComponentContent()); return componentContent; } } public void Refresh() { componentModel.DataSource = objectSpace.GetObjects<DemoTask>().AsEnumerable(); } public void Setup(IObjectSpace objectSpace, XafApplication application) { this.objectSpace = objectSpace; componentModel.DataSource = objectSpace.GetObjects<DemoTask>().AsEnumerable(); } }
The control in this example implements the IComplexControl interface. This allows the control to access an ObjectSpace instance and use the Object Space API to read the required data and then initialize the data source or refresh the data if necessary.
To display a Razor component in the UI, a View Item’s control must implement the IComponentContentHolder
interface. The ComponentContent
member returns a RenderFragment
. RenderFragment
is a delegate type that specifies a segment of UI content. In this tutorial, you need to deal with the following two RenderFragment
delegates:
- A
RenderFragment
returned by thecomponentModel.GetComponentContent()
method. This delegate defines aTaskChartComponent
based onTaskChartComponentModel
parameters. - A
RenderFragment
returned by theComponentModelObserver.Create()
method. In this method, you wrap the Chart’s component model in a built-in XAF component (ComponentModelObserver
) to ensure that theTaskChartComponent
re-renders whenever its component model (TaskChartComponentModel
) changes. For more information on rendering and other Razor component lifecycle events, refer to the following Microsoft article: ASP.NET Core Razor component lifecycle.
Add the Control to a View
In the Blazor application project, double-click the Model.xafml file to start the Model Editor. Right-click the Views node and choose Add | DashboardView.
Set the Id property to TaskChartView.
Right-click the Views | TaskChartView | Items node and choose Add… | ControlDetailItem.
Set the Id property to TaskChartView, and the IModelControlDetailItem.ControlTypeName property - to the type of the custom User Control you created (e.g., MainDemo.Blazor.Server.TaskChartControl).
Note
You can add the ControlViewItem View Item to any existing Detail View or Dashboard View instead of creating a new Dashboard View.
Create a Navigation Item to Show the View
Navigate to the NavigationItems | Items | Default | Items node. Right-click the Items node and select Add… | NavigationItem from the invoked context menu.
For the newly added node, in the IModelNavigationItem.View dropdown list, select the View you created earlier (
TaskChartView
).Run your Blazor application and click TaskChartView in the navigation tree. The Chart View bound to the
DemoTask
collection is displayed.