xaf-blazor-ui

XAF Blazor UI platform - BlazorApplication setup in Program.cs, AddXaf/AddXafBlazor services, InvokeAsync thread safety (critical for Blazor Server), async controller actions pattern, Blazor-specific editors, embedding custom Razor components as ViewItems using IComponentContentHolder, JavaScript interop via IJSRuntime, Detail View layout customization (tabs/groups), programmatic navigation, error handling with UserFriendlyException, SignalR configuration. Use when building or customizing XAF Blazor Server applications.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "xaf-blazor-ui" with this command: npx skills add kashiash/xaf-skills/kashiash-xaf-skills-xaf-blazor-ui

XAF: Blazor UI Platform

Application Setup

// Program.cs (minimal hosting model, .NET 8+)
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddXaf(builder.Configuration, b => {
    b.UseApplication<MyBlazorApplication>();
    b.AddObjectSpaceProviders(providers => {
        providers.UseEntityFramework(ef => {
            ef.DefaultDatabaseConnection("Default", p =>
                p.UseDbContext<MyDbContext>());
        });
        providers.AddNonPersistent();
    });
    b.Security
        .UseIntegratedMode(options => {
            options.RoleType = typeof(PermissionPolicyRole);
            options.UserType = typeof(ApplicationUser);
        })
        .AddPasswordAuthentication();
    b.AddModules(typeof(MyModule), typeof(ValidationModule));
});

builder.Services.AddDevExpressBlazor();

var app = builder.Build();
app.UseXaf();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
app.Run();
// BlazorApplication.cs
public class MyBlazorApplication : BlazorApplication {
    public MyBlazorApplication() {
        DatabaseUpdateMode = DatabaseUpdateMode.UpdateDatabaseAlways;
    }
    protected override void OnSetupStarted() {
        base.OnSetupStarted();
        // Initial setup configuration
    }
}

Thread Safety — InvokeAsync (CRITICAL)

Blazor Server runs on a circuit with a synchronization context. When updating UI from an async operation or background thread, always use InvokeAsync.

// CORRECT: async void for action handlers — NO ConfigureAwait(false)!
private async void MyAction_Execute(object sender, SimpleActionExecuteEventArgs e) {
    try {
        // Long-running work — await normally
        var result = await myService.DoWorkAsync(); // NO ConfigureAwait(false)!

        // UI updates must be on the Blazor circuit thread
        View.ObjectSpace.CommitChanges();
        View.Refresh();
        Application.ShowViewStrategy.ShowMessage("Done", InformationType.Success);
    }
    catch (Exception ex) {
        throw new UserFriendlyException(ex.Message);
    }
}

// If called from non-Blazor thread (e.g., background service):
await Application.InvokeAsync(() => {
    View.Refresh();
    // any UI update
});

Why ConfigureAwait(false) breaks Blazor: It resumes on a thread pool thread, outside the Blazor circuit, causing InvalidOperationException on UI updates.


Blazor-Specific Editors

EditorData TypeNotes
DxTextBoxPropertyEditorstringDevExpress DxTextBox
DxDateEditPropertyEditorDateTimeDevExpress DxDateEdit
DxComboBoxPropertyEditorenumDevExpress DxComboBox
DxCheckBoxPropertyEditorboolDevExpress DxCheckBox
DxSpinEditPropertyEditornumericDevExpress DxSpinEdit
DxLookupPropertyEditorreferencePopup lookup
DxTagBoxPropertyEditorcollectionTag selection
HtmlContentPropertyEditorstringRenders HTML

Custom Razor Component as ViewItem

Embed a Razor component in a Detail View:

1. Create the Razor component

@* MyCustomComponent.razor *@
@inject IServiceProvider ServiceProvider

<div class="my-component">
    <h4>@Title</h4>
    @if (Model != null) {
        <p>Current value: @Model.SomeProperty</p>
    }
</div>

@code {
    [Parameter] public string Title { get; set; }
    [Parameter] public MyObject Model { get; set; }
}

2. Create the ComponentModel

using DevExpress.ExpressApp.Blazor;

public class MyCustomComponentModel : ComponentModelBase {
    private MyObject model;

    public MyObject Model {
        get => model;
        set => SetProperty(ref model, value);
    }

    public override Type ComponentType => typeof(MyCustomComponent);
}

3. Create the ViewItem

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

[ViewItem(typeof(IModelViewItem))]
public class MyCustomViewItem : BlazorViewItem {
    private MyCustomComponentModel componentModel;

    public MyCustomViewItem(IModelViewItem model, Type objectType)
        : base(model, objectType) { }

    protected override IComponentModel CreateComponentAdapter() {
        componentModel = new MyCustomComponentModel();
        return componentModel;
    }

    public override void Refresh() {
        base.Refresh();
        componentModel.Model = CurrentObject as MyObject;
    }
}

4. Register ViewItem in Module

public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
    base.ExtendModelInterfaces(extenders);
    extenders.Add<IModelViewItem, IModelMyCustomViewItem>();
}

JavaScript Interop

public class JsInteropController : ViewController {
    [Autowired]
    IJSRuntime jsRuntime;

    private SimpleAction callJsAction;

    public JsInteropController() {
        callJsAction = new SimpleAction(this, "CallJsAction", PredefinedCategory.View);
        callJsAction.Execute += CallJsAction_Execute;
    }

    private async void CallJsAction_Execute(object sender, SimpleActionExecuteEventArgs e) {
        await jsRuntime.InvokeVoidAsync("console.log", "Hello from XAF!");
        await jsRuntime.InvokeVoidAsync("alert", "Action executed");
    }
}

Or inject via DI in the controller constructor:

[ActivatorUtilitiesConstructor]
public MyController(IServiceProvider serviceProvider) : base() {
    jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
}

Detail View Layout Customization

Layout is defined in Application Model: Views > <ClassName>_DetailView > Layout

Programmatic via controller:

// Access layout groups in the model
var detailViewModel = (IModelDetailView)Application.Model.Views["Contact_DetailView"];
// Navigate Layout node and modify group positions, visibility, captions, etc.

For runtime layout customization, use a ViewController:

protected override void OnViewControlsCreated() {
    base.OnViewControlsCreated();
    // Expand specific tab by index
    if (View is DetailView detailView) {
        var tabControl = detailView.Items.OfType<TabbedGroupViewItem>().FirstOrDefault();
        // tabControl?.Control.SelectedTabIndex = 1;
    }
}

Programmatic Navigation

// Navigate to object's Detail View
var showViewParams = Application.CreateDetailViewShowViewParameters(
    targetObject, objectSpace);
Application.ShowViewStrategy.ShowView(showViewParams, new ShowViewSource(Frame, null));

// Navigate to ListView
var lvId = Application.FindListViewId(typeof(Order));
var lv = Application.CreateListView(lvId, true);
Application.ShowViewStrategy.ShowView(
    new ShowViewParameters(lv), new ShowViewSource(Frame, null));

// Show popup message
Application.ShowViewStrategy.ShowMessage("Operation complete", InformationType.Success, 3000);

Error Handling

// User-friendly error (shown as dialog, not crash)
throw new UserFriendlyException("Invalid operation: " + reason);

// Validation error in actions
try {
    await DoSomethingAsync();
}
catch (Exception ex) when (ex is not UserFriendlyException) {
    throw new UserFriendlyException($"Error: {ex.Message}");
}

SignalR Configuration

// Increase timeout for long operations
builder.Services.AddSignalR(options => {
    options.ClientTimeoutInterval = TimeSpan.FromMinutes(5);
    options.HandshakeTimeout = TimeSpan.FromSeconds(30);
    options.MaximumReceiveMessageSize = 32 * 1024; // 32KB
});

v24.2 vs v25.1 Notes

Featurev24.2v25.1
.NET target.NET 8.NET 8 / .NET 9
Blazor render modeServerServer + enhanced SSR
DxGridv24.2 APIEnhanced column/toolbar API
InvokeAsyncAvailableAvailable (same)
Report designerPreviewImproved

Source Links

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

xaf-office

No summary provided by upstream source.

Repository SourceNeeds Review
General

xaf-winforms-ui

No summary provided by upstream source.

Repository SourceNeeds Review
General

xaf-editors

No summary provided by upstream source.

Repository SourceNeeds Review
General

xaf-deployment

No summary provided by upstream source.

Repository SourceNeeds Review