Skip to main content
All docs
V23.2
.NET 6.0+

Implement a Property Editor Based on a Custom Component (Blazor)

  • 5 minutes to read

This topic describes how to implement a Property Editor for ASP.NET Core Blazor applications. The example below demonstrates a custom String Property Editor that displays the InputText element.

To use a custom component in your ASP.NET Core Blazor application, implement the following components in the application project (MySolution.Blazor.Server):

  • Component Model – to change the state of the component.
  • Component Renderer – to bind the component model to the component.
  • Component Adapter – to apply the Property Editor settings to the component and notify the Property Editor about component changes.
  • Property Editor – to integrate the component into your XAF application.

Note

For more information about XAF Component Model, refer to the following topic: Underlying Controls and Components Behind UI Elements (ASP.NET Core Blazor)

Component Model

Create a ComponentModelBase descendant and name it InputModel. In this class, declare properties that describe your component and user’s interaction with it.

File: MySolution.Blazor.Server\Editors\InputModel.cs

using DevExpress.ExpressApp.Blazor.Components.Models;
using Microsoft.AspNetCore.Components;

namespace MySolution.Blazor.Server.Editors;
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);
    }
}

Component Renderer

  1. Create a new Razor component and name it InputRenderer.
  2. In the File Properties window, set the Build Action property of the created component to Content.
  3. Declare the ComponentModel component parameter that binds the <input> element to its model.
  4. Map the element parameters to the model properties and add the Create method that creates RenderFragment.

File: MySolution.Blazor.Server\Editors\InputRenderer.razor

<InputText Value=@ComponentModel.Value
           ValueChanged=ComponentModel.ValueChanged
           ValueExpression="() => ComponentModel.Value"
           readonly=@ComponentModel.ReadOnly
           @attributes=ComponentModel.Attributes />

@* The following example demonstrates how to achieve the same result using the HTML input element:

<input value=@ComponentModel.Value
       @onchange=@(e => ComponentModel.ValueChanged.InvokeAsync((string)e.Value))
       readonly=@ComponentModel.ReadOnly
       @attributes=@ComponentModel.Attributes /> *@

@code {
    [Parameter]
    public InputModel ComponentModel { get; set; }
    public static RenderFragment Create(InputModel componentModel) => @<InputRenderer ComponentModel=@componentModel />;
}

Component Adapter

Create a DevExpress.ExpressApp.Blazor.Editors.Adapters.ComponentAdapterBase descendant and name it InputAdapter. Override its methods to implement Property Editor logic for your component model.[1]

File: MySolution.Blazor.Server\Editors\InputAdapter.cs

using DevExpress.ExpressApp.Blazor.Components;
using DevExpress.ExpressApp.Blazor.Editors.Adapters;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Utils;
using Microsoft.AspNetCore.Components;

namespace MySolution.Blazor.Server.Editors;

public class InputAdapter : ComponentAdapterBase {
    public InputAdapter(InputModel componentModel) {
        ComponentModel = componentModel ?? throw new ArgumentNullException(nameof(componentModel));
        ComponentModel.ValueChanged = EventCallback.Factory.Create<string>(this, value => {
            componentModel.Value = value;
            RaiseValueChanged();
        });
    }
    public override InputModel ComponentModel { get; }
    public override void SetAllowEdit(bool allowEdit) => ComponentModel.ReadOnly = !allowEdit;
    public override object GetValue() => ComponentModel.Value;
    public override void SetValue(object value) => ComponentModel.Value = (string)value;
    protected override RenderFragment CreateComponent() => ComponentModelObserver.Create(ComponentModel, InputRenderer.Create(ComponentModel));
    public override void SetAllowNull(bool allowNull) { /* ...*/ }
    public override void SetDisplayFormat(string displayFormat) { /* ...*/ }
    public override void SetEditMask(string editMask) { /* ...*/ }
    public override void SetEditMaskType(EditMaskType editMaskType) { /* ...*/ }
    public override void SetErrorIcon(ImageInfo errorIcon) { /* ...*/ }
    public override void SetErrorMessage(string errorMessage) { /* ...*/ }
    public override void SetIsPassword(bool isPassword) { /* ...*/ }
    public override void SetMaxLength(int maxLength) { /* ...*/ }
    public override void SetNullText(string nullText) { /* ...*/ }
}

Property Editor

Create a BlazorPropertyEditorBase descendant and name it CustomStringPropertyEditor. Apply PropertyEditorAttribute to it and set the first attribute parameter to string and the second one to false. With these values, you can choose this Property Editor in the Model Editor for any string property, and this editor is not marked as default.

File: MySolution.Blazor.Server\Editors\CustomStringPropertyEditor.cs

using DevExpress.ExpressApp.Blazor.Editors;
using DevExpress.ExpressApp.Blazor.Editors.Adapters;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Model;

namespace MySolution.Blazor.Server.Editors;

[PropertyEditor(typeof(string), false)]
public class CustomStringPropertyEditor : BlazorPropertyEditorBase {
    public CustomStringPropertyEditor(Type objectType, IModelMemberViewItem model) : base(objectType, model) { }
    protected override IComponentAdapter CreateComponentAdapter() => new InputAdapter(new InputModel());
    public override InputModel ComponentModel => (Control as InputAdapter)?.ComponentModel;
}

Rebuild your solution and invoke the Model Editor for the ASP.NET Core Blazor application project (MySolution.Blazor.Server). Navigate to the required BOModel | <Class> | OwnMembers | <Member> node and set the node’s PropertyEditorType property to CustomStringPropertyEditor.

You can also set this Property Editor for a particular View only. To do this, specify the PropertyEditorType property of the Views | <DetailView> | Items | <PropertyEditor> node.

Access XafApplication and ObjectSpace to Query and Manipulate Data (Perform CRUD Operations)

A custom Property Editor may require access to the application or the View ObjectSpace object. If so, implement the IComplexViewItem interface as shown in the following topic: IComplexViewItem.

Use the IComplexViewItem.Setup method to get the XafApplication and IObjectSpace objects. The IObjectSpace object contains methods to access the application database.

Display a Custom Component in a List View

The <input> element from the above example will be rendered in Detail Views and in List Views during inline editing. To specify a component that your custom Property Editor should render in regular List View cells, override the BlazorPropertyEditorBase.CreateViewComponentCore method.

File: MySolution.Blazor.Server\CustomStringPropertyEditor.cs

using DevExpress.ExpressApp.Blazor.Editors;
using DevExpress.ExpressApp.Blazor.Editors.Adapters;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Model;
using Microsoft.AspNetCore.Components;

namespace MySolution.Blazor.Server.Editors;

[PropertyEditor(typeof(string), false)]
public class CustomStringPropertyEditor : BlazorPropertyEditorBase {
    public CustomStringPropertyEditor(Type objectType, IModelMemberViewItem model) : base(objectType, model) { }
    protected override IComponentAdapter CreateComponentAdapter() => new InputAdapter(new InputModel());
    public override InputModel ComponentModel => (Control as InputAdapter)?.ComponentModel;

    protected override RenderFragment CreateViewComponentCore(object dataContext) {
        InputModel componentModel = new InputModel() { Value = (string)this.GetPropertyValue(dataContext) };
        componentModel.ReadOnly = true;
        return InputRenderer.Create(componentModel);
    }
}

The image below demonstrates the result.

XAF ASP.NET Core Blazor Custom Component Based String Property Editor in a List View, DevExpress

Footnotes
  1. In this example, we use the InputText component that does not support some format settings. If your editor supports these settings, override related methods to apply the settings to the component model. See the following example:

    public override void SetDisplayFormat(string displayFormat) { ComponentModel.DisplayFormat = displayFormat; }

    If your project uses .NET 6 and earlier, an error may occur. For more information, refer to the following pull request in the ASP.NET Core open-source repository: Blazor input component support when EditContext is not supplied.

See Also