Reporting plugins
Add application-specific report elements without replacing the report designer, document model, or export pipeline.
A report element plugin describes one custom element type. Reporting keeps ownership of positioning, selection, drag and drop, resize handles, collision warnings, undo, copy and paste, JSON persistence, and pagination. The plugin owns its serializable properties and supplies the components used for designer and HTML rendering, an optional properties editor, and a PDF renderer.
Plugin model
IReportElementPluginexposes a stable type name, toolbox metadata, renderer component types, default element state, and the optional PDF renderer.ReportCustomElementDefinitionstores common report layout properties together with the pluginTypeName,SchemaVersion, and JSONProperties.BaseReportElementRendererreceives aReportElementRenderContextcontaining the definition, band, element, report data, current repeated item, running totals, and design-mode state.BaseReportElementPropertiesEditorreceives aReportElementPropertiesContext. Use itsUpdatemethod so edits participate in the report's undo and refresh pipeline.IReportElementPdfRendererreturns Blazorise PDF elements positioned relative to the custom element.
Type names are case-insensitive and must be unique. Keep them stable after reports have been saved. If a saved report references an unavailable plugin, Reporting preserves its JSON data and shows an unavailable placeholder; PDF generation reports the missing renderer instead of silently dropping content.
Registration
Register reusable plugins with dependency injection by chaining AddReportElementPlugin<TPlugin>() after AddBlazoriseReporting(). A plugin can also be scoped to one report by passing instances through the ElementPlugins parameter. Per-report registration is useful for feature-specific elements and for examples that should not change the application-wide toolbox.
Application-wide registration
Register a plugin once to make it available to every report in the current scope.
builder.Services
.AddBlazoriseReporting()
.AddReportElementPlugin<ProgressBarReportElementPlugin>();
Custom progress element
This report registers a Progress Bar plugin on one report instance. The element appears under the Custom toolbox group, can be moved and resized like a built-in element, exposes undo-aware Caption, Value, and Color properties, and renders the same fixed geometry in HTML preview and PDF output.Insert aggregate
Running total
Insert group
Connect data source
No provider settings defined.
Edit formula
| Item | Formula |
| Description | Select a field, function, or operator to insert it into the formula. |
<Report Definition="" ElementPlugins="" Editable PreviewFormats="ReportPreviewFormat.Html | ReportPreviewFormat.Pdf" />
@code { private readonly IReportElementPlugin[] plugins = [new ProgressBarReportElementPlugin()]; private readonly ReportDefinition definition = new() { Name = "Plugin example", Pages = [ new() { Name = "Plugin", Bands = [ new() { Name = "Custom elements", Type = ReportBandType.Detail, Default = true, Height = 150, Elements = [ new ReportTextElementDefinition { Text = "Custom report element", X = 30, Y = 18, Width = 240, Height = 24, Font = new() { Size = 18, Bold = true, }, }, new ReportCustomElementDefinition { TypeName = ProgressBarReportElementPlugin.TypeName, Name = "Project completion", X = 30, Y = 60, Width = 200, Height = 38, Properties = new() { ["caption"] = "Project completion", ["value"] = 72, ["color"] = "#0D6EFD", }, }, ], }, ], }, ], }; }
Plugin definition and PDF renderer
The descriptor controls toolbox metadata and capabilities, while the factory initializes plugin-owned JSON properties. The PDF renderer maps the progress element to text and rectangle primitives positioned inside the element bounds.public sealed class ProgressBarReportElementPlugin : IReportElementPlugin, IReportElementPdfRenderer { public const string TypeName = "docs.progress-bar"; public ReportElementDescriptor Descriptor { get; } = new() { TypeName = TypeName, DisplayName = "Progress Bar", Category = "Custom", Icon = IconName.ChartBar, Width = 200, Height = 38, Capabilities = ReportElementCapabilities.Default, }; public Type RendererComponentType => typeof( ProgressBarReportElementRenderer ); public Type PropertiesComponentType => typeof( ProgressBarReportElementProperties ); public IReportElementPdfRenderer PdfRenderer => this; public ReportCustomElementDefinition CreateElement() { return new() { Properties = new() { ["caption"] = "Progress", ["value"] = 60, ["color"] = "#0D6EFD", }, }; } public IEnumerable<PdfElementDefinition> Render( ReportElementPdfRenderContext context ) { int value = GetValue( context.Element ); double trackHeight = Math.Min( 12, context.Element.Height ); double trackY = Math.Max( 0, context.Element.Height - trackHeight ); yield return CreateLabel( context.Element, GetCaption( context.Element ), TextAlignment.Start ); yield return CreateLabel( context.Element, $"{value}%", TextAlignment.End ); yield return CreateRectangle( context.Element.Width, trackY, trackHeight, "#E9ECEF" ); yield return CreateRectangle( context.Element.Width * value / 100d, trackY, trackHeight, GetColor( context.Element ) ); } private static string GetCaption( ReportCustomElementDefinition element ) => element?.Properties?["caption"]?.GetValue<string>() ?? "Progress"; private static int GetValue( ReportCustomElementDefinition element ) => Math.Clamp( element?.Properties?["value"]?.GetValue<int>() ?? 0, 0, 100 ); private static string GetColor( ReportCustomElementDefinition element ) => element?.Properties?["color"]?.GetValue<string>() ?? "#0D6EFD"; private static PdfElementDefinition CreateLabel( ReportCustomElementDefinition element, string text, TextAlignment alignment ) { return new() { Type = PdfElementType.Text, Width = element.Width, Height = Math.Min( 14, element.Height ), Text = text, Wrap = false, Font = new() { Size = 9, Bold = true, Alignment = alignment, VerticalAlignment = VerticalAlignment.Middle, Color = "#212529", }, Border = new() { Width = 0, }, }; } private static PdfElementDefinition CreateRectangle( double width, double y, double height, string color ) { return new() { Type = PdfElementType.Rectangle, Y = y, Width = width, Height = height, Border = new() { Width = 0, }, Appearance = new() { BackgroundColor = color, }, }; } }
HTML renderer
The renderer receives current report and data state through its context and draws only inside the element content box.@inherits BaseReportElementRenderer <Div Height="Height.Is100" Width="Width.Is100" Flex="Flex.Column.JustifyContent.Between"> <Div Flex="Flex.JustifyContent.Between.AlignItems.Center" Style="height:14pt;font-size:9pt;line-height:14pt;font-weight:700;color:#212529"> <Span>@ProgressBarReportElementPlugin.GetCaption( Context.Element )</Span> <Span>@($"{Value}%")</Span> </Div> <Div Style="height:12pt;background-color:#E9ECEF;overflow:hidden"> <Div Height="Height.Is100" Style="" /> </Div> </Div>
@code { private int Value => ProgressBarReportElementPlugin.GetValue( Context.Element ); private string FillStyle => $"width:{Value}%;background-color:{ProgressBarReportElementPlugin.GetColor( Context.Element )}"; }
Properties editor
Plugin property changes are applied through the context so they participate in undo, refresh, and multi-selection updates.@inherits BaseReportElementPropertiesEditor <Field Horizontal> <FieldLabel ColumnSize="ColumnSize.Is4" TextSize="TextSize.Small">Caption</FieldLabel> <FieldBody ColumnSize="ColumnSize.Is8"> <TextInput Value="@ProgressBarReportElementPlugin.GetCaption( Context.Element )" ValueChanged="" Size="Size.Small" /> </FieldBody> </Field> <Field Horizontal> <FieldLabel ColumnSize="ColumnSize.Is4" TextSize="TextSize.Small">Value</FieldLabel> <FieldBody ColumnSize="ColumnSize.Is8"> <NumericInput TValue="int" Value="@ProgressBarReportElementPlugin.GetValue( Context.Element )" ValueChanged="" Min="0" Max="100" Immediate="true" Size="Size.Small" /> </FieldBody> </Field> <Field Horizontal> <FieldLabel ColumnSize="ColumnSize.Is4" TextSize="TextSize.Small">Color</FieldLabel> <FieldBody ColumnSize="ColumnSize.Is8"> <ColorPicker Value="@ProgressBarReportElementPlugin.GetColor( Context.Element )" ValueChanged="" Size="Size.Small" /> </FieldBody> </Field>
@code { private Task OnCaptionChanged( string value ) => Context.Update( element => element.Properties["caption"] = value ); private Task OnValueChanged( int value ) => Context.Update( element => element.Properties["value"] = Math.Clamp( value, 0, 100 ) ); private Task OnColorChanged( string value ) => Context.Update( element => element.Properties["color"] = value ); }
Rendering responsibilities
Renderer components draw only inside the element's content box. They should not implement designer selection, dragging, resizing, or absolute positioning because the standard report element shell already provides those behaviors. Keep plugin properties JSON-serializable and use SchemaVersion when the payload format evolves.
PDF renderers receive the same report, band, data item, running totals, and custom properties as HTML renderers. Returned PDF element coordinates are relative to the custom element. A plugin that supports HTML only can leave PdfRenderer unset, but PDF export will then fail explicitly when the element is present.
The first plugin release supports leaf elements. Custom container semantics and automatic content measurement remain owned by built-in elements such as panels and tables.
API
Parameters
BaseReportElementRenderer
| Parameter | Description | Type | Default |
|---|---|---|---|
Context |
Gets or sets the custom element render context. |
ReportElementRenderContext | null |
BaseReportElementPropertiesEditor
| Parameter | Description | Type | Default |
|---|---|---|---|
Context |
Gets or sets the custom element properties context. |
ReportElementPropertiesContext | null |