DevExpress
Overview
DevExpress is a commercial UI component suite for .NET that provides hundreds of high-performance controls for Blazor, WPF, WinForms, and ASP.NET Core. The Blazor component library (DevExpress.Blazor) includes a Data Grid, Pivot Grid, Scheduler, Rich Text Editor, Charts, Reporting, and Layout components. DevExpress components are optimized for large datasets, enterprise workflows, and complex LOB (line-of-business) applications. They ship with built-in themes, localization, accessibility support, and extensive documentation.
Blazor Data Grid Setup
Configure the DevExpress Blazor Data Grid with sorting, filtering, grouping, and editing.
@page "/employees"
@using DevExpress.Blazor
@inject IEmployeeService EmployeeService
<DxGrid Data="_employees"
KeyFieldName="Id"
EditMode="GridEditMode.EditRow"
EditModelSaving="OnEditModelSaving"
PageSize="20"
ShowFilterRow="true"
ShowGroupPanel="true"
ColumnResizeMode="GridColumnResizeMode.NextColumn">
<Columns>
<DxGridDataColumn FieldName="FirstName" Caption="First Name" Width="150px" />
<DxGridDataColumn FieldName="LastName" Caption="Last Name" Width="150px" />
<DxGridDataColumn FieldName="Department" Caption="Department" Width="200px">
<FilterRowCellTemplate>
<DxComboBox Data="_departments"
Value="(string)context.FilterRowValue"
ValueChanged="(string v) => context.FilterRowValue = v"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto" />
</FilterRowCellTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="Salary" Caption="Salary" DisplayFormat="C0" Width="120px" />
<DxGridDataColumn FieldName="HireDate" Caption="Hired" DisplayFormat="d" Width="120px" />
<DxGridCommandColumn Width="160px">
<CellDisplayTemplate>
<DxButton Text="Edit" Click="() => context.Grid.StartEditRowAsync(context.VisibleIndex)"
RenderStyle="ButtonRenderStyle.Link" />
</CellDisplayTemplate>
</DxGridCommandColumn>
</Columns>
<TotalSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Count" FieldName="Id" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Avg" FieldName="Salary" DisplayFormat="Avg: {0:C0}" />
</TotalSummary>
</DxGrid>
@code {
private List<Employee> _employees = new();
private List<string> _departments = new() { "Engineering", "Sales", "HR", "Finance" };
protected override async Task OnInitializedAsync()
{
_employees = await EmployeeService.GetAllAsync();
}
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
{
var editedEmployee = (Employee)e.EditModel;
var dataItem = _employees.First(emp => emp.Id == editedEmployee.Id);
dataItem.FirstName = editedEmployee.FirstName;
dataItem.LastName = editedEmployee.LastName;
dataItem.Department = editedEmployee.Department;
dataItem.Salary = editedEmployee.Salary;
await EmployeeService.UpdateAsync(dataItem);
}
}
Scheduler Component
The DxScheduler provides a full-featured calendar with appointments, resources, and recurrence rules.
@page "/schedule"
@using DevExpress.Blazor
<DxScheduler StartDate="@DateTime.Today"
DataStorage="_dataStorage"
ActiveViewType="SchedulerViewType.WorkWeek"
AppointmentInserted="OnAppointmentInserted"
AppointmentUpdated="OnAppointmentUpdated"
GroupType="SchedulerGroupType.Resource">
<Views>
<DxSchedulerDayView DayCount="1" ShowWorkTimeOnly="true"
WorkTime="new(TimeSpan.FromHours(8), TimeSpan.FromHours(18))" />
<DxSchedulerWorkWeekView ShowWorkTimeOnly="true" />
<DxSchedulerWeekView />
<DxSchedulerMonthView />
</Views>
</DxScheduler>
@code {
private DxSchedulerDataStorage _dataStorage = new()
{
AppointmentsSource = new List<Appointment>(),
AppointmentMappings = new DxSchedulerAppointmentMappings
{
Id = nameof(Appointment.Id),
Start = nameof(Appointment.StartDate),
End = nameof(Appointment.EndDate),
Subject = nameof(Appointment.Subject),
Description = nameof(Appointment.Description),
LabelId = nameof(Appointment.LabelId),
StatusId = nameof(Appointment.StatusId),
ResourceId = nameof(Appointment.RoomId),
RecurrenceInfo = nameof(Appointment.RecurrenceInfo)
},
ResourcesSource = new List<Room>
{
new() { Id = 1, Name = "Conference Room A", Color = System.Drawing.Color.Blue },
new() { Id = 2, Name = "Conference Room B", Color = System.Drawing.Color.Green }
},
ResourceMappings = new DxSchedulerResourceMappings
{
Id = nameof(Room.Id),
Caption = nameof(Room.Name),
Color = nameof(Room.Color)
}
};
private async Task OnAppointmentInserted(DxSchedulerAppointmentItem e)
{
var appointment = (Appointment)e.SourceObject;
await AppointmentService.CreateAsync(appointment);
}
private async Task OnAppointmentUpdated(DxSchedulerAppointmentItem e)
{
var appointment = (Appointment)e.SourceObject;
await AppointmentService.UpdateAsync(appointment);
}
}
Charts
DevExpress Blazor Charts support bar, line, area, pie, and financial chart types with interactive tooltips and zooming.
@using DevExpress.Blazor
<DxChart Data="_salesData" Width="100%" Height="400px">
<DxChartTitle Text="Quarterly Sales" />
<DxChartBarSeries Name="Revenue"
ArgumentField="@((SalesRecord s) => s.Quarter)"
ValueField="@((SalesRecord s) => s.Revenue)"
Color="System.Drawing.Color.SteelBlue" />
<DxChartLineSeries Name="Target"
ArgumentField="@((SalesRecord s) => s.Quarter)"
ValueField="@((SalesRecord s) => s.Target)"
Color="System.Drawing.Color.OrangeRed" />
<DxChartLegend Position="RelativePosition.Outside"
HorizontalAlignment="HorizontalAlignment.Center"
VerticalAlignment="VerticalEdge.Bottom" />
<DxChartTooltip Enabled="true">
<ContentTemplate>
<div>@($"{context.Point.Argument}: {context.Point.Value:C0}")</div>
</ContentTemplate>
</DxChartTooltip>
</DxChart>
@code {
private readonly List<SalesRecord> _salesData = new()
{
new("Q1 2024", 120_000m, 100_000m),
new("Q2 2024", 145_000m, 130_000m),
new("Q3 2024", 138_000m, 140_000m),
new("Q4 2024", 162_000m, 150_000m)
};
public record SalesRecord(string Quarter, decimal Revenue, decimal Target);
}
DevExpress vs Other Blazor Component Libraries
| Feature | DevExpress | Telerik | Blazorise | MudBlazor |
|---|---|---|---|---|
| License | Commercial | Commercial | Open source + Pro | Open source |
| Grid performance (10K+ rows) | Virtual scrolling | Virtual scrolling | Server paging | Virtual scrolling |
| Built-in reporting | Yes (XtraReports) | Yes (Telerik Reporting) | No | No |
| Scheduler | Full-featured | Full-featured | Basic | No |
| Rich Text Editor | Yes | Yes | No | No |
| Pivot Grid | Yes | No | No | No |
| Blazor render modes | Server, WASM, SSR | Server, WASM, SSR | Server, WASM | Server, WASM |
Best Practices
Use
DxGridvirtual scrolling mode (VirtualScrollingEnabled="true") for datasets exceeding 500 rows and bind toIQueryable<T>instead ofList<T>so the grid translates sort/filter operations into LINQ expressions that execute server-side, avoiding loading the entire dataset into memory.Set
KeyFieldNameon everyDxGridinstance to the primary key property because DevExpress uses this field to track row identity during editing, selection, and focus operations; omitting it causes unpredictable behavior when rows are added or reordered.Configure
DxScheduler.DataStoragefield mappings usingnameof()expressions instead of hardcoded strings, so that property renames on the appointment or resource model produce compile-time errors rather than silent runtime failures.Create a shared DevExpress theme configuration in
App.razorusing<DxResourceManager>and set the global size mode (SizeMode.Small,Medium, orLarge) once, rather than settingSizeModeon individual components, which leads to inconsistent density across pages.Implement
GridEditModelSavingEventArgshandlers that validate theEditModeland sete.Cancel = trueon validation failure with a user-visible notification, rather than silently ignoring invalid edits; the grid closes the edit row regardless unlessCancelis explicitly set.Use
DxGridDataColumn.DisplayFormatwith .NET format strings (e.g.,"C2","N0","d") on numeric and date columns instead of formatting inDisplayTemplate, becauseDisplayFormatis used by the grid's built-in export, summary, and clipboard operations.Wrap chart data updates in
InvokeAsync(StateHasChanged)when data arrives from background services or SignalR hubs becauseDxChartrelies on Blazor's render cycle to detect changes; mutating the data list without triggering a render produces stale visuals.Set explicit
Widthvalues onDxGridDataColumnfor columns that display fixed-format data (dates, currency, status badges) and leave only one text-heavy column without a width to absorb remaining space, preventing layout shifts during data loading.License the DevExpress NuGet feed using a
nuget.configfile with the DevExpress package source and credentials stored in CI environment variables, not committed to source control; the DevExpress feed URL (https://nuget.devexpress.com/api) requires an active license key.Register DevExpress services before
builder.Build()usingbuilder.Services.AddDevExpressBlazor()and add the<DxResourceManager>component in the<head>section ofApp.razorto ensure CSS and JavaScript resources are loaded before any DevExpress component renders.