Back to Devexpress

Templates

devextremeaspnetmvc-401287-concepts-templates.md

latest19.4 KB
Original Source

Templates

  • Oct 01, 2025
  • 11 minutes to read

This article explains how to implement and apply templates. Templates allow you to customize how the control parts (titles, cells, items, and so on) are rendered.

Use *Template() methods to define templates, for example:

  • DataGridColumnBuilder.CellTemplate - specifies a template for column cells in a DataGrid control.
  • ListBuilder.ItemTemplate - specifies a template for items in a List control.
  • PopupBuilder.ContentTemplate - specifies a template for a Popup control’s content.

Template Syntax

A template consists of Razor markup and ERB-style constructs (<% %>) that can use parameters. To define a template, use the @<text> block in a control’s *Template(RazorBlock templateContent) method.

Note

Razor VB : When you use the @<text> block:

  • enclose the control configuration with @Code/End Code;

  • end the control configuration with Render().

  • Razor C#

  • Razor VB

cshtml
@(Html.DevExtreme().List()
    .DataSource(DataSource)
    .ItemTemplate(@<text>
        <div><%- Name %></div>
    </text>)
)
vb
@Code
    Html.DevExtreme().List() _
        .DataSource(DataSource) _
        .ItemTemplate(Sub()
            @<text>
                <div><%- Name %></div>
            </text>
        End Sub) _
        .Render()
End Code

The List control is bound to the following data source:

csharp
object[] DataSource = new[] {
    new { Name = "John" },
    new { Name = "Jane" }
};
vb
Dim DataSource = {
    New With {.Name = "John"},
    New With {.name = "Jane"}
}

You can also use @Html in templates, for example, to nest controls or access standard HTML helpers.

If a template is short and does not use Razor constructs (started with @), you can use a shorthand overload of a *Template method with a String argument:

cshtml
@(Html.DevExtreme().List()
    .DataSource(DataSource)
    .ItemTemplate("<div><%- Name %></div>")
)
vb
@(Html.DevExtreme().List() _
    .DataSource(DataSource) _
    .ItemTemplate("<div><%- Name %></div>")
)

External Templates

You can define templates outside of a control declaration. This can be useful in the following cases:

  • templates are large;
  • you want to reuse a template;
  • you need to nest templates (the code below demonstrates how to nest the List control within the Popup control).

There are two ways to declare external templates:

  • Extract the template markup to a partial Razor file.

  • Use named templates.

ERB-style Constructs

Template syntax allows you to embed JavaScript code within a pair of <% and %> delimiters. There are three types of tags:

  • HTML-encoded expression tags

  • Non-HTML-encoded expression tags

  • Execution tags

Note

ERB-style expressions are executed on a client. You cannot use server code in them.

Access Template Parameters

Template parameters are the *Data or *Info objects that are specific for different templates and are documented in the client API reference. For example:

  • The cellInfo object of a DataGrid column’s cellTemplate provides a predefined set of properties: value, text, columnIndex, and so on.
  • The itemData object of a List’s itemTemplate is a list item that comes from items or dataSource.

The entire objects can be referenced in a template as obj. Properties of the *Data or *Info object are available directly in a template scope. These properties are called free variables.

The example below shows how to access the Name property.

cshtml
@(Html.DevExtreme().List()
    .DataSource(DataSource)
    .ItemTemplate(@<text>
        <%- Name %> <!-- Name as a free variable -->
        <%- obj.Name %> <!-- Name as a property of obj -->
    </text>)
)
vb
@Code
    Html.DevExtreme().List() _
        .DataSource(DataSource) _
        .ItemTemplate(Sub()
            @<text>
                <%- Name %>
                <%- obj.Name %>
            </text>
        End Sub) _
        .Render()
End Code

The List control is bound to the following data source:

csharp
object[] DataSource = new[] {
    new { Name = "John" },
    new { Name = "Jane" }
};
vb
Dim DataSource = {
    New With {.Name = "John"},
    New With {.Name = "Jane"}
}

You can use obj and free variables in JavaScript code within a template. Refer to Bind a Nested Control to Template Parameters for more information.

JavaScript Functions as Templates

You can define templates as JavaScript functions like you do in DevExtreme (refer to the Function section of the template article) and use them to:

  • implement complex logic (like conditional render);
  • modify a template container element;
  • access extra parameters passed by DevExtreme.

Use *Template(new JS(...)) to define a template as a function.

In the example below, the List’s ItemTemplate is declared as the myList_itemTemplate function that renders items content and applies a custom style to the items using jQuery. The JavaScript function approach is used to access the itemIndex and itemElement parameters in the template. These parameters are not available if you use ERB-style constructs (refer to Access Template Parameters).

cshtml
@(Html.DevExtreme().List()
    .DataSource(DataSource)
    .ItemTemplate(new JS("myList_itemTemplate"))
)
vb
@(Html.DevExtreme().List() _
    .DataSource(DataSource) _
    .ItemTemplate(New JS("myList_itemTemplate")) _
)
js
<script>
    function myList_itemTemplate(itemData, itemIndex, itemElement) {
        itemElement
            .addClass("my-custom-style")
            .append(
                $("<span>").text("Item index: " + itemIndex + ", Name: " + itemData.Name)
            );
    }
</script>

Nested Controls

You can use DevExtreme controls within templates. For example, the following code nests the DataGrid control in the Popup control:

cshtml
@(Html.DevExtreme().Popup()
    // ...
    // Specifies the contents of the Popup control
    .ContentTemplate(@<text>
        @(Html.DevExtreme().DataGrid<Sale>()
            .Columns(columns => {
                columns.AddFor(m => m.Region);
                columns.AddFor(m => m.City);
                columns.AddFor(m => m.Amount);
                columns.AddFor(m => m.Date);
            })
        )
    </text>)
)
vb
@Code
    Html.DevExtreme().Popup() _
        .ContentTemplate(Sub()
            @<text>
                @(Html.DevExtreme().DataGrid(Of Sale)() _
                    .DataSource(Function(d) d.WebApi().Controller("GridData")) _
                    .Columns(Sub(columns)
                        columns.AddFor(Function(m) m.Region)
                        columns.AddFor(Function(m) m.City)
                        columns.AddFor(Function(m) m.Amount)
                        columns.AddFor(Function(m) m.Date)
                    End Sub)
                )
            </text>
        End Sub) _
        .Render()
End Code

The popup should look like this:

You can find this code in the Demo: Drill Down.

Note

  • Since Razor allows only one level of @<text> tags, you cannot define a template within another template. Use external templates for high-level nesting.
  • If you use DevExtreme controls within an ItemTemplate or a CellTemplate template, ensure you specify a unique ID in the nested control’s ID option. Alternatively, do not specify this option. In this case, nested control instances get auto-generated IDs.

Bind a Nested Control to Template Parameters

A nested control’s configuration can depend on template parameters.

The example below defines a template for DataGrid cells. A cell contains a button whose configuration depends on the cellTemplate‘s value free variable.

  • To pass value to the Text method, the Text(JS) overload is used.
  • To pass value to the handleGridButtonClick(cellValue) event handler, the short anonymous function is used. This function captures the value free variable and passes it to the event handler as an argument.

Note

If a handler is short, you do not need to extract it to an external function - you can use a short inline function instead, for example:

.OnClick("function() { alert(value); }")

Refer to Handle Events and Define Callbacks for more information.

cshtml
@(Html.DevExtreme().DataGrid()
    // ...
    .Columns(columns => {
        columns.Add()
            .DataField("Name")
            .CellTemplate(@<text>
                @(Html.DevExtreme().Button()
                    .Text(new JS("value"))
                    .OnClick("function() { handleGridButtonClick(value); }")
                )
            </text>);
    })
)
vb
@Code
    Html.DevExtreme().DataGrid() _
        .Columns(Sub(columns)
            columns.Add() _
                .DataField("Name") _
                .CellTemplate(Sub()
                    @<text>
                        @(Html.DevExtreme().Button() _
                            .Text(New JS("value")) _
                            .OnClick("function() { handleGridButtonClick(value); }")
                        )
                    </text>
                End Sub)
        End Sub) _
        .Render()
End Code
js
<script>
    function handleGridButtonClick(cellValue) {
        alert("Cell value:" + cellValue);
    }
</script>

Example: Master-Detail Grid

A common use-case of binding a nested control to template parameters is the Master-Detail Grid. In the following code, the detail section nests another DataGrid. The data.OrderID free variable is used in the LoadParams of its DataSource.

cshtml
@(Html.DevExtreme().DataGrid()
    .DataSource(d => d.WebApi().Controller("DataGridMasterDetailView").Key("ID"))
    .Columns(columns => {
        columns.Add().DataField("FirstName");
        columns.Add().DataField("LastName");
        // ...
    })
    // Configures the Master-Detail UI
    .MasterDetail(md => md
        .Enabled(true)
        // Specifies the contents of the detail section
        .Template(@<text>
            @(Html.DevExtreme().DataGrid()
                .DataSource(d => d.WebApi()
                    .Controller("DataGridMasterDetailView")
                    .LoadAction("TasksDetails")
                    // Use "data.ID" in LoadParams
                    .LoadParams(new { id = new JS("data.ID") })
                )
            )
        </text>)
    )
)
vb
@Code
    Html.DevExtreme().DataGrid() _
        .DataSource(Function(d) d.WebApi().Controller("DataGridMasterDetailView").Key("ID")) _
        .Columns(Sub(columns)
            columns.Add().DataField("FirstName")
            columns.Add().DataField("LastName")
        End Sub) _
        .MasterDetail(Sub(md) md _
            .Enabled(True) _
            .Template(Sub()
                @<text>
                    @(Html.DevExtreme().DataGrid() _
                        .DataSource(Function(d) d.WebApi() _
                            .Controller("DataGridMasterDetailView") _
                            .LoadAction("TasksDetails") _
                            .LoadParams(New With { .id = New JS("data.ID") })
                        )
                    )
                </text>
            End Sub)
            ) _
        .Render()
End Code

Demo: Master-Detail View

Multi-Nested Controls

There are cases when template parameters should be passed through an intermediate control. For example, if you place a detail DataGrid in a TabPanel, this DataGrid has access to Tab template’s parameters, but does not see parameters of a master DataGrid’s detail template. To solve this situation, attach the tabExtras custom option to the Tab, then you can access it in the tab1Template template:

cshtml
@(Html.DevExtreme().DataGrid()
    .KeyExpr("ID")
    .DataSource(MasterGridDataSource, key: "ID")
    .MasterDetail(m => m
        .Enabled(true)
        .Template(@<text>
            @(Html.DevExtreme().TabPanel()
                .Items(items => {
                    items.Add()
                        .Title("Tab 1")
                        .Option("tabExtras", new {
                            masterKey = new JS("key")
                        })
                        .Template(new TemplateName("tab1Template"));
                })
            )
        </text>)
    )
)

@using (Html.DevExtreme().NamedTemplate("tab1Template")) {
    <!-- Use tabExtras.masterKey to configure a detail grid -->
}
vb
@Code
    Html.DevExtreme().DataGrid() _
        .KeyExpr("ID") _
        .DataSource(MasterGridDataSource, key:= "ID") _
        .MasterDetail(Sub(md) md _
            .Enabled(True) _
            .Template(Sub()
                @<text>
                    @(Html.DevExtreme().TabPanel() _
                        .Items(Sub(items)
                            items.Add() _
                                .Title("Tab 1") _
                                .Option("tabExtras", New With {.masterKey = New JS("key")}) _
                                .Template(New TemplateName("tab1Template"))
                        End Sub)
                    )
                </text>
            End Sub)
        ) _
        .Render()
End Code

@Using (Html.DevExtreme().NamedTemplate("tab1Template"))
    ' Use tabExtras.masterKey to configure a detail grid 
End Using

The DataGrid control is bound to the following data source:

csharp
object[] MasterGridDataSource = new[] {
    new { ID = 1, Name = "John" },
    new { ID = 2, Name = "Jane" }
};
vb
Dim MasterGridDataSource = {
    New With {.ID = 1, .Name = "John"},
    New With {.ID = 2, .Name = "Jane"}
}

Demo: Advanced Master-Detail View

HTML Helpers in Templates

You can use HTML helpers in templates as usual if they render static content. For instance, if you configure grid columns and a column’s cells should display the same link. The code below shows how to use the ActionLink HTML helper to render this link.

cshtml
@(Html.DevExtreme().DataGrid()
    .Columns(columns => {
        //...
        columns.Add().CellTemplate(@<text>
            @Html.ActionLink("Link Text", "Details")
        </text>);
    })
)
vb
@Code
    Html.DevExtreme().DataGrid() _
        .Columns(Sub(columns)
            columns.Add().CellTemplate(Sub()
                @<text>
                    @Html.ActionLink("Link Text", "Details")
                </text>
            End Sub)
        End Sub) _
        .Render()
End Code

If you need to use template parameters, use the following approach:

cshtml
@(Html.DevExtreme().DataGrid()
    .Columns(columns => {
        columns.Add().CellTemplate(@<text>
            @Html.Raw(
                Html.ActionLink("Link Text", "Details", "ControllerName", new { id = "ID_PLACEHOLDER" }, null)
                    .ToString()
                    .Replace("ID_PLACEHOLDER", "<%- data.OrderID %>")
            )
        </text>);
    })
)
vb
@Code
    Html.DevExtreme().DataGrid() _
        .Columns(Sub(columns)
            columns.Add().CellTemplate(Sub()
                @<text>
                    @Html.Raw(
                        Html.ActionLink("Link Text", "Details", "ControllerName", New With {.id = "ID_PLACEHOLDER"}, Nothing) _
                            .ToString() _
                            .Replace("ID_PLACEHOLDER", "<%- data.OrderID %>")
                    )
                </text>
            End Sub)
        End Sub) _
        .Render()
End Code

This code demonstrates how to create a template if the links in the grid column depend on the OrderID. The ActionLink has a dynamic route value (id) the data.OrderID template parameter should provide. However, you should use a placeholder because data.OrderID cannot be passed to id (the ActionLink is generated on the server and data.OrderID receives a value only on the client).

The resulting expression:

js
<a href="/ControllerName/Details/"<%- data.OrderID %>">Link Text</a>

Note

You can use HTML elements instead of helpers. The examples below demonstrate this.

cshtml
@(Html.DevExtreme().DataGrid()
    .Columns(columns => {
        columns.Add().CellTemplate(@<text>
            <a href="@Url.Action("ActionName", "ControllerName")/<%- data.OrderID %>">Link Text</a>
        </text>);
    })
)
vb
@Code
    Html.DevExtreme().DataGrid() _
        .Columns(Sub(columns)
            columns.Add().CellTemplate(Sub()
                @<text>
                    <a href="@Url.Action("ActionName", "ControllerName")/<%- data.OrderID %>">Link Text</a>
                </text>
            End Sub)
        End Sub) _
        .Render()
End Code