How to: Show a Custom Data-Bound Control in an XAF View (Blazor) - Current Object Data
- 5 minutes to read
This article explains how to create a reusable View Item that can work with data supplied by the View’s current object. Before you continue, make sure you are familiar with the tutorial that describes the commonly required steps - How to: Show a Custom Data-Bound Control in an XAF View (Blazor) - External Data.
Create a Razor Component
In this example, you create a Razor component based on DxAccordion. The new component displays employees assigned to a particular department.
To add this component to your project, follow the steps below:
- In the Solution Explorer, right-click your project’s name and select Add -> New Item from the ensuing context menu.
- Specify a component name (
DepartmentViewer.razor
). Add the following code to the created file.
@using MainDemo.Module.BusinessObjects; @using DevExpress.Blazor; <b>Department name:</b> <DxTextBox Text="@Department?.Title" TextChanged="@OnTitleChanged" /><br /> <b>Department head:</b> @Department?.DepartmentHead?.FullName<br /> <b>Department description:</b> @Department?.Description<br /><br /> <DxAccordion ExpandMode="AccordionExpandMode.SingleOrNone" style="max-width: 600px" AnimationType="LayoutAnimationType.Slide"> <Items> @if (Department?.Employees != null) { @foreach (var employee in Department.Employees) { <DxAccordionItem Text="@employee.FullName" @key="employee"> <ContentTemplate> <div style="display: flex;"> <div style="flex: 1 0 0;"> <div><b>Full name:</b> @employee.FullName</div> <div><b>Email address:</b> @employee.Email</div> <div><b>Position:</b> @employee.Position</div> <div><b>Office:</b> @employee.Department.Office</div> @if (employee.Manager is not null) { <div><b>Manager:</b> @employee.Manager.FullName</div> } </div> <div style="flex: 1 0 0; display: flex; justify-content: end;"> @if (employee.Photo != null) { <div style="flex: 1 0 0; display: flex; justify-content: end;"> <img src="@($"data:image/png;base64,{Convert.ToBase64String(employee.Photo)}")" style="max-width: 300px; max-height: 300px;"> </div> } </div> </div> </ContentTemplate> </DxAccordionItem> } } </Items> </DxAccordion> @code { [Parameter] public DepartmentViewerModel ComponentModel { get; set; } public static RenderFragment Create(DepartmentViewerModel componentModel) => @<DepartmentViewer ComponentModel="@componentModel" />; private Department Department => ComponentModel.Department; private void OnTitleChanged(string title) => ComponentModel.SetTitleFromUI(title); }
The
DepartmentViewerModel.SetTitleFromUI
method notifies subscribers when a user changes the department’s title.In the Properties window, set this file’s
Build Action
toContent
.
An important difference between this component and the one used in a similar tutorial is the OnTitleChanged
method. This method allows you to notify the application that a user changed the value in the department name text box.
Implement a Custom View Item and Its Control
Implement a custom View Item that returns an IComponentContentHolder
as its control, as well as a Component Model for the custom DepartmentViewer
Razor component. To do this, follow the steps below:
- Add a new file (
DepartmentViewerViewItem.cs
) to your Blazor project. Add the following code to the created file.
using DevExpress.ExpressApp.Blazor; using DevExpress.ExpressApp.Blazor.Components; using DevExpress.ExpressApp.Blazor.Components.Models; using DevExpress.ExpressApp.Editors; using DevExpress.ExpressApp.Model; using MainDemo.Module.BusinessObjects; using Microsoft.AspNetCore.Components; namespace MainDemo.Blazor.Server; public class DepartmentViewerModel : ComponentModelBase { public Department Department { get => GetPropertyValue<Department>(); set => SetPropertyValue(value); } public void SetTitleFromUI(string title) { TitleChanged?.Invoke(this, title); } public event EventHandler<string> TitleChanged; } public class DepartmentViewerAdapter : IComponentContentHolder { private RenderFragment _componentContent; public DepartmentViewerModel ComponentModel { get; set; } public DepartmentViewerAdapter(DepartmentViewerModel componentModel) { ComponentModel = componentModel; } public RenderFragment ComponentContent { get { _componentContent ??= ComponentModelObserver.Create(ComponentModel, DepartmentViewer.Create(ComponentModel)); return _componentContent; } } } public interface IModelDepartmentViewerViewItem : IModelViewItem { } [ViewItem(typeof(IModelDepartmentViewerViewItem))] public class DepartmentViewerViewItem : ViewItem { public DepartmentViewerAdapter ComponentAdapter { get; private set; } public DepartmentViewerViewItem(IModelDepartmentViewerViewItem model, Type objectType) : base(objectType, model.Id) { } protected override object CreateControlCore() { ComponentAdapter = new DepartmentViewerAdapter(new DepartmentViewerModel() { Department = View.CurrentObject as Department }); return ComponentAdapter; } }
Rebuild your solution.
For more information on how to implement View Items in XAF applications, refer to the following topic: Implement a View Item (WinForms).
Create a View Controller to Manage the Control’s Data
To make sure that the View Item refreshes to reflect the correct data when the current object changes, add the following controller to your application. This controller is also used to update the object after the user made changes to the View Item (e.g., changed the Department name).
- Add a new file (
DepartmentViewerController.cs
) to your Blazor project. Add the following code to the created file.
using DevExpress.ExpressApp; using MainDemo.Module.BusinessObjects; namespace MainDemo.Blazor.Server.Controllers; public class DepartmentViewerController : ObjectViewController<DetailView, Department> { protected override void OnActivated() { base.OnActivated(); View.CurrentObjectChanged += View_CurrentObjectChanged; View.CustomizeViewItemControl<DepartmentViewerViewItem>(this, viewItem => { viewItem.ComponentAdapter.ComponentModel.TitleChanged += (_, title) => { if (View.CurrentObject is Department department) { department.Title = title; } }; }); } protected override void OnDeactivated() { base.OnDeactivated(); View.CurrentObjectChanged -= View_CurrentObjectChanged; } // Alternatively, you can use the ViewItem.CurrentObjectChanged event to achieve similar functionality directly in the View Item. private void View_CurrentObjectChanged(object sender, EventArgs e) { foreach (var departmentViewItem in View.GetItems<DepartmentViewerViewItem>()) { if (departmentViewItem.ComponentAdapter is not null) { departmentViewItem.ComponentAdapter.ComponentModel.Department = View.CurrentObject as Department; } } } }
Change the Default Detail View for the Department List 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 | DetailView.
Set the Id property to CustomDepartment_DetailView and the ModelClass property to MainDemo.Module.BusinessObjects.Department.
Right-click the Views | MainDemo.Module.BusinessObjects | CustomDepartment_DetailView | Items node and choose Add… | DepartmentViewerItemView.
Set the Id property to DepartmentViewItem.
Navigate to the Views | MainDemo.Module.BusinessObjects | Department_ListView node. In the DetailView drop-down list, select CustomDepartment_DetailView.
Run the your Blazor application, navigate to the Department List View and see the result.