Underlying Controls and Components Behind UI Elements (ASP.NET Core Blazor)
- 5 minutes to read
Overview
In XAF Blazor applications, UI is built using Razor components. Component classes are usually Razor markup pages with .razor file extension. The code snippet below demonstrates a custom Razor component:
<DxSpinEdit @bind-Value="@Value" ShowSpinButtons="false" CssClass="my-custom-class"></DxSpinEdit>
@code {
int Value { get; set; } = 10;
}
In this code snippet, you add a DxSpinEdit<T> control to the markup, and specify the following parameters:
- ShowSpinButtons - disables increment/decrement buttons.
- CssClass - adds a custom class to HTML elements rendered by the
DxSpinEdit
component.
@bind-Value="@Value"
is responsible for the following functionality:
- When XAF renders a
DxSpinEdit
component, its value is set to theValue
property. - When you change the
DxSpinEdit
value in UI, theValue
property is updated.
See also:
You will rarely need to create custom components., but may need to customize built-in components. To do that, you don’t need to modify markup code. Use XAF component models - proxy objects that allow you to change the parameters of their respective components from anywhere in code.
For example, DxSpinEditModel
is an object representation of the DxSpinEdit
component. Each DxSpinEditModel
property has a corresponding DxSpinEdit property:
public class DxSpinEditModel : ComponentModelBase {
//
public bool ShowSpinButtons {
get => GetPropertyValue<bool>(true);
set => SetPropertyValue(value);
}
public string CssClass {
get => GetPropertyValue<string>();
set => SetPropertyValue(value);
}
}
To customize a property editor’s component in an auto-generated XAF View, you have to work with Component Model at runtime or in code:
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Blazor.Editors;
public class AddCustomCssClassController : ViewController<DetailView> {
protected override void OnActivated() {
base.OnActivated();
View.CustomizeViewItemControl<NumericPropertyEditor>(this, editor => {
editor.ComponentModel.ShowSpinButtons = false;
editor.ComponentModel.CssClass += " my-custom-class";
});
}
}
You can use Component Models to customize visual appearance and behavior of Property Editors (such as DxTextBox, DxDateEdit, DxSpinEdit), List Editors (such as DxGrid, DxScheduler), navigation (such as DxAccordion, DxTreeView), DxToolbar, and other components in XAF ASP.NET Core Blazor applications.
See also:
- Customize a Built-in Property Editor (Blazor)
- How to: Access the Grid Component in a List View
- Ways to Access UI Elements and Their Controls
- ActionBase.CustomizeControl
A Base Class for Underlying XAF Blazor Components
DevExpress.ExpressApp.Blazor.Components.Models.ComponentModelBase
is an abstract base class for all Component Models. Built-in Component Models replicate all parameters of the corresponding component by default. However, when you create a custom Component Model, you can include only necessary parameters. You can compare XAF Blazor’s ComponentModelBase
to BaseEdit in XAF WinForms or ASPxEditBase in XAF Web Forms apps.
In most cases, you can use the SetPropertyValue
and GetPropertyValue
methods to add the required component parameters to your Component Model class:
using DevExpress.ExpressApp.Blazor.Components.Models;
using Microsoft.AspNetCore.Components;
namespace MainDemo.Blazor.Server.Controllers;
public class InputModel : ComponentModelBase {
public string Value {
get => GetPropertyValue<string>();
set => SetPropertyValue(value);
}
public EventCallback<string> ValueChanged {
get => GetPropertyValue<EventCallback<string>>();
set => SetPropertyValue(value);
}
public bool ReadOnly {
get => GetPropertyValue<bool>();
set => SetPropertyValue(value);
}
}
The following methods and properties can also be useful:
ComponentModelBase.SetAttribute
This method sets additional attributes for the component (an equivalent of the ParameterAttribute.CaptureUnmatchedValues property). For more information, refer to the following topic: Arbitrary attributes.
public void SetAttribute(string name, object value)
Example of usage:
InputModel model = new InputModel(); model.SetAttribute("title", "A tooltip text");
ComponentModelBase.Attributes
This property contains a list of additional attributes specified by the
ComponentModelBase.SetAttribute
method.public IReadOnlyDictionary<string, object> Attributes { get; }
Example of usage:
<InputText Value=@ComponentModel.Value @* --- *@ @attributes=ComponentModel.Attributes /> @code { [Parameter] public InputModel ComponentModel { get; set; } }
Note
For additional context, see the following topic: Component Renderer.
See also:
- Manually Build a Render Tree (RenderTreeBuilder)
- Implement a Property Editor Based on a Custom Component (Blazor)
- How to: Use a Custom Component to Implement List Editor (Blazor)
Component State Changes and Content Re-Render
ComponentModelBase
implements the IComponentModel
interface that has the Changed
event.
namespace DevExpress.ExpressApp.Blazor.Components.Models
public interface IComponentModel {
event EventHandler Changed;
}
This event is triggered every time a property of the Component Model changes, for example, when you call the SetPropertyValue
method with a new value.
DevExpress.ExpressApp.Blazor.Components.ComponentModelObserver
is a component that wraps the main component and subscribes to the ComponentModel.Changed
event. When the event is triggered, ComponentModelObserver
calls the StateHasChanged method and re-renders itself and the child component.
- Razor markup
<ComponentModelObserver ComponentModel="@ComponentModel"> <InputRenderer ComponentModel="@ComponentModel" /> </ComponentModelObserver> @code { [Parameter] public InputModel ComponentModel { get; set; } }
- In XAF
public class InputAdapter : ComponentAdapterBase { //... public override InputModel ComponentModel { get; } protected override RenderFragment CreateComponent() { return ComponentModelObserver.Create(ComponentModel, InputRenderer.Create(ComponentModel)); } }
Tip
For the complete scenario, refer to the following topic: Implement a Property Editor Based on a Custom Component (Blazor).
For all component models shipped with XAF, we auto-generate the GetComponentContent
extension method that returns RenderFragment of the related component. The following code snipped demonstrates how to use this method:
- Razor markup
<ComponentModelObserver ComponentModel="TextBoxModel"> @TextBoxModel.GetComponentContent() </ComponentModelObserver> @code { DxTextBoxModel TextBoxModel { get; set; } = new DxTextBoxModel(); }
- In XAF
DxTextBoxModel TextBoxModel { get; set; } = new DxTextBoxModel(); // public RenderFragment CreateComponent() { return ComponentModelObserver.Create(TextBoxModel, TextBoxModel.GetComponentContent()); }
Handle Component Events
Razor components usually have events implemented as properties of the EventCallback<T> type. Examples include DxTextBox.TextChanged or InputBase.ValueChanged.
XAF can handle some of these events. To safely add your own event handler without overriding the default XAF implementation, we recommend that you use the following pattern:
var defaultEventHandler = ComponentModel.SomeEvent;
ComponentModel.SomeEvent = EventCallback.Factory.Create<TArgument>(this, async eventArgument => {
// Comment out the line below if you do not want XAF to handle this event.
await defaultEventHandler.InvokeAsync(eventArgument);
// Implement your custom logic here.
// ...
});
See also: