New to Telerik UI for .NET MAUIStart a free 30-day trial

AI Smart Assistant in .NET MAUI TreeDataGrid

Updated on Feb 13, 2026

The .NET MAUI TreeDataGrid control offers AI-powered features that allow users to interact with the TreeDataGrid using natural language prompts. This functionality enables users to perform various operations such as sorting, grouping, filtering, and manipulating columns without needing to write code.

On mobile, the AI Smart Assistant panel can be accessed via a floating action button on the bottom-right corner of the TreeDataGrid. On desktop platforms, the AI Smart Assistant panel displays above the header area of the TreeDataGrid.

Here is how the AI Smart Assistant panel is arranged on desktop, with the panel above the TreeDataGrid header area, and on mobile, where it is opened from a floating action button in the bottom-right corner of the TreeDataGrid:

.NET MAUI TreeDataGrid AI Smart Assistant Panel

Here is how the AI Smart Assistant panel behaves on desktop and mobile when no suggestions are shown:

.NET MAUI TreeDataGrid AI Smart Assistant Panel

When no suggestions are available, the AI Smart Assistant panel shows only the prompt input area and any configured helper text, without a suggestions list.

Supported Operations

The following operations can be performed using AI Smart Assistant:

  • Sorting—Users can sort the control by specifying the column and the sort direction (ascending or descending).
  • Filtering—Users can filter the control based on specific criteria.
  • Column Manipulation—Users can freeze, hide columns and more.

Getting Started with the AI Smart Assistant

To get started with the AI Smart Assistant functionality in the .NET MAUI TreeDataGrid, follow the steps:

Prerequisites

  1. .NET9 or later.
  2. An AI provider. For example Azure OpenAI or OpenAI, etc.
  3. Telerik.AI.SmartComponents.Extensions package.

1. Getting Started with the Telerik.AI.SmartComponents.Extensions

The AI Smart Assistant functionality relies on the Telerik.AI.SmartComponents.Extensions package. The package provides the necessary extensions to integrate AI capabilities into the control.

  1. Install the Telerik.AI.SmartComponents.Extensions package in your .NET MAUI project.
script
dotnet add package Telerik.AI.SmartComponents.Extensions

The Telerik.AI.SmartComponents.Extensions package has a dependency on the Microsoft.Extensions.AI package.

  1. Configure the AI services in your application. This typically involves setting up an AI provider (such as Azure OpenAI, OpenAI, etc.) and providing the necessary API keys or credentials.

  2. Register the AI service and AI chat client in your application.

    For the example, we will use the Azure OpenAI. To register the AI service and chat client, the following code is needed in MauiProgram.cs:

    C#
    builder.Services.AddSingleton(sp =>
    {
        return new AzureOpenAIClient(new Uri("AZURE_OPENAI_ENDPOINT"), new AzureKeyCredential("AZURE_OPENAI_API_KEY"));
    });
    
    builder.Services.AddChatClient(services =>
        services.GetRequiredService<AzureOpenAIClient>().GetChatClient("gpt-4.1").AsIChatClient()
    );

2. Process TreeDataGrid AI Requests

  1. How to access the AI model in the ViewModel is a developer decision.

  2. The Telerik.AI.SmartComponents.Extensions library provides two key methods:

    • AddGridChatTools()—Configures the AI model with DataGrid-specific capabilities.
    • ExtractGridResponse()—Extracts structured commands and messages from the AI response that the DataGrid can understand.

    For the demo we will use a command in the ViewModel. The command is bound to the DataGrid UserCommand. The command executes when a prompt is submitted from the AI Smart Assistant panel.

    Here is a sample ViewModel definition:

    C#
    public class AIPromptViewModel : NotifyPropertyChangedBase
    {
        private CancellationTokenSource cancellationTokenSource;
        private ICommand processAICommand;
        private ICommand cancelAICommand;
        private readonly IChatClient chatClient;
    
        public AIPromptViewModel(IChatClient chatClient)
        {
            this.chatClient = chatClient;
        }
    
        public ICommand ProcessAICommand
        {
            get => this.processAICommand ?? (this.processAICommand = new Command<DataGridPromptRequestCommandContext>(this.ExecuteProcessAI));
        }
    
        public ICommand CancelAICommand
        {
            get => this.cancelAICommand ?? (this.cancelAICommand = new Command(this.ExecuteCancelAI));
        }
    
        private async void ExecuteProcessAI(DataGridPromptRequestCommandContext context)
        {
            if (this.cancellationTokenSource != null)
            {
                // An AI request is already being processed
                return;
            }
    
            this.cancellationTokenSource = new CancellationTokenSource();
    
            try
            {
                var request = JsonSerializer.Deserialize<GridAIRequest>(context.RequestJson);
    
                var options = new ChatOptions();
                options.AddGridChatTools(request.Columns);
    
                List<ChatMessage> messages = request.Contents.Select(m => new ChatMessage(ChatRole.User, m.Text)).ToList();
    
                ChatResponse completion = await this.chatClient.GetResponseAsync(messages, options);
                var response = JsonSerializer.Serialize(completion.ExtractGridResponse(), new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
    
                context.ResponseJson = response;
            }
            catch (OperationCanceledException)
            {
                // Cancellation was already handled by setting ProcessingState to Canceled
                // No need to set it again here
            }
            catch (Exception ex)
            {
                await this.ShowErrorAsync($"Failed to process request: {ex.Message}");
                context.HasError = true;
            }
            finally
            {
                this.cancellationTokenSource?.Dispose();
                this.cancellationTokenSource = null;
            }
        }
    
        private void ExecuteCancelAI()
        {
            this.cancellationTokenSource?.Cancel();
        }
    
        protected async Task ShowErrorAsync(string message)
        {
    #if NET10_0_OR_GREATER
            await Microsoft.Maui.Controls.Application.Current?.Windows[0].Page?.DisplayAlertAsync("Error", message, "OK");
    #else
            await Microsoft.Maui.Controls.Application.Current?.Windows[0].Page?.DisplayAlert("Error", message, "OK");
    #endif
        }
    }
    • Deserializes the incoming JSON (context.RequestJson) into a GridAIRequest. This contains grid columns and user prompt contents.

    • Creates ChatOptions and registers DataGrid-specific tools via options.AddGridChatTools(request.Columns) so the AI knows what columns/operations are available.

    • Maps each request content to a ChatMessage with ChatRole.User.

    • Calls the AI backend through IChatClient.GetResponseAsync(messages, options) to get a ChatResponse.

    • Extracts the grid-specific instruction from the AI result with completion.ExtractGridResponse().

    • Serializes the response to a JSON and assign to context.ResponseJson for the DataGrid to consume.

  3. Get the service registered in the MauiProgram.cs and pass it to the instance of the ViewModel. Then set the binding context of the page to be this ViewModel.

    C#
    var chatClient = Application.Current.Handler.MauiContext.Services.GetRequiredService<IChatClient>();
    this.viewModel = new AIPromptViewModel(chatClient);
    this.BindingContext = this.viewModel;
  4. Set the TreeDataGrid's IsAIEnabled property to true. When setting to true, the AI Smart Assistant panel gets enabled, allowing users to input prompts.

Example Prompts

Here are some example prompts that users can use to interact with the TreeDataGrid:

  • "Sort the data by the Name column in ascending order."
  • "Filter the data to show only items with a price greater than 100."
  • To freeze a column use the lock keyword. For example: "Lock the Price column."

Example with AI Smart Assistant

1. Add TreeDataGrid definition in XAML:

xaml
<telerik:RadTreeDataGrid x:Name="treeDataGrid"
                         ItemsSource="{Binding Items}"
                         IsAIEnabled="True"
                         AutoGenerateColumns="False">
    <telerik:RadTreeDataGrid.ItemDescriptor>
        <telerik:TreeDataGridItemDescriptor ItemsSourceBinding="{Binding Children}" />
    </telerik:RadTreeDataGrid.ItemDescriptor>
    <telerik:RadDataGrid.Commands>
        <commands:DataGridUserCommand Id="PromptRequest"
                                      Command="{Binding ProcessAICommand}" />
        <commands:DataGridUserCommand Id="CancelPromptRequest"
                                      Command="{Binding CancelAICommand}" />
    </telerik:RadDataGrid.Commands>
    <telerik:RadTreeDataGrid.AISettings>
        <telerik:DataGridAISettings SuggestedPrompts="{Binding SuggestedPrompts}" />
    </telerik:RadTreeDataGrid.AISettings>
    <telerik:RadTreeDataGrid.Columns>
        <telerik:DataGridTextColumn PropertyName="Name" />
        <telerik:DataGridNumericalColumn PropertyName="Size" />
        <telerik:DataGridTextColumn PropertyName="Type" />
    </telerik:RadTreeDataGrid.Columns>
</telerik:RadTreeDataGrid>

2. Add the telerik namespace:

xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"

3. Add sample data model:

c#
public class Data
{
	public Data(string name, int size, string type)
	{
		this.Name = name;
		this.Size = size;
		this.Type = type;
		this.Children = new ObservableCollection<Data>();
	}

	public string Name { get; set; }
	public int Size { get; set; }
	public string Type { get; set; }
	public ObservableCollection<Data> Children { get; set; }
}

4. Define the ViewModel:

c#
public class ViewModel
{
    private static readonly HttpClient HttpClient = new HttpClient();
    private CancellationTokenSource cancellationTokenSource;
    private ICommand processAICommand;
    private ICommand cancelAICommand;
    private readonly ObservableCollection<string> suggestedPrompts;

    public ViewModel()
    {
        this.suggestedPrompts = this.CreateDefaultSuggestedPrompts();
        this.Items = this.CreateSampleData();
    }

    public ObservableCollection<Data> Items { get; private set; }

    public ObservableCollection<string> SuggestedPrompts => this.suggestedPrompts;

    public ICommand ProcessAICommand
    {
        get => this.processAICommand ?? (this.processAICommand = new Command<DataGridPromptRequestCommandContext>(this.ExecuteProcessAI));
    }

    public ICommand CancelAICommand
    {
        get => this.cancelAICommand ?? (this.cancelAICommand = new Command(this.ExecuteCancelAI));
    }

    private ObservableCollection<Data> CreateSampleData()
    {
        var root = new ObservableCollection<Data>();

        var folderDocs = new Data("Documents", 0, "Folder");
        folderDocs.Children.Add(new Data("Resume.docx", 24, "File"));
        folderDocs.Children.Add(new Data("Budget.xlsx", 120, "File"));
        // More documents
        folderDocs.Children.Add(new Data("ProjectProposal.pdf", 340, "File"));
        folderDocs.Children.Add(new Data("MeetingNotes.txt", 18, "File"));
        var reportsFolder = new Data("Reports", 0, "Folder");
        reportsFolder.Children.Add(new Data("Q1.pdf", 220, "File"));
        reportsFolder.Children.Add(new Data("Q2.pdf", 240, "File"));
        reportsFolder.Children.Add(new Data("Q3.pdf", 260, "File"));
        reportsFolder.Children.Add(new Data("Q4.pdf", 280, "File"));
        folderDocs.Children.Add(reportsFolder);

        var folderMedia = new Data("Media", 0, "Folder");
        var folderPhotos = new Data("Photos", 0, "Folder");
        folderPhotos.Children.Add(new Data("Vacation1.jpg", 2048, "Image"));
        folderPhotos.Children.Add(new Data("Vacation2.jpg", 1950, "Image"));
        folderPhotos.Children.Add(new Data("Family1.png", 980, "Image"));
        folderPhotos.Children.Add(new Data("Family2.png", 1120, "Image"));
        folderPhotos.Children.Add(new Data("Sunset.tif", 3250, "Image"));
        var eventsPhotos = new Data("Events", 0, "Folder");
        eventsPhotos.Children.Add(new Data("Birthday.jpg", 1540, "Image"));
        eventsPhotos.Children.Add(new Data("Conference.jpg", 1760, "Image"));
        folderPhotos.Children.Add(eventsPhotos);
        folderMedia.Children.Add(folderPhotos);
        folderMedia.Children.Add(new Data("Video.mp4", 150000, "Video"));
        folderMedia.Children.Add(new Data("Trailer.mov", 98000, "Video"));
        var musicFolder = new Data("Music", 0, "Folder");
        musicFolder.Children.Add(new Data("Track1.mp3", 5300, "Audio"));
        musicFolder.Children.Add(new Data("Track2.mp3", 6100, "Audio"));
        var albumFolder = new Data("Album - 2024", 0, "Folder");
        albumFolder.Children.Add(new Data("SongA.flac", 14500, "Audio"));
        albumFolder.Children.Add(new Data("SongB.flac", 15200, "Audio"));
        musicFolder.Children.Add(albumFolder);
        folderMedia.Children.Add(musicFolder);

        var folderSrc = new Data("Source", 0, "Folder");
        var subFolderApp = new Data("App", 0, "Folder");
        subFolderApp.Children.Add(new Data("MainPage.xaml", 12, "Xaml"));
        subFolderApp.Children.Add(new Data("MainPage.xaml.cs", 8, "CSharp"));
        subFolderApp.Children.Add(new Data("AppShell.xaml", 10, "Xaml"));
        subFolderApp.Children.Add(new Data("AppShell.xaml.cs", 7, "CSharp"));
        var subFolderViewModels = new Data("ViewModels", 0, "Folder");
        subFolderViewModels.Children.Add(new Data("MainViewModel.cs", 16, "CSharp"));
        subFolderViewModels.Children.Add(new Data("DetailsViewModel.cs", 22, "CSharp"));
        var subFolderServices = new Data("Services", 0, "Folder");
        subFolderServices.Children.Add(new Data("DataService.cs", 30, "CSharp"));
        subFolderServices.Children.Add(new Data("AuthService.cs", 26, "CSharp"));
        folderSrc.Children.Add(subFolderApp);
        folderSrc.Children.Add(subFolderViewModels);
        folderSrc.Children.Add(subFolderServices);
        folderSrc.Children.Add(new Data("Utils.cs", 4, "CSharp"));
        var folderTests = new Data("Tests", 0, "Folder");
        var unitFolder = new Data("UnitTests", 0, "Folder");
        unitFolder.Children.Add(new Data("ViewModelTests.cs", 18, "CSharp"));
        unitFolder.Children.Add(new Data("ServiceTests.cs", 20, "CSharp"));
        var uiFolder = new Data("UITests", 0, "Folder");
        uiFolder.Children.Add(new Data("LoginFlowTests.cs", 25, "CSharp"));
        uiFolder.Children.Add(new Data("CheckoutFlowTests.cs", 28, "CSharp"));
        folderTests.Children.Add(unitFolder);
        folderTests.Children.Add(uiFolder);
        folderSrc.Children.Add(folderTests);

        var folderAssets = new Data("Assets", 0, "Folder");
        var images = new Data("Images", 0, "Folder");
        images.Children.Add(new Data("logo.png", 256, "Image"));
        images.Children.Add(new Data("banner.jpg", 1024, "Image"));
        images.Children.Add(new Data("icon.svg", 34, "Vector"));
        var fonts = new Data("Fonts", 0, "Folder");
        fonts.Children.Add(new Data("OpenSans-Regular.ttf", 1920, "Font"));
        fonts.Children.Add(new Data("OpenSans-Bold.ttf", 2048, "Font"));
        folderAssets.Children.Add(images);
        folderAssets.Children.Add(fonts);

        var folderConfigs = new Data("Configs", 0, "Folder");
        folderConfigs.Children.Add(new Data("appsettings.json", 6, "Config"));
        folderConfigs.Children.Add(new Data("launchSettings.json", 4, "Config"));
        var envFolder = new Data("Environments", 0, "Folder");
        envFolder.Children.Add(new Data("dev.json", 3, "Config"));
        envFolder.Children.Add(new Data("staging.json", 3, "Config"));
        envFolder.Children.Add(new Data("prod.json", 3, "Config"));
        folderConfigs.Children.Add(envFolder);

        var folderDownloads = new Data("Downloads", 0, "Folder");
        folderDownloads.Children.Add(new Data("Installer.exe", 54000, "Binary"));
        folderDownloads.Children.Add(new Data("Archive.zip", 24000, "Compressed"));
        folderDownloads.Children.Add(new Data("Readme.md", 2, "Text"));

        var folderSandbox = new Data("Sandbox", 0, "Folder");
        var experiments = new Data("Experiments", 0, "Folder");
        experiments.Children.Add(new Data("PhysicsSim.py", 12, "Script"));
        experiments.Children.Add(new Data("MLModel.ipynb", 64, "Notebook"));
        experiments.Children.Add(new Data("shader.glsl", 6, "Shader"));
        var prototypes = new Data("Prototypes", 0, "Folder");
        prototypes.Children.Add(new Data("UIPrototype.fig", 8500, "Design"));
        prototypes.Children.Add(new Data("FlowDiagram.drawio", 1200, "Diagram"));
        folderSandbox.Children.Add(experiments);
        folderSandbox.Children.Add(prototypes);

        root.Add(folderDocs);
        root.Add(folderMedia);
        root.Add(folderSrc);
        root.Add(folderAssets);
        root.Add(folderConfigs);
        root.Add(folderDownloads);
        root.Add(folderSandbox);

        return root;
    }

    private ObservableCollection<string> CreateDefaultSuggestedPrompts()
    {
        return new ObservableCollection<string>
        {
            "Filter files by type equals to Image",
            "Show files which size is greater than 2000",
            "Sort by name ascending",
            "Clear all filters and sorting"
        };
    }

    private async void ExecuteProcessAI(DataGridPromptRequestCommandContext context)
    {
        if (this.cancellationTokenSource != null)
        {
            // An AI request is already being processed
            return;
        }

        this.cancellationTokenSource = new CancellationTokenSource();

        try
        {
            var request = JsonSerializer.Deserialize<object>(context.RequestJson);
            var requestResult = await HttpClient.PostAsJsonAsync(
                "https://demos.telerik.com/service/v2/ai/grid/smart-state",
                request,
                this.cancellationTokenSource.Token);

            var response = await requestResult.Content.ReadAsStringAsync(this.cancellationTokenSource.Token);
            context.ResponseJson = response;
        }
        catch (OperationCanceledException)
        {
            // Cancellation was already handled by setting ProcessingState to Canceled
        }
        catch (Exception ex)
        {
            await this.ShowErrorAsync($"Failed to process request: {ex.Message}");
            context.HasError = true;
        }
        finally
        {
            this.cancellationTokenSource?.Dispose();
            this.cancellationTokenSource = null;
        }
    }

    private void ExecuteCancelAI()
    {
        this.cancellationTokenSource?.Cancel();
    }

    private async Task ShowErrorAsync(string message)
    {
#if NET10_0_OR_GREATER
        await Microsoft.Maui.Controls.Application.Current?.Windows[0].Page?.DisplayAlertAsync("Error", message, "OK");
#else
        await Microsoft.Maui.Controls.Application.Current?.Windows[0].Page?.DisplayAlert("Error", message, "OK");
#endif
    }
}

5. Define the user command for the ProcessAICommand and CancelAICommand:

c#
public sealed class DataGridUserCommand : DataGridCommand
{
    public static readonly BindableProperty CommandProperty =
        BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(DataGridUserCommand), null);

    public ICommand Command
    {
        get => (ICommand)this.GetValue(CommandProperty);
        set => this.SetValue(CommandProperty, value);
    }

    public override bool CanExecute(object parameter)
    {
        var command = this.Command;
        if (this.Owner == null || command == null)
        {
            return false;
        }

        return command.CanExecute(parameter);
    }

    public override void Execute(object parameter)
    {
        var command = this.Command;
        if (command != null)
        {
            command.Execute(parameter);
        }
    }
}

The TreeDataGrid AI Smart Assistant examples in the SDKBrowser Demo Application use a Telerik-hosted AI service for demonstration purposes only.

To use the AI Smart Assistant in your application, you must configure your own AI service.

How to do that is described in the Getting Started with the AI Smart Assistant article.

For a runnable example demonstrating the AI Smart Assistant, see the SDKBrowser Demo Application and go to the TreeDataGrid > AI Smart Assistant category.

See Also