Skip to main content

Reuse and Customize Components

  • 5 minutes to read

Blazor framework allows you to build and reuse composite components. You can also render parts or entire components dynamically in C#.

Composite Components

This section describes how to create a composite Blazor component.

  1. In Solution Explorer, right-click a folder with components and select AddNew Item. In the Add New Item dialog, select Razor Component, enter the component’s name, and click Add.

    Add New Item

  2. Add DevExpress Blazor components to the newly created *.razor file:

    <h3>My Component</h3>
    
    <DxCalendar VisibleDate="@DateTime.Today" />
    
    <DxTextBox Text="@Text"
               ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
               NullText="Type text..." />
    
    <DxButton Text="My Button" />
    
    @code {
        [Parameter]
        public string Text { get; set; }
    }
    
  3. Add MyComponent code to a page (for example, Index.razor):

    <MyComponent Text="My Text" />
    

    My Component

Refer to Microsoft documentation for more information on how to create ASP.NET Core Razor components.

Build Components Dynamically

You can use the following methods to render Blazor components dynamically:

Render Fragments

A render fragment is a custom UI element that you can reuse on pages or in component markup. To define a fragment, use either Razor template syntax or the RenderFragment delegate. To insert the fragment into markup, use the @ symbol.

Razor Template Syntax

The following code snippet demonstrates the renderTextBox fragment that renders a Text Box component.

@{
    RenderFragment<string> renderTextBox = (string model) =>
    @<DxTextBox Text="@model"></DxTextBox>;
}

@renderTextBox("MyText")

The following code snippet demonstrates the renderGrid fragment that renders the Grid component.

@{
    RenderFragment<DataItem> renderGrid = (model) =>
    @<DxGrid @key="@model" Data="@model.Data">
        @foreach (var column in model.ColumnNames) {
            <DxGridDataColumn FieldName="@column" @key="@column" />
        }
    </DxGrid>; 
}

@renderGrid(dataObject)

Note that you cannot use this approach in code-behind (.cs) files.

RenderFragment Delegate

You can use the RenderFragment delegate to define render fragments for simple, nested components, and components that include <Template> elements.

The code above uses template syntax to render a Text Box component. You can render the same component dynamically using the RenderFragment delegate. To define a render fragment, call the RenderTreeBuilder‘s methods: OpenComponent, AddAttribute, and CloseComponent.

@renderTextBox()

@code {
    private RenderFragment renderTextBox() {
        RenderFragment item = b => {
            b.OpenComponent<DxTextBox>(0);
            b.AddAttribute(1, "Text", "My text");
            b.CloseComponent();
        };
        return item;
    }
}

Optionally, you can call the AddComponentReferenceCapture method to capture a reference to the component, for example:

builder.AddComponentReferenceCapture(n++, grid => Grid = (IGrid)grid);

Note

Refer to Manually build a render tree for more information on how to use RenderTreeBuilder.

RenderFragment Delegate for Nested Components

You can create render fragments for nested components. For example, you need to build a Form Layout that contains a tabbed group with an item:

<DxFormLayout>
    <DxFormLayoutTabPage Caption="My Tab">
        <DxFormLayoutItem Caption="DynLayoutItem" ColSpanMd="6">
        </DxFormLayoutItem>
    </DxFormLayoutTabPage>
</DxFormLayout>

To build this component, define a render fragment that contains <DxFormLayoutTabPage>. Then add the ChildContent attribute that specifies a nested render fragment for <DxFormLayoutItem>. To add the attribute, call the AddAttribute(sequence, “ChildContent”, RenderFragment) method.

<DxFormLayout>
    <DxFormLayoutTabPages>
        @renderLayoutTabPage()
    </DxFormLayoutTabPages>
</DxFormLayout>

@code {
    private RenderFragment renderLayoutTabPage() {
        RenderFragment item = b => {
            b.OpenComponent<DxFormLayoutTabPage>(0);
            b.AddAttribute(1, "Caption", "My tab");
            b.AddAttribute(2, "ChildContent", (RenderFragment)((tabPageBuilder) => {
                tabPageBuilder.OpenComponent<DxFormLayoutItem>(0);
                tabPageBuilder.AddAttribute(1, "Caption", "DynLayoutItem");
                tabPageBuilder.AddAttribute(2, "ColSpanMd", 6); 
                tabPageBuilder.CloseComponent();
            }));
            b.CloseComponent();
        };
        return item;
    }
}

A component can contain a template with another component, for example:

<DxFormLayout>
    <DxFormLayoutTabPage Caption="My Tab">
        <DxFormLayoutItem Caption="DynLayoutItem" ColSpanMd="6">
            <Template>
                <DxTextBox @bind-Text="@text"></DxTextBox>
            </Template>
        </DxFormLayoutItem>
    </DxFormLayoutTabPage>
</DxFormLayout>

To build this component, use the following code snippet. To add the <Template> attribute, use the AddAttribute(sequence, “Template”, RenderFragment<Object>delegate) method.

<DxFormLayout>
    <DxFormLayoutTabPages>
        @renderLayoutTabPage()
    </DxFormLayoutTabPages>
</DxFormLayout>

@code {
    private RenderFragment renderLayoutTabPage() {
        RenderFragment item = b => {
            b.OpenComponent<DxFormLayoutTabPage>(0);
            b.AddAttribute(1, "Caption", "My tab");
            b.AddAttribute(2, "ChildContent", (RenderFragment)((tabPageBuilder) => {
                tabPageBuilder.OpenComponent<DxFormLayoutItem>(0);
                tabPageBuilder.AddAttribute(1, "Caption", "DynLayoutItem");
                tabPageBuilder.AddAttribute(2, "ColSpanMd", 6);
                tabPageBuilder.AddAttribute(3, "Template", (RenderFragment<Object>)((context) => ((itemTemplate) => {
                    itemTemplate.OpenComponent<DxTextBox>(0);
                    itemTemplate.AddAttribute(1, "Text", text);
                    itemTemplate.CloseComponent();
                })));
                tabPageBuilder.CloseComponent();
            }));
            b.CloseComponent();
        };
        return item;
    }
}

BuildRenderTree Method

To create and reuse a modified version of an existing DevExpress Blazor component, create a ComponentBase descendant and override the BuildRenderTree method to generate the component’s content.

For example, you need to add multiple Grid components to an application, and some of them (Group 1) should have the same settings. You can end up with repeated code like demonstrated in the following example:

Group 1
<DxGrid PageSize="5" ShowFilterRow="false" PagerVisible="false" ShowGroupPanel="true" ...>
<DxGrid PageSize="5" ShowFilterRow="false" PagerVisible="false" ShowGroupPanel="true" ...>
<DxGrid PageSize="5" ShowFilterRow="false" PagerVisible="false" ShowGroupPanel="true" ...>
...
Group 2
<DxGrid PageSize="15" ShowFilterRow="false" ShowGroupPanel="false" SelectionMode="GridSelectionMode.Single" ...>
...

You can turn repeated code into a single composite parameter (for example, Settings). This parameter can accept a dictionary with grid settings.

  1. Create a ComponentBase descendant (for example, MyGrid).
  2. Add the Data, ChildContent (for columns), and Settings parameters to the class.
  3. Override the BuildRenderTree method to define the MyGrid<T> component’s markup. This method accepts a RenderTreeBuilder object. Use its methods to add <DxGrid> to the markup and specify the component’s attributes.

    public class MyGrid<T> : ComponentBase {
        [Parameter]
        public IEnumerable<T> Data { get; set; }
        [Parameter]
        public RenderFragment ChildContent { get; set; }
        [Parameter]
        public Dictionary<string, object> Settings { get; set; }
        protected override void BuildRenderTree(RenderTreeBuilder builder) {
            builder.OpenComponent<DxGrid>(0);
            builder.AddAttribute(1, "Data", (object)Data);
            builder.AddAttribute(2, "Columns", ChildContent);
            if (Settings != null) {
                builder.AddMultipleAttributes(3, Settings);
                //OR
                //int seq = 3;
                //foreach (var item in Settings) {
                //    builder.AddAttribute(seq++, item.Key, item.Value);
                //}
            }
            builder.CloseComponent();
        }
    }
    
  4. Add the MyGrid component to a page.

    <MyGrid Data="Forecasts" Settings="InputAttributes">
        <DxGridCommandColumn Width="150px" />
        <DxGridDataColumn FieldName="@nameof(WeatherForecast.Date)"></DxGridDataColumn>
        <DxGridDataColumn FieldName="@nameof(WeatherForecast.TemperatureC)"></DxGridDataColumn>
        <DxGridDataColumn FieldName="@nameof(WeatherForecast.TemperatureF)"></DxGridDataColumn>
        <DxGridDataColumn FieldName="@nameof(WeatherForecast.Summary)"></DxGridDataColumn>
    </MyGrid>
    
    @code {
        public List<WeatherForecast> Forecasts { get; set; }
        public Dictionary<string, object> InputAttributes { get; set; } =
            new Dictionary<string, object>() {
                { "PageSize", 5 },
                { "ShowFilterRow", false },
                { "PagerVisible" , false },
                { "ShowGroupPanel", true }
            };
        protected override async Task OnInitializedAsync() {
            base.OnInitialized();
            WeatherForecast[] data = await ForecastService.GetForecastAsync(DateTime.Now);
            Forecasts = data.ToList();
        }
    }
    

View Example: How to use a dictionary to configure the Grid state View Example: Set predefined settings for the specific component