# Devexpress Blazor Treelist

> Build and configure the DevExpress Blazor TreeList (DxTreeList) — a hierarchical data grid / tree grid for Blazor Server, WebAssembly, and Hybrid apps. Use when displaying tree-structured or parent-child data; binding flat data with KeyFieldName, ParentKeyFieldName, and RootValue; expanding/collapsing nodes; sorting, filtering, search box, and filter panel; implementing CRUD editing for tree node…

- **Type:** Skill
- **Install:** `agentstack add skill-devexpress-agent-skills-devexpress-blazor-treelist`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [DevExpress](https://agentstack.voostack.com/s/devexpress)
- **Installs:** 0
- **Category:** [Data & Analytics](https://agentstack.voostack.com/c/data-and-analytics)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [DevExpress](https://github.com/DevExpress)
- **Source:** https://github.com/DevExpress/agent-skills/tree/main/plugins/dx-blazor/skills/devexpress-blazor-treelist
- **Website:** https://www.devexpress.com

## Install

```sh
agentstack add skill-devexpress-agent-skills-devexpress-blazor-treelist
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# DevExpress Blazor TreeList

`DxTreeList` is a hierarchical data grid for Blazor. It displays data as a tree with expandable/collapsible rows based on parent-child key relationships. It shares most of DxGrid's feature set — sorting, filtering, CRUD editing, export, selection — and adds tree-specific capabilities such as multi-level expand/collapse, load-on-demand child nodes, and tree filtering modes.

## When to Use This Skill

- Display hierarchical data (organizational charts, product categories, file systems, bill of materials)
- Bind flat data with parent-child ID relationships (`KeyFieldName` + `ParentKeyFieldName`)
- Sort, filter, or page tree nodes
- Implement CRUD for hierarchical data (create, edit, delete nodes)
- Export tree data to CSV, XLS/XLSX, or PDF
- Load child nodes on demand from a remote API
- Select single or multiple tree nodes with checkboxes
- Reorder tree nodes within the TreeList or move rows between TreeLists and Grids with drag-and-drop
- Change node hierarchy (re-parent nodes) via drag-and-drop

## Prerequisites & Installation

### NuGet Package

| Package | Purpose |
|---|---|
| `DevExpress.Blazor` | TreeList + all standard Blazor UI components |

```bash
# Install from NuGet.org:
dotnet add package DevExpress.Blazor
```

### Setup (existing project)

1. Register DevExpress resources in `Program.cs`:
   ```csharp
   builder.Services.AddDevExpressBlazor();
   ```
    > **v26.1 note**: `DevExpress.Blazor` no longer includes `options.BootstrapVersion` or `DevExpress.Blazor.BootstrapVersion`. Do not generate either API.
2. Apply a theme and add client scripts in `App.razor` inside ``:
   ```razor
   @using DevExpress.Blazor
   @DxResourceManager.RegisterTheme(Themes.Fluent)
   @DxResourceManager.RegisterScripts()
   ```
3. Add the namespace to `_Imports.razor`:
   ```razor
   @using DevExpress.Blazor
   ```

## Before You Start — Ask the Developer

Before generating code, ask:

1. **Render mode**: Are you using `InteractiveServer`, `InteractiveWebAssembly`, or `InteractiveAuto`? (TreeList requires an interactive render mode for tree expansion, filtering, and editing.)
2. **Data structure**: Is your data a flat list with parent ID references, or is it already a nested object graph?
3. **Key fields**: What are the primary key field and the parent key field names? What is the root value (the parent ID of root nodes — `null`, `0`, or something else)?
4. **Features needed**: Do you need editing? Export? Selection? Load-on-demand children?
5. **New or existing project?**: Are you adding the TreeList to an existing project or starting fresh?

## Component Overview

`DxTreeList` provides:

- **Data Binding** (`Data`, `KeyFieldName`, `ParentKeyFieldName`): Binds flat data with parent-child relationships; `RootValue` defines root nodes
- **Column Types** (`DxTreeListDataColumn`, `DxTreeListCommandColumn`, `DxTreeListSelectionColumn`, `DxTreeListBandColumn`): Same column model as DxGrid
- **Tree Navigation** (`AllowExpandCollapse`, `ExpandedRowKeys`): Expand/collapse tree levels, expand all, collapse all
- **Data Shaping** (`AllowSort`, `ShowSearchBox`, `FilterPanelDisplayMode`): Sort, filter row, filter panel, search box
- **Editing** (`EditMode`, `EditModelSaving`, `DataItemDeleting`): EditRow, EditForm, PopupEditForm, EditCell
- **Selection** (`SelectionMode`, `SelectedDataItems`): Single and multiple node selection
- **Export** (`ExportToCsvAsync`, `ExportToXlsxAsync`, `ExportToPdfAsync`): CSV, XLS/XLSX, PDF
- **Load on Demand** (`ChildrenLoaded` event): Load child nodes asynchronously when a node is expanded
- **Summary** (`TotalSummary`, `DxTreeListSummaryItem`): Total aggregate summaries — Sum, Min, Max, Avg, Count — displayed in the footer
- **Focused Row** (`FocusedRowEnabled`): Highlights a single row on click; use `GetFocusedRowIndex()`, `GetFocusedDataItem()`, and `SetFocusedRowIndex()` to work with the current row
- **Toolbar** (`ToolbarTemplate`): Embed a toolbar at the top of the TreeList with custom action buttons and data shaping controls
- **Drag-and-Drop** (`AllowDragRows`, `AllowedDropTarget`, `ItemsDropped`): Row reordering within the same TreeList, moving rows between TreeLists and Grids, and changing node hierarchy (re-parenting); requires `ObservableCollection` for automatic UI refresh

### Core Entry Point (Razor)

```razor
@rendermode InteractiveServer


    
        
        
        
    

```

## Documentation & Navigation Guide

### Getting Started
📄 [references/getting-started.md](references/getting-started.md)

When you need to:
- Set up the TreeList from scratch
- Create your first hierarchical data display
- Understand key field configuration

### Data Binding
📄 [references/data-binding.md](references/data-binding.md)

When you need to:
- Bind to flat data with `KeyFieldName` / `ParentKeyFieldName`
- Configure `RootValue` for non-null/non-zero root nodes
- Load child nodes on demand with the `ChildrenLoaded` event

### Editing & Validation
📄 [references/editing-and-validation.md](references/editing-and-validation.md)

When you need to:
- Enable CRUD for tree nodes
- Add / edit / delete nodes with `EditModelSaving` and `DataItemDeleting`
- Customize the edit form
- Open the edit form from a toolbar or external button for the current focused or selected row

### Data Shaping
📄 [references/data-shaping.md](references/data-shaping.md)

When you need to:
- Sort by columns, add filter row, search box
- Create total or group summaries
- Show the filter panel or customize filter-builder operators for a specific field

### Export
📄 [references/export.md](references/export.md)

When you need to:
- Export tree data to CSV, XLSX, or PDF
- Control which rows are exported (expanded, all, selected)

### Drag-and-Drop
📄 [references/drag-and-drop.md](references/drag-and-drop.md)

When you need to:
- Reorder rows within the TreeList
- Move rows between TreeLists or Grids
- Change node hierarchy (re-parent nodes) via drag-and-drop
- Handle the `ItemsDropped` event to update the data source

### Examples
💻 [examples/quickstart.razor](examples/quickstart.razor) — Hierarchical CRUD with `CustomizeEditModel`, search box, summaries, and export  
💻 [examples/edit-form-selected-item.razor](examples/edit-form-selected-item.razor) — `EditFormTemplate` plus an external button that edits the current selected/focused row  
💻 [examples/filter-panel-custom-date-operators.razor](examples/filter-panel-custom-date-operators.razor) — `FilterPanelDisplayMode` with a custom Filter Builder that removes month operators for `DueDate`  
💻 [examples/load-on-demand.razor](examples/load-on-demand.razor) — Async child loading via `ChildrenLoaded` for large trees

## Quick Start Example

```razor
@page "/treelist-demo"
@rendermode InteractiveServer


    
        
        
        
        
        
        
    


@code {
    List Tasks { get; set; }

    protected override void OnInitialized() {
        Tasks = new List {
            new TaskItem { Id = 1, ParentId = 0, Name = "Project Alpha", AssignedTo = "Alice", StartDate = DateTime.Today, DueDate = DateTime.Today.AddMonths(3), Status = "Active" },
            new TaskItem { Id = 2, ParentId = 1, Name = "Design Phase", AssignedTo = "Bob", StartDate = DateTime.Today, DueDate = DateTime.Today.AddDays(30), Status = "In Progress" },
            new TaskItem { Id = 3, ParentId = 1, Name = "Development Phase", AssignedTo = "Carol", StartDate = DateTime.Today.AddDays(31), DueDate = DateTime.Today.AddDays(90), Status = "Pending" },
            new TaskItem { Id = 4, ParentId = 3, Name = "Backend API", AssignedTo = "Dave", StartDate = DateTime.Today.AddDays(31), DueDate = DateTime.Today.AddDays(60), Status = "Pending" },
            new TaskItem { Id = 5, ParentId = 3, Name = "Frontend UI", AssignedTo = "Eve", StartDate = DateTime.Today.AddDays(61), DueDate = DateTime.Today.AddDays(90), Status = "Pending" },
        };
    }

    async Task OnEditModelSaving(TreeListEditModelSavingEventArgs e) {
        var model = (TaskItem)e.EditModel;
        if (e.IsNew) {
            model.Id = Tasks.Max(t => t.Id) + 1;
            Tasks.Add(model);
        } else {
            e.CopyChangesToDataItem();
        }
    }

    async Task OnDataItemDeleting(TreeListDataItemDeletingEventArgs e) {
        var item = (TaskItem)e.DataItem;
        // Remove the item and all its descendants
        RemoveWithDescendants(item.Id);
    }

    void RemoveWithDescendants(int id) {
        var children = Tasks.Where(t => t.ParentId == id).Select(t => t.Id).ToList();
        foreach (var childId in children)
            RemoveWithDescendants(childId);
        Tasks.RemoveAll(t => t.Id == id);
    }

    class TaskItem {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public string Name { get; set; }
        public string AssignedTo { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime DueDate { get; set; }
        public string Status { get; set; }
    }
}
```

## Key Properties & API Surface

### DxTreeList

| Property / Method | Type | Description |
|---|---|---|
| `Data` | `object` | Data source (`IEnumerable` or `IListSource`) |
| `KeyFieldName` | `string` | Unique key field for tree node identification |
| `ParentKeyFieldName` | `string` | Field containing each node's parent key |
| `RootValue` | `object` | Parent key value of root nodes (default: `null`) |
| `EditMode` | `TreeListEditMode` | EditRow, EditForm, PopupEditForm, EditCell |
| `SelectionMode` | `TreeListSelectionMode` | Single or Multiple |
| `SelectedDataItems` | `IReadOnlyList` | Currently selected items (two-way bindable) |
| `PageSize` | `int` | Rows per page |
| `ShowSearchBox` | `bool` | Show/hide the search box |
| `AllowSort` | `bool` | Enable/disable column sorting |
| `ExportToCsvAsync()` | `Task` | Export to CSV |
| `ExportToXlsxAsync()` | `Task` | Export to XLS/XLSX |
| `ExportToPdfAsync()` | `Task` | Export to PDF |

### DxTreeListDataColumn

| Property | Type | Description |
|---|---|---|
| `FieldName` | `string` | Data field name (required) |
| `Caption` | `string` | Column header text |
| `Width` | `string` | Column width |
| `DisplayFormat` | `string` | Value format string |
| `SortOrder` | `TreeListColumnSortOrder` | Ascending or Descending |
| `SortIndex` | `int` | Multi-column sort position |
| `AllowSort` | `bool` | Allow user sorting |

## Key Differences from DxGrid

| Feature | DxGrid | DxTreeList |
|---|---|---|
| Data structure | Flat | Hierarchical (parent-child) |
| Grouping | `GroupIndex` on columns | Built-in tree hierarchy |
| Group Panel | `ShowGroupPanel` | Not applicable |
| Group Summaries | `` | Not applicable |
| Child load on demand | Not available | `ChildrenLoaded` event |
| Filter tree mode | Not applicable | `TreeListColumnFilterMode` |

## Common Patterns

### Pattern 1: Load Child Nodes on Demand

```razor

    
        
    


@code {
    List RootNodes { get; set; }

    protected override async Task OnInitializedAsync() {
        RootNodes = await DataService.GetRootNodesAsync();
    }

    async Task OnChildrenLoaded(TreeListChildrenLoadingEventArgs e) {
        var parentId = ((TreeNode)e.DataItem).Id;
        var children = await DataService.GetChildrenAsync(parentId);
        e.Children = children;
    }
}
```

### Pattern 2: Expand/Collapse All Programmatically

```csharp
// Expand all nodes
TreeList.ExpandAll();

// Collapse all nodes
TreeList.CollapseAll();

// Expand a specific row by its visible index
TreeList.ExpandRow(visibleIndex);
```

### Pattern 3: Export Expanded Rows Only

```csharp
await TreeList.ExportToXlsxAsync("tree.xlsx", new TreeListXlExportOptions {
    RowExpandMode = TreeListExportRowExpandMode.Expanded
});
```

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| All rows appear at root level, no hierarchy | `ParentKeyFieldName` not set or doesn't match data | Verify `ParentKeyFieldName` matches the parent ID property name exactly |
| Root nodes don't appear | `RootValue` mismatch | If root nodes have `ParentId = 0`, set `RootValue="@((object)0)"` — `@0` is invalid Razor syntax (RZ1005) |
| Tree doesn't expand (clicks ignored) | Static render mode | Add `@rendermode InteractiveServer` to the page |
| Editing creates rows at root level | New row `ParentId` not initialized | Use `CustomizeEditModel` to set the `ParentId` for new child nodes |
| Children not shown after data reload | TreeList state not refreshed | Call `TreeList.Reload()` after updating data |
| `"Enumeration type XXX not registered for parse operation"` | Custom enum used in TreeList filter criteria | Call `EnumProcessingHelper.RegisterEnum()` in `Program.cs` before `builder.Build()` |
| `"Unhandled exception on the current circuit"` with no detail | `CircuitOptions.DetailedErrors` not set | Add `builder.Services.Configure(o => o.DetailedErrors = true);` in `Program.cs` (development only) |
| `"Component parameter 'ValueChanged' is used two or more times"` compile error | `@bind-Value` and `ValueChanged` used together | Use `@bind-Value="@val"` for two-way binding, or `Value="@val" ValueChanged="@handler"` — never both simultaneously |
| `dx-blazor.js` not found (404) behind a reverse proxy | Reverse proxy strips the app base path | Add `app.UsePathBase("/subpath")` before `app.MapBlazorHub()`, or set `` in `App.razor` |
| Static assets return 404 (`dx-blazor.css`, `dx-blazor.js`) | `UseStaticWebAssets()` not called | Add `app.UseStaticWebAssets();` in `Program.cs` before `app.UseStaticFiles()` |
| `"Could not find 'X' in 'window.DxBlazor'"` JavaScript error | Stale browser-cached JS from an older DevExpress version | Hard-refresh the browser (Ctrl+Shift+R), clear site data, or verify all DevExpress NuGet packages are the same version |
| `"Cannot pass the parameter 'X' to component 'Y' with rendermode"` | Non-serializable parameter passed across a render mode boundary | Move the component to a child `.razor` file with its own `@rendermode` directive; pass only serializable parameters |

## Constraints & Rules

0. **Never invent API**: If a property, method, event, or feature is not documented in this skill or its references, do **not** assume it exists. When asked about an unfamiliar API, first try to verify it using the DevExpress documentation MCP (`devexpress_docs_search`) or the local `apidoc/` folder. Only after checking: if confirmed, use the API; if not found, explicitly state that it does not appear to be part of the `DxTreeList` API. Do not warn that a feature "may have been introduced in a recent version" as a way to justify inventing it.
1. **Programmatic editing uses row indexes**: For existing rows, use `StartEditRowAsync(...)`, not Grid-only APIs such as `StartEditDataItemAsync(...)`. For toolbar or external edit buttons, call `StartEditRowAsync(TreeList.GetFocusedRowIndex())`. To add a child row from code, use `StartEditNewRowAsync(parentVisibleIndex)`.
2. **Cast edit models explicitly**: `EditFormTemplate` and event args expose `EditModel` as `object`. Cast it to your model type before you access properties such as `Name`.
3. **Filter panel API**: Use `FilterPanelDisplayMode`, not `ShowFilterPanel`. Set it to `Always` or `Auto` depending on whether the panel should always be visible.
4. **Filter builder operators**: To change date operators such as `IsJanuary` for a specific field, customize `DxFilterBuilder` inside `FilterBuilderTemplate`. `DxTreeList.CustomizeFilterMenu` only affects the column filter menu and `DxTreeListDataColumn.CustomizeFilterMenu` does not exist.
5. **Render mode**: `DxTreeList` requires an interactive render mode for

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [DevExpress](https://github.com/DevExpress)
- **Source:** [DevExpress/agent-skills](https://github.com/DevExpress/agent-skills)
- **License:** MIT
- **Homepage:** https://www.devexpress.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-devexpress-agent-skills-devexpress-blazor-treelist
- Seller: https://agentstack.voostack.com/s/devexpress
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
