﻿<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://community.devexpress.com/feed-stylesheets/rss.xsl" media="screen"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dx="https://www.devexpress.com/" xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>ASP.NET Team Blog</title>
    <link>https://community.devexpress.com/Blogs/aspnet/default.aspx</link>
    <description>JavaScript, HTML 5, ASP.NET, DevExpress, ASP.NET MVC &amp; WebForms, and News - Mehul Harry's DevExpress blog</description>
    <language>en-US</language>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388289</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2026/05/22/devexpress-blazor-ai-chat-multi-model-support-mcp-server-integration-and-a-look-at-what-39-s-coming-next.aspx</link>
      <category domain="https://community.devexpress.com/Tags/AI">AI</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Chat+UI">Chat UI</category>
      <category domain="https://community.devexpress.com/Tags/LLM">LLM</category>
      <category domain="https://community.devexpress.com/Tags/MCP">MCP</category>
      <category domain="https://community.devexpress.com/Tags/v26.1">v26.1</category>
      <title>DevExpress Blazor AI Chat — Multi-Model Support, MCP Server Integration, and a Look at What's Coming Next</title>
      <description>&lt;p&gt;We continue to extend the capabilities of the&amp;nbsp;&lt;a href="https://docs.devexpress.com/Blazor/DevExpress.AIIntegration.Blazor.Chat.DxAIChat" target="_blank" title="DevExpress Blazor AI Chat"&gt;DevExpress Blazor AI Chat&lt;/a&gt; component and publish GitHub examples designed to address real-world usage scenarios. This post highlights two new examples: a multi-model chat with persistent conversation history, and MCP server integration that extends AI context with external data sources. I&amp;#39;ll also share planned features for v26.1 (scheduled for mid-June 2026).&lt;/p&gt;

&lt;h2 id="multi-model-chat-with-conversation-history"&gt;Multi-Model Chat with Conversation History&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://community.devexpress.com/Blogs/aspnet/archive/2025/05/06/devexpress-blazor-ai-chat-build-a-multi-llm-chat-application.aspx" target="_blank" title="DevExpress Blazor AI Chat — Build a Multi-LLM Chat Application"&gt;earlier multi-LLM chat application example&lt;/a&gt; demonstrated how to switch between AI providers within a single chat session. The new &lt;a href="https://github.com/DevExpress-Examples/blazor-ai-chat-with-multiple-llm-services" target="_blank" title="DevExpress Blazor AI Chat — Multi-Model Chat with Conversation History"&gt;DevExpress Blazor AI Chat — Multi-Model Chat with Conversation History&lt;/a&gt; example adds persistent conversation threads and automated chat session title generation.&lt;/p&gt;

&lt;img class="small" src="https://community.devexpress.com/blogs/aspnet/26.1/mcp-multi-llm/devexpress-ai-blazor-chat-multi-llm-with-title-generation.png" alt="The multi-model chat UI showing the left sidebar with a list of conversation threads (each with an auto-generated title), a model selector dropdown at the top, and the active chat pane on the right." style="width:730px;height:606px;"&gt;

&lt;p&gt;The application uses a two-pane layout with &lt;code&gt;DxSplitter&lt;/code&gt;. The left pane is a sidebar that hosts a &lt;code&gt;DxComboBox&lt;/code&gt; for model selection and a &lt;code&gt;DxListBox&lt;/code&gt; for conversation threads. &lt;code&gt;InMemoryChatThreadStore&lt;/code&gt; manages thread data. This thread-safe dictionary-backed store tracks message history and timestamps. The right pane hosts the &lt;code&gt;DxAIChat&lt;/code&gt; component. The following Razor markup defines the layout:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;DxSplitter CssClass=&amp;quot;chat-splitter&amp;quot; Height=&amp;quot;100%&amp;quot;&amp;gt;
    &amp;lt;Panes&amp;gt;
        &amp;lt;DxSplitterPane Size=&amp;quot;320px&amp;quot; MinSize=&amp;quot;220px&amp;quot; MaxSize=&amp;quot;500px&amp;quot;&amp;gt;
            &amp;lt;DxButton RenderStyle=&amp;quot;ButtonRenderStyle.Primary&amp;quot;
                      RenderStyleMode=&amp;quot;ButtonRenderStyleMode.Contained&amp;quot;
                      Text=&amp;quot;New Chat&amp;quot;
                      Click=&amp;quot;CreateNewThreadAsync&amp;quot; /&amp;gt;
            &amp;lt;DxComboBox Data=&amp;quot;@ModelsList&amp;quot;
                        Value=&amp;quot;@SelectedModel&amp;quot;
                        TextFieldName=&amp;quot;@nameof(ChatClientSession.Name)&amp;quot;
                        ValueChanged=&amp;quot;@((ChatClientSession session) =&amp;gt; OnSelectedThreadModelChangedAsync(session))&amp;quot; /&amp;gt;
            &amp;lt;DxListBox Data=&amp;quot;@Threads&amp;quot;
                       Value=&amp;quot;@SelectedThread&amp;quot;
                       ValueChanged=&amp;quot;@((ChatThread thread) =&amp;gt; OnThreadSelectedAsync(thread))&amp;quot;
                       TextFieldName=&amp;quot;@nameof(ChatThread.Title)&amp;quot;&amp;gt;
                &amp;lt;ItemDisplayTemplate&amp;gt;
                    &amp;lt;div class=&amp;quot;thread-list-item&amp;quot;&amp;gt;
                        &amp;lt;div class=&amp;quot;thread-title&amp;quot;&amp;gt;@context.DataItem.Title&amp;lt;/div&amp;gt;
                        &amp;lt;div class=&amp;quot;thread-model&amp;quot;&amp;gt;@GetModelName(context.DataItem.ModelSessionId)&amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/ItemDisplayTemplate&amp;gt;
            &amp;lt;/DxListBox&amp;gt;
        &amp;lt;/DxSplitterPane&amp;gt;
        &amp;lt;DxSplitterPane&amp;gt;
            &amp;lt;DxAIChat @ref=&amp;quot;DxAiChat&amp;quot;
                      Initialized=&amp;quot;OnChatInitialized&amp;quot; /&amp;gt;
        &amp;lt;/DxSplitterPane&amp;gt;
    &amp;lt;/Panes&amp;gt;
&amp;lt;/DxSplitter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Automatic thread title generation is a key implementation detail. The &lt;code&gt;CompositeChatClient&lt;/code&gt; class implements &lt;code&gt;IChatClient&lt;/code&gt; and intercepts outgoing user messages via &lt;code&gt;GetResponseAsync&lt;/code&gt; and &lt;code&gt;GetStreamingResponseAsync&lt;/code&gt; methods. On the first message in a new thread, the class sends a background request to the selected AI model using a dedicated system prompt and requests a concise 3–6 word title. &lt;code&gt;IChatThreadStore&lt;/code&gt; stores the result. The &lt;code&gt;ThreadTitleUpdated&lt;/code&gt; event updates the UI and refreshes the sidebar without blocking the main chat response:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;// CompositeChatClient.cs
public IAsyncEnumerable&amp;lt;ChatResponseUpdate&amp;gt; GetStreamingResponseAsync(IEnumerable&amp;lt;ChatMessagegt; messages, 
ChatOptions? options = null, CancellationToken cancellationToken = new CancellationToken())
    {
        var selectedSession = GetRequiredSelectedSession();
        var messageList = messages.ToList();
        TryQueueTitleGeneration(messageList, selectedSession);
		...
        await foreach (var update in selectedSession.Client.GetStreamingResponseAsync(
        	messageList, options, cancellationToken))
            yield return update;
        ...
    }

private void TryQueueTitleGeneration(IEnumerable&amp;lt;ChatMessage&amp;gt; messages, ChatClientSession selectedSession) {
    var threadId = _activeThreadId.Value;
    var firstUserMessage = GetFirstUserMessage(messages);
    ...
    _ = GenerateTitleForThreadAsync(threadId, selectedSession, firstUserMessage);
}

private async Task GenerateTitleForThreadAsync(Guid threadId,
    ChatClientSession selectedSession, string firstUserMessage) {
    try {
        var thread = await _threadStore.GetThreadAsync(threadId, CancellationToken.None);
        if (thread is null || thread.HasGeneratedTitle)
            return;

        var modelSession = AvailableChatClients
            .FirstOrDefault(x =&amp;gt; x.Id == thread.ModelSessionId) ?? selectedSession;

        string generatedTitle;
            try {
                generatedTitle = await _titleGenerator.GenerateTitleAsync(modelSession, firstUserMessage, CancellationToken.None);
            }
            catch {
                generatedTitle = _titleGenerator.BuildFallbackTitle(firstUserMessage);
            }

            if (string.IsNullOrWhiteSpace(generatedTitle)) {
                generatedTitle = _titleGenerator.BuildFallbackTitle(firstUserMessage);
            }

            await _threadStore.UpdateTitleAsync(threadId, generatedTitle, true, CancellationToken.None);
            lock (_syncRoot) {
                _titledThreadIds.Add(threadId);
            }
            ThreadTitleUpdated?.Invoke(threadId, generatedTitle);
        }
        catch (OperationCanceledException) { }
        finally {
            lock (_syncRoot)
                _titleGenerationInProgress.Remove(threadId);
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The example includes an in-memory store. The &lt;code&gt;IChatThreadStore&lt;/code&gt; interface allows for replacement with an EF Core-backed implementation for applications that require persistent history.&lt;/p&gt;

&lt;p&gt;To download and explore our implementation, navigate to the following&amp;nbsp;DevExpress GitHub repository: &lt;a href="https://github.com/DevExpress-Examples/blazor-ai-chat-with-multiple-llm-services" target="_blank" title="Blazor AI Chat — Multi-Model Chat with Conversation History"&gt;Blazor AI Chat — Multi-Model Chat with Conversation History&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="mcp-server-integration"&gt;MCP Server Integration&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/DevExpress-Examples/blazor-ai-chat-mcp-resources" target="_blank" title="DevExpress Blazor AI Chat — Integration with Model Context Protocol"&gt;DevExpress Blazor AI Chat — Integration with Model Context Protocol&lt;/a&gt; example connects our Blazor AI Chat component to external data through the &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" target="_blank" title="Model Context Protocol — Introduction"&gt;Model Context Protocol (MCP)&lt;/a&gt;.&lt;/p&gt;

&lt;img class="small" src="https://community.devexpress.com/blogs/aspnet/26.1/mcp-multi-llm/devexpress-ai-blazor-chat-mcp-resources-window.png" alt="The Blazor AI Chat UI with an MCP-powered chat session open, showing the chat querying a server access log and receiving an AI-generated analysis in response." style="width:626px;height:602px;"&gt;

&lt;p&gt;The solution includes two projects.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;AIChatMcpServer&lt;/code&gt; is a custom MCP server that exposes sample tools, resources, and prompt templates to the client application.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;AIChatMcpClient&lt;/code&gt; is a Blazor Server application that hosts &lt;code&gt;DxAIChat&lt;/code&gt; and loads MCP capabilities at startup through a hosted &lt;code&gt;McpRepository&lt;/code&gt; service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sample MCP server exposes three primitives: &lt;strong&gt;tools&lt;/strong&gt; (executable functions the AI model can call automatically), &lt;strong&gt;resources&lt;/strong&gt; (static content such as logs, text files, and binary images), and &lt;strong&gt;prompts&lt;/strong&gt; (reusable parameterized templates). &lt;code&gt;McpRepository&lt;/code&gt; loads these primitives at startup and passes them to &lt;code&gt;DxAIChat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each primitive maps directly to a &lt;code&gt;DxAIChat&lt;/code&gt; feature. Resources map to &lt;code&gt;AIChatResource&lt;/code&gt; objects and populate the &lt;code&gt;Resources&lt;/code&gt; collection. Prompts map to &lt;code&gt;DxAIChatPromptSuggestion&lt;/code&gt; entries displayed when the chat opens. Tools attach to &lt;code&gt;IChatClient&lt;/code&gt; through &lt;code&gt;UseFunctionInvocation&lt;/code&gt; at startup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index.razor&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;DxAIChat FileUploadEnabled=&amp;quot;true&amp;quot;
          Resources=&amp;quot;Resources&amp;quot;
          IncludeFunctionCallInfo=&amp;quot;true&amp;quot;&amp;gt;
        &amp;lt;PromptSuggestions&amp;gt;
            @foreach (var suggestion in PromptSuggestions){
                &amp;lt;DxAIChatPromptSuggestion PromptMessage=&amp;quot;@suggestion.PromptMessage&amp;quot; Title=&amp;quot;@suggestion.Title&amp;quot; Text=&amp;quot;@suggestion.PromptMessage&amp;quot;/&amp;gt;
            }
        &amp;lt;/PromptSuggestions&amp;gt;
        &amp;lt;AIChatSettings&amp;gt;
            &amp;lt;DxAIChatFileUploadSettings MaxFileSize=&amp;quot;10000000&amp;quot; MaxFileCount=&amp;quot;3&amp;quot;/&amp;gt;
        &amp;lt;/AIChatSettings&amp;gt;
    &amp;lt;/DxAIChat&amp;gt;

@code {
    IEnumerable&amp;lt;AIChatResource&amp;gt; Resources { get; set; } = [];
    IEnumerable&amp;lt;PromptSuggestion&amp;gt; PromptSuggestions { get; set; } = [];

    protected override async Task OnInitializedAsync() {
        // Map MCP resources to AIChatResource — DxAIChat fetches content on demand via LoadResourceData
        Resources = McpRepository.Resources.Select(x =&amp;gt;
            new AIChatResource(x.Uri, x.Name, LoadResourceData, x.MimeType, x.Description));
        // Map MCP prompt templates to prompt suggestions shown in the chat UI
        PromptSuggestions = McpRepository.PromptSuggestions;
    }

    async Task&amp;lt;IList&amp;lt;AIContent&amp;gt;&amp;gt; LoadResourceData(AIChatResource resource, CancellationToken ct) {
        var result = await McpRepository.Client.ReadResourceAsync(resource.Uri, cancellationToken: ct);
        return result.Contents.ToAIContents();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Program.cs&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;using Azure;
using Azure.AI.OpenAI;
using AIChatMcpClient;
using AIChatMcpClient.Components;
using AIChatMcpClient.Services;
using Microsoft.Extensions.AI;
...

builder.Services.AddSingleton&amp;lt;McpRepository&amp;gt;();
builder.Services.AddHostedService(sp =&amp;gt; sp.GetRequiredService&amp;lt;McpRepository&amp;gt;());

builder.Services.AddSingleton&amp;lt;IChatClient&amp;gt;(sp =&amp;gt; {
    var mcpRepository = sp.GetService&amp;lt;McpRepository&amp;gt;();
    var azureOpenAIClient = new AzureOpenAIClient(
        new Uri(azureOpenAISettings.Endpoint),
        new AzureKeyCredential(azureOpenAISettings.ApiKey));
    var chatClient = azureOpenAIClient.GetChatClient(azureOpenAISettings.DeploymentName).AsIChatClient();
    return new ChatClientBuilder(chatClient)
        .ConfigureOptions(co =&amp;gt; {
            co.Tools = mcpRepository.Tools.ToArray&amp;lt;AITool&amp;gt;();
        })
        .UseFunctionInvocation()
        .Build();
});
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The implementation follows MCP standards. Client code requires no changes when you switch to another MCP-compliant backend. To connect the Blazor application to a different MCP server, modify the &lt;code&gt;McpRepository&lt;/code&gt; endpoint:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;using AIChatMcpClient.Models;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
...
public class McpRepository : IHostedService, IAsyncDisposable {
    private readonly string _mcpEndpoint;

    public McpClient Client { get; private set; } = null!;
    public List&amp;lt;McpClientTool&amp;gt; Tools { get; } = [];
    public List&amp;lt;McpClientResource&amp;gt; Resources { get; } = [];
    public List&amp;lt;McpClientPrompt&amp;gt; Prompts { get; } = [];
    public List&amp;lt;PromptSuggestion&amp;gt; PromptSuggestions { get; } = [];

    public McpRepository(IConfiguration configuration) {
        _mcpEndpoint = configuration.GetSection(&amp;quot;McpServer:Endpoint&amp;quot;).Value 
                       ?? throw new InvalidOperationException(&amp;quot;McpServer:Endpoint is not configured in appsettings.json&amp;quot;);
    }

    public async Task StartAsync(CancellationToken cancellationToken) {
        var transport = new HttpClientTransport(new() { Endpoint = new(_mcpEndpoint) });
        Client = await McpClient.CreateAsync(transport);
        
        var tools = await Client.ListToolsAsync(cancellationToken: cancellationToken);
        var resources = await Client.ListResourcesAsync(cancellationToken: cancellationToken);
        var prompts = await Client.ListPromptsAsync(cancellationToken: cancellationToken);
        
        Tools.AddRange(tools);
        Resources.AddRange(resources);
        Prompts.AddRange(prompts);

        // Preload prompt suggestions at startup
        foreach (var prompt in Prompts) {
            var result = await prompt.GetAsync();
            var content = result.Messages[0].Content;
            PromptSuggestions.Add(new PromptSuggestion {
                PromptMessage = ((TextContentBlock)content).Text,
                Title = prompt.Title ?? &amp;quot;Untitled&amp;quot;
            });
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) =&amp;gt; Task.CompletedTask;

    public async ValueTask DisposeAsync() {
        await Client.DisposeAsync();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;span&gt;To download and explore our implementation, navigate to the following&amp;nbsp;DevExpress GitHub repository&lt;/span&gt;: &lt;a href="https://github.com/DevExpress-Examples/blazor-ai-chat-mcp-resources" target="_blank" title="DevExpress Blazor AI Chat — Integration with Model Context Protocol"&gt;DevExpress Blazor AI Chat — Integration with Model Context Protocol&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="whats-coming-in-v26-1"&gt;What&amp;#39;s Coming in v26.1&lt;/h2&gt;

&lt;p&gt;Our v26.1 release is scheduled for mid-June 2026 and includes the following enhancements to DevExpress AI Chat components for Blazor, WinForms, and WPF.&lt;/p&gt;

&lt;h3 id="microsoft-agent-framework-and-openai-responses-api-support"&gt;Microsoft Agent Framework and OpenAI Responses API Support&lt;/h3&gt;

&lt;p&gt;The most substantial addition is a new &lt;code&gt;IChatResponseProvider&lt;/code&gt; abstraction layer that decouples the chat UI from the underlying AI service. This layer allows you to bind &lt;code&gt;DxAIChat&lt;/code&gt; to a wider set of AI backends beyond the standard &lt;code&gt;IChatClient&lt;/code&gt; interface, including the Microsoft Agent Framework (with support for agents, executors, and multi-step workflows), the OpenAI Responses API, and Azure AI Projects. The API also supports custom &lt;code&gt;IChatResponseProvider&lt;/code&gt; implementations for usage scenarios that don&amp;#39;t fit standard providers.&lt;/p&gt;

&lt;p&gt;Planned demos will illustrate how to connect our&amp;nbsp;AI Chat Control to individual agents, composite workflows, AG-UI backends, and tool approval workflows in agentic pipelines.&lt;/p&gt;

&lt;h3 id="api-enhancements"&gt;API Enhancements&lt;/h3&gt;

&lt;p&gt;v26.1 replaces the &lt;code&gt;MessageSent&lt;/code&gt; event with &lt;code&gt;MessageSending&lt;/code&gt;. This event fires before the message is added to chat history and sent to the AI service. Additionally, this event exposes an &lt;code&gt;e.Cancel&lt;/code&gt; parameter that allows you to block send operations entirely. Use it to preprocess and validate input, filter content, call external services and handle the messaging pipeline manually. Alternatively, if &lt;code&gt;e.Cancel&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;, the AI Chat Control will continue sending and displaying messages and allow you to log and audit user messages without disruption to the normal message pipeline.&lt;/p&gt;

&lt;p&gt;The new event also supports augmentation before delivery — for example, appending a system message or supplemental context to the chat history via the new &lt;code&gt;AppendMessageAsync&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;async void AiChatControl1_MessageSending(object sender, AIChatControlMessageSendingEventArgs e) {
    // Append a system message before sending the user&amp;#39;s prompt to the AI service.
    await e.Chat.AppendMessageAsync(&amp;quot;Translate text to Spanish&amp;quot;, ChatRole.System);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="empty-chat-customization"&gt;Empty Chat Customization&lt;/h3&gt;

&lt;p&gt;v26.1 introduces two new properties designed to customize initial chat state. &lt;code&gt;EmptyMessageAreaText&lt;/code&gt; specifies text dispalyed in the empty chat area, and &lt;code&gt;InputBoxNullText&lt;/code&gt; specifies placeholder text in the input box. These properties allow you to align the initial chat experience with application context and tone:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;DxAIChat EmptyMessageAreaText=&amp;quot;How can I help you today?&amp;quot;
          InputBoxNullText=&amp;quot;Ask a question or describe a task...&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/mcp-multi-llm/26-1-blazor-aichat-empty-chat-ui@2x.png" alt="Blazor AI Chat — Customized Empty Text Area and Input Null Text" style="width:780px;height:300px;"&gt;

&lt;h2 id="share-your-feedback"&gt;Share Your Feedback&lt;/h2&gt;

&lt;p&gt;Looking for a particular code example? &lt;a href="https://supportcenter.devexpress.com/" target="_blank" title="DevExpress Support Center"&gt;Contact us via the DevExpress Support Center&lt;/a&gt; to share your usage scenario and we&amp;#39;ll be happy to recommend an implementation.&lt;/p&gt;</description>
      <pubDate>Fri, 22 May 2026 00:01:00 Z</pubDate>
      <dc:creator>Dmitry Tokmachev (DevExpress)</dc:creator>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388281</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2026/03/24/blazor-end-of-support-for-bootstrap-v4-v26.1.aspx</link>
      <category domain="https://community.devexpress.com/Tags/ASP.NET">ASP.NET</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Bootstrap">Bootstrap</category>
      <category domain="https://community.devexpress.com/Tags/Featured">Featured</category>
      <category domain="https://community.devexpress.com/Tags/Themes">Themes</category>
      <category domain="https://community.devexpress.com/Tags/v26.1">v26.1</category>
      <title>Blazor — End of Support for Bootstrap v4 (v26.1)</title>
      <description>&lt;p&gt;As previously announced, &lt;a href="https://www.devexpress.com/blazor/" target="_blank"&gt;DevExpress Blazor&lt;/a&gt; deprecated support for Bootstrap v4 in our v25.1 release cycle. With v26.1, we expect to complete our&amp;nbsp;transition away from Bootstrap v4 by  removing support as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;DevExpress.Blazor.Themes&lt;/code&gt; package will no longer include Bootstrap v4-based stylesheets for &lt;a href="https://docs.devexpress.com/Blazor/404360/styling-and-themes/devexpress-theme-customization"&gt;classic themes&lt;/a&gt; (Blazing Berry, Blazing Dark, Office White, Purple).&lt;/li&gt;
&lt;li&gt;All remaining theme dependencies on Bootstrap v4 or default CSS will end.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post explains why this change was made, how it impacts existing apps, and what actions you may need to take before upgrading.&lt;/p&gt;
&lt;h2&gt;Bootstrap v4:&amp;nbsp;End-of-Life Status&lt;/h2&gt;
&lt;p&gt;Bootstrap v4 was&amp;nbsp;a reliable and widely used CSS framework for many years. It reached its &lt;a href="https://github.com/twbs/release?tab=readme-ov-file#release-schedule"&gt;end of life (EOL) in 2023&lt;/a&gt; and is no longer maintained or updated.&lt;/p&gt;
&lt;p&gt;Continuing to leverage a non-supported framework increases maintenance costs and introduces potential long-term risks for apps that rely on the framework. To align with modern development practices and ensure a stable platform for future releases, DevExpress Blazor will end Bootstrap v4 support once we officially ship&amp;nbsp;v26.1.&lt;/p&gt;
&lt;h2&gt;Bootstrap v5 Support&lt;/h2&gt;
&lt;p&gt;In our v25.1 release cycle, we formally deprecated Bootstrap v4 support&amp;nbsp;and marked the &lt;code&gt;BootstrapVersion&lt;/code&gt; global option as obsolete (see the following breaking change notice for additional information in this regard: &lt;a href="https://supportcenter.devexpress.com/ticket/details/t1288580/globaloption-bootstrapversion-is-now-obsolete"&gt;GlobalOption.BootstrapVersion is now obsolete&lt;/a&gt;).&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This announcement gave customers time to transition away from Bootstrap v4&amp;nbsp;and address styling requirements ahead of full removal.&lt;/p&gt;
&lt;h2&gt;Impact on Existing Apps&lt;/h2&gt;
&lt;p&gt;You should review your project if any of the following applies to your DevExpress-powered Blazor app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You explicitly set Bootstrap version to &lt;code&gt;v4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You did not specify Bootstrap version prior to v25.1.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;You applied a DevExpress classic theme (Blazing Berry, Blazing Dark, Purple, or Office White) using a Bootstrap v4-based stylesheet:&lt;/span&gt;&lt;br&gt;&lt;code&gt;&amp;lt;link href=@AppendVersion(&amp;quot;_content/DevExpress.Blazor.Themes/blazing-berry.bs4.min.css&amp;quot;) rel=&amp;quot;stylesheet&amp;quot;&amp;nbsp;/&amp;gt;&lt;/code&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;Your custom styles relied on Bootstrap v4 classes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Steps to Take Before You Upgrade to v26.1&lt;/h2&gt;
&lt;p&gt;Before upgrading to v26.1, you must:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Remove any usage of &lt;code&gt;BootstrapVersion=v4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Migrate custom styles from Bootstrap v4 to &lt;a href="https://getbootstrap.com/docs/5.0/migration/#sass"&gt;Bootstrap v5&lt;/a&gt; where necessary.&lt;/li&gt;
&lt;li&gt;Replace Bootstrap v4-based theme stylesheets with supported alternatives. Please review the following help topic for additional information: &lt;a href="https://docs.devexpress.com/Blazor/401523/styling-and-themes/themes"&gt;DevExpress Blazor Themes&lt;/a&gt;.&lt;/li&gt;
&lt;p&gt;Once&amp;nbsp;you upgrade to v26.1, thoroughly test layouts and component appearance.&lt;/p&gt;
&lt;/ul&gt;
&lt;h2&gt;Your Feedback Matters&lt;/h2&gt;
&lt;p&gt;If you have questions about this change or need personal assistance, feel free to contact us via the &lt;a href="https://supportcenter.devexpress.com/ticket/create/"&gt;DevExpress Support Center&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Tue, 24 Mar 2026 03:00:00 Z</pubDate>
      <dc:creator>Elena Peskova (DevExpress)</dc:creator>
      <dx:excerpt>As announced earlier, DevExpress Blazor deprecated support for Bootstrap v4 in v25.1. With v26.1, we&amp;#39;re completing this transition by entirely removing Bootstrap v4 support.</dx:excerpt>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388278</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2026/02/24/blazor-june-2026-roadmap-v26-1.aspx</link>
      <category domain="https://community.devexpress.com/Tags/2026">2026</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Featured">Featured</category>
      <category domain="https://community.devexpress.com/Tags/Roadmap">Roadmap</category>
      <category domain="https://community.devexpress.com/Tags/Survey">Survey</category>
      <category domain="https://community.devexpress.com/Tags/v26.1">v26.1</category>
      <title>Blazor — June 2026 Roadmap (v26.1)</title>
      <description>&lt;div class="Note"&gt;The information contained within this blog post details our current/projected development plans. Please note that this information is being shared for INFORMATIONAL PURPOSES ONLY and does not represent a binding commitment on the part of Developer Express Inc. This blog post and the features/products listed within it are subject to change. You should not rely or use this information to help make a purchase decision about Developer Express Inc products.&lt;/div&gt;
&lt;h2 id="blazor-2026-strategy"&gt;Blazor 2026 Strategy&lt;/h2&gt;
&lt;p&gt;As you will see below, we expect to continue extending the capabilities of individual components — but we also want to invest in larger initiatives that span multiple release cycles.&lt;/p&gt;
&lt;p&gt;Many of these initiatives require foundational work (architecture, styling infrastructure, interaction layers, and design-system alignment) before we can commit to a specific release or prepare a complete feature list. Some of this work will not be delivered in the first half of the year (including v26.1).&lt;/p&gt;
&lt;p&gt;Even with our evolving objectives, we want to be transparent about what we are focusing on, why some details are not available yet, and what direction we hope to move toward over time.&lt;/p&gt;
&lt;h3 id="styling-and-layout-foundations"&gt;Styling and Layout Foundations&lt;/h3&gt;
&lt;p&gt;We want DevExpress Blazor components to address more UI needs out of the box, so you can build cohesive layouts with minimal custom CSS.&lt;/p&gt;
&lt;p&gt;Historically, many apps relied on Bootstrap utility classes and layout patterns as the primary styling foundation. Bootstrap is still available, but it does not match modern theme requirements (for instance, misses runtime-configurable accent colors).&lt;/p&gt;
&lt;p&gt;Our direction is to create theme-aware styling alternatives so you do not need to recreate these fundamentals yourself. We are exploring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DevExpress CSS helper classes for common spacing/layout patterns.&lt;/li&gt;
&lt;li&gt;Lightweight &amp;quot;primitive&amp;quot; components for element composition (for instance, Card, Badge, Avatar).&lt;/li&gt;
&lt;/ul&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/Roadmap/Styling%20and%20Layout%20Foundations%20-%20first%20image%20(2x).png" alt="Styling and Layout Foundations (part 1)"&gt;
&lt;p&gt;We also plan to reduce the amount of fine-tuning needed for complex layouts (alignment, consistent spacing, border/background treatments), especially when size mode can change at runtime. Our goal is to improve visual defaults and introduce new layout-focused settings. These settings will allow you to implement common layouts using component APIs, theme-aware patterns, and helper classes mentioned above.&lt;/p&gt;
&lt;p&gt;Finally, we’ll build demos and project templates that recreate popular app layouts without custom styling.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/Roadmap/Styling%20and%20Layout%20Foundations%20-%20second%20image%20(2x).png" alt="Styling and Layout Foundations (part 2)"&gt;
&lt;h3 id="classic-theme-deprecation"&gt;Classic Theme Deprecation&lt;/h3&gt;
&lt;p&gt;Blazing Berry, Office White, Purple, Blazing Dark, and Bootstrap-based themes use an old architecture, are hard to maintain, and do not support new styling capabilities (accent colors, public CSS variables, and our UI Kit). We want to focus our efforts on new styling architecture and gradually move away from classic themes. &lt;/p&gt;
&lt;p&gt;We plan to make this transition carefully. Before we deprecate any classic theme, we will prepare viable alternatives, publish clear migration guidance, and give you enough time to move at your own pace.&lt;/p&gt;
&lt;h3 id="new-themes"&gt;New Themes&lt;/h3&gt;
&lt;p&gt;We want to expand our modern theme lineup (starting with Fluent) and evolve our styling infrastructure. Theme directions that we are currently considering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Material — a frequent request, and a good fit for teams building hybrid solutions that mix UI stacks (including DevExtreme).&lt;/li&gt;
&lt;li&gt;A classic-inspired modern theme — a longer-term option for customers who prefer a look closer to our classic themes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have strong preferences on one or the other, we would like to hear your thoughts (please submit a support ticket via the DevExpress Support Center at anytime).&lt;/p&gt;
&lt;h3 id="icon-library"&gt;Icon Library&lt;/h3&gt;
&lt;p&gt;We plan to make our DevExpress Icon Library available to Blazor developers and expand it with new icons. We also expect to introduce a new API that will simplify icon usage for built-in/custom icons, across multiple themes and size modes.&lt;/p&gt;
&lt;h3 id="rtl-support"&gt;RTL Support&lt;/h3&gt;
&lt;p&gt;Right-to-left (RTL) support is an important direction for us and for many of you. We are researching RTL requirements across our Blazor component suite and do preliminary work so components behave more consistently in RTL&amp;nbsp; mode (layout, alignment, and interaction patterns).&lt;/p&gt;
&lt;p&gt;We do not expect RTL support to be available in v26.1, but we will work on it throughout this year.&lt;/p&gt;
&lt;h3 id="performance"&gt;Performance&lt;/h3&gt;
&lt;p&gt;We made significant performance improvements in 2025, and still performance remains an ongoing investment area. We plan to continue researching and testing optimizations across our Blazor component suite. Our focus areas include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Memory throughput and performance under a large number of concurrent sessions.&lt;/li&gt;
&lt;li&gt;Reducing Blazor logical and visual tree size.&lt;/li&gt;
&lt;li&gt;Reducing the number of WebSocket messages per interaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once new optimizations are ready, we will add more info to this roadmap.&lt;/p&gt;
&lt;h3 id="security"&gt;Security&lt;/h3&gt;
&lt;p&gt;Security is a core requirement for our components. We already use secure development practices, including automated security scans/reviews. We plan to continue strengthening this process, conducting even more&amp;nbsp;penetration tests, and publish a software bill of materials (SBOM) for DevExpress Blazor.&lt;/p&gt;
&lt;h3 id="accessibility"&gt;Accessibility&lt;/h3&gt;
&lt;p&gt;Accessibility is another key focus area for us. We will&amp;nbsp;continue delivering fixes and improvements across DevExpress Blazor components (keyboard navigation, semantics/ARIA, and visual states).&lt;/p&gt;
&lt;h2 id="share-feedback-on-the-strategy"&gt;Share Feedback on the Strategy&lt;/h2&gt;
&lt;p&gt;We’d like your feedback on these strategic directions before we lock in detailed plans:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which directions are most important for you in 2026?&lt;/li&gt;
&lt;li&gt;Are we missing any strategic areas?&lt;/li&gt;
&lt;li&gt;Do you have concerns about tradeoffs (for example, themes vs. component features)?&lt;/li&gt;
&lt;li&gt;Any general comments on the strategy or where we should focus first?&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-survey-id="ef59a25a-5f58-4794-90bf-053a4e303c72" data-survey-auth-required="false"&gt;&lt;/div&gt;
&lt;h2 id="product-plans"&gt;Product Plans&lt;/h2&gt;
&lt;p&gt;Our mid-year&amp;nbsp;Blazor roadmap will be updated regularly to reflect development progress. As we get closer to the release, we expect to add additional&amp;nbsp;items and modify the status of planned features. Look for the following labels next to every feature:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Planned&lt;/span&gt; — Feature is scheduled but not yet in development.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt; — Under active development.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in EAP v26.1&lt;/span&gt; — Will be available in our first v26.1 early access preview.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt; — Will be included in the final v26.1 release.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v26.1&lt;/span&gt; — Fully implemented and available.&lt;/p&gt;&lt;/li&gt;    
&lt;/ul&gt;
&lt;h3 id="accessibility-enhancements"&gt;Accessibility Enhancements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Improvements and fixes for our Blazor Data Editors&lt;/li&gt;
&lt;li&gt;Screen reader support for the Blazor Rich Text Editor&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="blazor-smart-paste"&gt;Blazor Smart Paste&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We expect to add AI-powered Smart Paste functionality in v26.1.&lt;/p&gt;
&lt;p&gt;Smart Paste will analyze clipboard content and populate each form field with relevant data (to transform traditional copy-and-paste operations).&lt;/p&gt;
&lt;img src="https://www.devexpress.com/subscriptions/i/25.2/25-2-js-form-smart-paste@2x.gif" alt="New Built-in Command Column Action Buttons"&gt;
&lt;h3 id="blazor-grid-treelist"&gt;Blazor Grid &amp;amp; TreeList&lt;/h3&gt;
&lt;h4 id="focus-cells"&gt;Focus Cells&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;DevExpress Blazor Grid and TreeList components will support focused cell highlighting. While these components already display focus borders during keyboard navigation, this feature adds a new highlight layer for the active cell (both when users navigate with the keyboard or select a cell with the mouse).&lt;/p&gt;
&lt;p&gt;We will also introduce APIs to identify the focused cell, move focus to a specific cell, and react to focused cell changes.&lt;/p&gt;
&lt;h4 id="total-number-of-records-in-pager"&gt;Total Number of Records in Pager&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;DevExpress Blazor Grid and TreeList components will display the total number of records in the Pager to improve data navigation clarity.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/Roadmap/Grid%20-%20Total%20Number%20of%20Records%20in%20Pager%20(2x).png" alt="Grid - Total Number of Records in Pager"&gt;
&lt;h4 id="built-in-toolbar-items"&gt;Built-in Toolbar Items&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We expect to add built-in toolbar items to the DevExpress Blazor Grid and TreeList components. Built-in items will include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Column Chooser&lt;/li&gt;
&lt;li&gt;Export Buttons (CSV, PDF, XLS, and XLSX)&lt;/li&gt;
&lt;li&gt;Search Box&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="built-in-criteria-selector-menu"&gt;Filter Row — Built-in criteria selector menu&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We hope to introduce a Criteria Selector Menu for our Blazor Grid and TreeList Filter Row. This menu allows users to select filter conditions directly within the row editor and apply them to the entered value. &lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/Roadmap/Grid%20-%20Built-in%20criteria%20selector%20menu%20(2x).png" alt="Grid - Built-in criteria selector menu"&gt;
&lt;h4 id="bind-filter-on-input-option"&gt;Filter Row — Bind Filter On Input Option&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Filter Row Data Editors will support on‑input data binding mode. In this mode, DevExpress Blazor Grid and TreeList components apply filters as users type (no need to press Enter). &lt;/p&gt;
&lt;h3 id="ui-ux-enhancements"&gt;UI/UX Enhancements&lt;/h3&gt;
&lt;h4 id="vertical-grid-line-visibility"&gt;Vertical Grid Line Visibility&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will add an option to hide column lines (vertical separators between adjacent columns) in our Blazor Grid and TreeList components.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/Roadmap/Grid%20-%20Vertical%20Grid%20Line%20Visibility%20(2x).png" alt="Column Lines Visibility Customization"&gt;
&lt;h4 id="striped-rows"&gt;Striped Rows&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to introduce an option to highlight alternating (odd) rows in our&amp;nbsp;Blazor Grid and TreeList with a different style (to enhance readability).&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/26.1/Roadmap/Grid%20-%20Striped%20Rows%20(2x).png" alt="Striped Rows"&gt;
&lt;h4 id="filter-menu-button-new-on-hover-display-mode"&gt;Filter Menu Button — New On‑Hover Display Mode&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;DevExpress Blazor Grid and TreeList will support on‑hover display mode for Filter Menu buttons. When enabled, buttons will appear after users hover over column headers. This mode helps create a cleaner UI and avoid column caption truncation.&lt;/p&gt;
&lt;h4 id="custom-row-height"&gt;Custom Row Height&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In v26.1, you will be able to specify a minimum row height for the DevExpress Blazor Grid and TreeList. This new option allows you to adjust visual density by increasing/decreasing row spacing and gives you more control over row appearance.&lt;/p&gt;
&lt;h3 id="blazor-treelist-unbound-columns"&gt;Blazor TreeList — Unbound Columns&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to introduce unbound column support for the DevExpress Blazor TreeList component. Unbound columns display values that are not stored in the underlying data source. Our TreeList can calculate these values on the fly or derive them from other fields. Use unbound columns to display supplementary/computed data without modifying the original data model.&lt;/p&gt;
&lt;h3 id="blazor-data-editors"&gt;Blazor Data Editors&lt;/h3&gt;
&lt;h4 id="textbox-password-button"&gt;TextBox — Password Button&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;v26.1 will include a built‑in button that displays/hides a password within the DevExpress Blazor TextBox editor. This button allows users to verify text they have entered.&lt;/p&gt;
&lt;h4 id="spinedit-maskedinput-disable-arrow-keys"&gt;SpinEdit &amp;amp; MaskedInput — Disable Arrow Keys&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Both DevExpress Blazor SpinEdit and MaskedInput components will allow you to prevent a change to values&amp;nbsp;when users press Up/Down arrow keys.&lt;/p&gt;
&lt;h4 id="maskedinput-disable-mouse-wheel"&gt;MaskedInput — Disable Mouse Wheel&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Our Blazor MaskedInput will allow you to prevent a change to values&amp;nbsp;when users scroll&amp;nbsp;the mouse wheel. This option prevents accidental modifications during page/content scrolling.&lt;/p&gt;
&lt;h4 id="focusable-custom-buttons"&gt;Focusable Custom Buttons&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Our Blazor Data Editors will include a new Focusable option for custom buttons. Once activated, users can interact with custom buttons using the keyboard.&lt;/p&gt;
&lt;h3 id="blazor-formlayout"&gt;Blazor FormLayout&lt;/h3&gt;
&lt;h4 id="new-caption-position-mode"&gt;New Caption Position Mode&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We expect to extend the CaptionPosition property with a new Auto mode. In this mode, Form Layout will automatically switch the caption to a vertical position when horizontal space is insufficient. This behavior matches the current implementation of the Horizontal mode.&lt;/p&gt;
&lt;p&gt;With this update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto becomes the new default caption position and enables automatic switching based on available space.&lt;/li&gt;
&lt;li&gt;Horizontal mode will no longer change caption position and will always display captions horizontally, regardless of layout constraints.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="api-enhancements"&gt;API Enhancements&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The DevExpress Blazor FormLayout component will include the following new API members:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TabsPosition — Specifies&amp;nbsp;tab header position.&lt;/li&gt;
&lt;li&gt;AllowClose | AllowTabReorder — Allows users to close/reorder tabs.&lt;/li&gt;
&lt;li&gt;TabClosing | TabReordering — Allows you to handle tab closing/reordering actions.&lt;/li&gt;
&lt;li&gt;TabPageEnabled — Allows you to disable a tab page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="blazor-dialog-service-content-template"&gt;Blazor Dialog Service - Content Template&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We hope to add a content template option for message dialogs displayed via the DevExpress Blazor Dialog Service. This option will allow you to customize the dialog body: format message text, add links, or display additional interactive elements.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private async Task OpenConfirmDialogAsync() {
    Result = await DialogService.ConfirmAsync(new MessageBoxOptions() {
        ContentTemplate = @&amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;Are you sure you want to delete this record?&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;This action cannot be undone.&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;,
        OkButtonText = &amp;quot;Yes&amp;quot;,
        CancelButtonText = &amp;quot;No&amp;quot;,
        RenderStyle = MessageBoxRenderStyle.Danger,
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="blazor-layout-navigation"&gt;Blazor Layout &amp;amp; Navigation&lt;/h3&gt;
&lt;h4 id="treeview-accordion-menu-toolbar-data-mapping-api-enhancements"&gt;TreeView, Accordion, Menu &amp;amp; Toolbar — Data Mapping API Enhancements&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to extend Data Mapping APIs available for DevExpress Blazor TreeView, Accordion, Menu, and Toolbar components. New APIs will allow you to implement more business scenarios using data binding (instead of static Razor markup).&lt;/p&gt;
&lt;p&gt;Use cases include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build navigation from a shared model (for instance, a sitemap or route list).&lt;/li&gt;
&lt;li&gt;Display/hide items based on permissions/flags.&lt;/li&gt;
&lt;li&gt;Generate hierarchical structures (groups, child items) based on data.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-ts"&gt;&amp;lt;DxTreeView Data=&amp;quot;@Data&amp;quot;&amp;gt;
    &amp;lt;DataMappings&amp;gt;
        &amp;lt;DxTreeViewDataMapping Text=&amp;quot;Name&amp;quot;
                               Key=&amp;quot;Id&amp;quot;
                               BadgeText=&amp;quot;Badge&amp;quot;
                               ParentKey=&amp;quot;CategoryId&amp;quot;/&amp;gt;
    &amp;lt;/DataMappings&amp;gt;
&amp;lt;/DxTreeView&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="accordion-cache-collapsed-content"&gt;Accordion — Cache Collapsed Content&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The DevExpress Blazor Accordion will allow you to cache collapsed group content. When caching is active, the Accordion retains collapsed content and does not need to recreate it once the group is expanded.&lt;/p&gt;
&lt;p&gt;This feature is useful for groups containing complex UI content that is costly to re-initialize (for instance, Forms with validation, Charts, or Data Grids).&lt;/p&gt;
&lt;h3 id="blazor-pivot-table"&gt;Blazor Pivot Table&lt;/h3&gt;
&lt;h4 id="server-mode"&gt;Server Mode&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We expect to support Server Mode in the DevExpress Pivot Table component. In this mode, the Pivot Table retrieves data on demand and executes grouping, filtering, and summary calculations on the server. Server‑side data processing optimizes performance when working with large datasets and complex filters. &lt;/p&gt;
&lt;h4 id="drill-down-support"&gt;Drill‑Down Support&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Postponed to v26.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to support drill‑down scenarios in the DevExpress Blazor Pivot Table. Drill‑down allows you to retrieve the underlying records used to calculate a specific summary value. This capability is useful when you need to examine the data behind aggregated results.&lt;/p&gt;
&lt;p&gt;New APIs will include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CellClick — Provides access to the underlying data of the clicked cell.&lt;/li&gt;
&lt;li&gt;CreateDrillDownDataSource(options) — Returns a drill‑down data source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will be able to bind underlying data to our Blazor Grid component and allow users to review detailed records within the same page.&lt;/p&gt;
&lt;h3 id="blazor-themes"&gt;Blazor Themes&lt;/h3&gt;
&lt;h4 id="fluent-themes-high-contrast-mode"&gt;Fluent Themes — High Contrast Mode&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;DevExpress Blazor Fluent themes will support high contrast mode in our June release. They will automatically modify colors if Windows &lt;a href="https://support.microsoft.com/en-us/windows/turn-high-contrast-mode-on-or-off-in-windows-909e9d89-a0f9-a3a9-b993-7a6dcee85025"&gt;high contrast mode&lt;/a&gt; is active.&lt;/p&gt;
&lt;h2 id="blazor-reporting"&gt;Blazor Reporting&lt;/h2&gt;
&lt;p&gt;Learn more:&amp;nbsp;&lt;a href="https://community.devexpress.com/blogs/reporting/archive/2026/02/10/reporting-amp-bi-dashboard-june-2026-roadmap-v26-1.aspx" target="_blank"&gt;Reporting &amp;amp; BI Dashboard — June 2026 Roadmap (v26.1)&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="your-feedback"&gt;Your Feedback Matters&lt;/h2&gt;
&lt;div data-survey-id="73a2487e-13ca-41b5-9bc3-3d4f1ef4f317" data-survey-auth-required="false"&gt;&lt;/div&gt;</description>
      <pubDate>Tue, 24 Feb 2026 08:30:00 Z</pubDate>
      <dc:creator>Slava Khudyakov (DevExpress)</dc:creator>
      <dx:excerpt>Thank you for choosing DevExpress and for your on-going support. We value your business. As you will see below, we expect to continue extending the capabilities of individual components — but we also want to invest in larger initiatives that span multiple release cycles.&#xD;
&#xD;
</dx:excerpt>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388264</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2026/01/08/devexpress-blazor-ai-chat-agent2agent-a2a-protocol-integration.aspx</link>
      <category domain="https://community.devexpress.com/Tags/A2A">A2A</category>
      <category domain="https://community.devexpress.com/Tags/Agent">Agent</category>
      <category domain="https://community.devexpress.com/Tags/AI">AI</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Chat+UI">Chat UI</category>
      <category domain="https://community.devexpress.com/Tags/LLM">LLM</category>
      <title>DevExpress Blazor AI Chat — Agent2Agent (A2A) Protocol Integration</title>
      <description>&lt;p&gt;Modern AI-powered applications increasingly implement automated functions through specialized AI agents. The statistics show impressive growth: &lt;a href="https://www.index.dev/blog/ai-agents-statistics" target="_blank" title="50+ Key AI Agent Statistics and Adoption Trends in 2025"&gt;in 2025, 85% of organizations have integrated AI agents into at least one workflow&lt;/a&gt;. Users now expect conversational interfaces for various business tasks: customer service automation, personalized shopping assistance, financial transaction management, content creation, and streamlined workflow coordination.&lt;/p&gt;
&lt;p&gt;The key distinction between AI agents and working with standard large language models lies in a simple formula: an agent equals LLM plus instructions and tools it can invoke, representing&amp;nbsp;an autonomous system. &lt;/p&gt;&lt;p&gt;&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/a2a/a2a-what-is-an-agent.png" alt="What is an AI Agent — Diagram" style="width:2360px;height:1336px;"&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Source/Credit: &lt;a href="https://www.youtube.com/watch?v=DUdRdeUtuZQ" target="_blank" title="Azure AI Foundry: The AI app and Agent Factory | BRK155"&gt;Microsoft Developer YouTube channel&lt;/a&gt;.&amp;nbsp;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;While LLMs can only generate text, agents extend these capabilities into actionable intelligence by integrating external APIs, accessing databases, managing files, and connecting with enterprise systems. This transforms them from passive text generators into autonomous systems capable of executing complex, multi-step tasks. One of the first agents that was introduced in the industry were&amp;nbsp;ChatGPT plugins that added web browsing and code interpreter capabilities (allowing the LLM to search the Web and execute code while replying to a user query).&lt;/p&gt;
&lt;p&gt;In April 2025, Google introduced its&amp;nbsp;&lt;a target="_blank" href="https://a2a-protocol.org/latest"&gt;Agent2Agent (A2A) protocol&lt;/a&gt; — an open standard for standardizing communication between agents. The protocol has gained support from over 150 organizations, including tech giants like Microsoft, Salesforce, SAP, ServiceNow, and Amazon Web Services. The protocol&amp;#39;s advantage lies in enabling agents developed by different teams on various platforms to seamlessly exchange information and coordinate actions, creating essentially an &amp;quot;internet of agents&amp;quot; or &amp;quot;agentic web&amp;quot; — similar to how HTTP allowed websites to interact with each other.&lt;/p&gt;
&lt;p&gt;The benefits notwithstanding, integrating agents with user interface components presents technical challenges. Agents use structured data exchange formats and support complex task lifecycles, while chat components expect simple text messages and streaming responses. In this article, I describe how to connect the &lt;a target="_blank" href="https://docs.devexpress.com/Blazor/405290/components/ai-chat"&gt;DevExpress Blazor AI Chat&lt;/a&gt; component to A2A protocol-compliant agents with a small adapter. The implementation hosts four agents in separate ASP.NET Core servers and switches agents at runtime within a single chat UI. The sample also includes a &lt;em&gt;Researcher&lt;/em&gt; agent from the official &lt;a target="_blank" href="https://github.com/a2aproject/a2a-dotnet"&gt;Microsoft A2A .NET SDK samples&lt;/a&gt; to show compatibility with existing agents.&lt;/p&gt;
&lt;div class="Note"&gt;
    &lt;p&gt;This example focuses on agent connectivity rather than orchestration. The A2A protocol supports advanced inter-agent patterns such as routing, delegation, and task handoffs, but this sample does not implement them. Any A2A-compliant agent can connect to the Blazor AI Chat component with the approach documented here.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id="architectureoverview"&gt;Architecture Overview&lt;/h2&gt;
&lt;p&gt;The solution uses a distributed layout. A shared library defines all agent classes and base types. Four ASP.NET Core projects host &lt;strong&gt;Poem&lt;/strong&gt;, &lt;strong&gt;Shakespearean Style&lt;/strong&gt;, &lt;strong&gt;Task&lt;/strong&gt;, and &lt;strong&gt;Researcher&lt;/strong&gt; agents. Each server runs on its own port and exposes a single A2A endpoint at &lt;code&gt;/agent&lt;/code&gt;. A Blazor Server app embeds the &lt;strong&gt;DevExpress AI Chat&lt;/strong&gt; component and talks to agents through &lt;code&gt;IChatClient&lt;/code&gt;-based adapters.&lt;/p&gt;
&lt;h2 id="a2acompliantagentsserverapp"&gt;Agent Server Apps&lt;/h2&gt;
&lt;p&gt;Each agent server follows the same pattern. The example below shows a minimal setup that maps the A2A pipeline to &lt;code&gt;/agent&lt;/code&gt; and wires an agent to a task manager.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using A2A;
using A2A.AspNetCore;
using Azure;
using Azure.AI.OpenAI;
using A2AAgents.Shared;
using A2AAgentsServer.Agents;
using Microsoft.Extensions.AI;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

TaskManager taskManager = new TaskManager();

var settings = builder.Configuration.GetSection(&amp;quot;AzureOpenAISettings&amp;quot;).Get&amp;lt;AzureOpenAIServiceSettings&amp;gt;();
if (settings == null || string.IsNullOrEmpty(settings.Endpoint) || string.IsNullOrEmpty(settings.Key) || string.IsNullOrEmpty(settings.DeploymentName))
    throw new InvalidOperationException(&amp;quot;Specify Azure OpenAI endpoint, key, and deployment name in appsettings.json.&amp;quot;);
var chatClient = new AzureOpenAIClient(new Uri(settings.Endpoint), new AzureKeyCredential(settings.Key))
    .GetChatClient(settings.DeploymentName)
    .AsIChatClient();

var agent = new PoemAgent(chatClient, taskManager);

app.UseHttpsRedirection();
app.MapA2A(taskManager, &amp;quot;/agent&amp;quot;);
app.Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Shakespearean Style, Task, and Researcher servers use the same structure. They differ by agent type and the port configured in each project&amp;#39;s launch settings.&lt;/p&gt;
&lt;h2 id="protocolcompliancevalidationwitha2ainspector"&gt;Protocol Compliance Validation with a2a-inspector&lt;/h2&gt;
&lt;p&gt;All agents pass validation with the &lt;a target="_blank" href="https://github.com/a2aproject/a2a-inspector" title="a2a-inspector tool – GitHub Repository"&gt;a2a-inspector tool&lt;/a&gt;. The inspector checks Agent Card metadata, message format, and streaming support, task state signaling for task agents, and protocol version compatibility.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/a2a/a2a-inspector-poem-agent.png" alt="a2a-inspector - The Poem Agent Validation Screenshot" style="width:1616px;height:2400px;"&gt;
&lt;h2 id="bridginga2atoichatclient"&gt;Bridging A2A to IChatClient&lt;/h2&gt;
&lt;p&gt;The key to this integration is a lightweight adapter that translates between A2A protocol messages and the &lt;code&gt;IChatClient&lt;/code&gt; interface used by DevExpress Blazor AI Chat to interact with LLMs. This adapter maintains conversation context, converts chat messages to A2A protocol format, and streams responses back to the UI:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public sealed class MessageAgentChatClient : DelegatingChatClient {
    private readonly A2AClient _agentClient;
    private readonly string _contextId;

    public MessageAgentChatClient(IChatClient innerClient, A2AClient agentClient, string contextId) 
        : base(innerClient) {
        _agentClient = agentClient ?? throw new ArgumentNullException(nameof(agentClient));
        _contextId = $&amp;quot;{contextId}-{Guid.NewGuid()}&amp;quot;;
    }

    public override async IAsyncEnumerable&amp;lt;ChatResponseUpdate&amp;gt; GetStreamingResponseAsync(
        IEnumerable&amp;lt;ChatMessage&amp;gt; messages, 
        ChatOptions? options = null,
        [EnumeratorCancellation] CancellationToken cancellationToken = default) {

        var sendParams = new MessageSendParams { 
            Message = CreateA2AMessage(messages) 
        };

        await foreach (var item in _agentClient.SendMessageStreamingAsync(sendParams, cancellationToken)) {
            if (item.Data is AgentMessage message &amp;amp;&amp;amp; message.Parts.FirstOrDefault() is TextPart textPart) {
                yield return new ChatResponseUpdate(ChatRole.Assistant, textPart.Text);
            }
        }
    }
    
    private AgentMessage CreateA2AMessage(IEnumerable&amp;lt;ChatMessage&amp;gt; messages) {
            return new AgentMessage() {
                Role = MessageRole.User,
                ContextId = _contextId,
                Parts = [new TextPart() { Text = messages.Last().Text }]
            };
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="runtimeagentselection"&gt;Runtime Agent Selection&lt;/h2&gt;
&lt;p&gt;The app registers each agent as a keyed &lt;code&gt;IChatClient&lt;/code&gt; service and switches between them at runtime through a simple dropdown.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Register each A2A agent as a keyed IChatClient service
builder.Services.AddKeyedScoped&amp;lt;IChatClient&amp;gt;(AgentsEndpoints.PoemAgent, (provider, key) =&amp;gt;
    new MessageAgentChatClient(chatClient, 
        new A2AClient(new Uri(AgentsEndpoints.PoemAgent)), 
        AgentsEndpoints.PoemAgent));

builder.Services.AddKeyedScoped&amp;lt;IChatClient&amp;gt;(AgentsEndpoints.ShakespeareanStyleAgent, (provider, key) =&amp;gt;
    new MessageAgentChatClient(chatClient, 
        new A2AClient(new Uri(AgentsEndpoints.ShakespeareanStyleAgent)), 
        AgentsEndpoints.ShakespeareanStyleAgent));

builder.Services.AddKeyedScoped&amp;lt;IChatClient&amp;gt;(AgentsEndpoints.TaskAgent, (provider, key) =&amp;gt;
    new TaskAgentChatClient(chatClient, 
        new A2AClient(new Uri(AgentsEndpoints.TaskAgent)), 
        AgentsEndpoints.TaskAgent));

builder.Services.AddKeyedScoped&amp;lt;IChatClient&amp;gt;(AgentsEndpoints.ResearcherAgent, (provider, key) =&amp;gt;
    new TaskAgentChatClient(chatClient, 
        new A2AClient(new Uri(AgentsEndpoints.ResearcherAgent)), 
        AgentsEndpoints.ResearcherAgent));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.AIIntegration.Blazor.Chat.DxAIChat.ChatClientServiceKey"&gt;ChatClientServiceKey&lt;/a&gt; property (available with DevExpress v25.1.4) enables runtime switching between multiple &lt;code&gt;IChatClient&lt;/code&gt; services.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;DxAIChat @ref=&amp;quot;_dxAIChat&amp;quot; 
          UseStreaming=&amp;quot;true&amp;quot; 
          ChatClientServiceKey=&amp;quot;@CurrentServiceKey&amp;quot;
          CssClass=&amp;quot;chat-container&amp;quot; /&amp;gt;

&amp;lt;DxComboBox Data=&amp;quot;@AgentOptions&amp;quot;
            TextFieldName=&amp;quot;nameof(AgentOption.Text)&amp;quot;
            ValueFieldName=&amp;quot;nameof(AgentOption.Value)&amp;quot;
            @bind-Value=&amp;quot;@CurrentServiceKey&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="welcomechatscreen"&gt;Welcome Chat Screen&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;EmptyMessageAreaTemplate&lt;/code&gt; shows agent descriptions and instructions when the chat is empty. This helps users understand each agent and start a conversation quickly.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;EmptyMessageAreaTemplate&amp;gt;
        &amp;lt;div class=&amp;quot;empty-message-area&amp;quot;&amp;gt;
            &amp;lt;h4&amp;gt;🤖 Multi-Agent AI Chat&amp;lt;/h4&amp;gt;
            &amp;lt;p&amp;gt;Welcome to the Agent-to-Agent communication platform! Choose your specialized AI agent and start chatting.&amp;lt;/p&amp;gt;

            &amp;lt;div&amp;gt;
                &amp;lt;h5&amp;gt;Available Agents:&amp;lt;/h5&amp;gt;
                &amp;lt;ul class=&amp;quot;agents-list&amp;quot;&amp;gt;
                    &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;🎭 Poem Agent:&amp;lt;/strong&amp;gt; Enter 2-5 keywords and I&amp;#39;ll generate a short lyrical poem&amp;lt;/li&amp;gt;
                    &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;📜 Shakespearean Style Agent:&amp;lt;/strong&amp;gt; Transform your text into William Shakespeare&amp;#39;s style&amp;lt;/li&amp;gt;
                    &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;📋 Task Agent:&amp;lt;/strong&amp;gt; Help plan and execute task descriptions&amp;lt;/li&amp;gt;
                    &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Researcher Agent:&amp;lt;/strong&amp;gt; Agent from https://github.com/a2aproject/a2a-dotnet/blob/main/samples/AgentServer/ResearcherAgent.cs &amp;lt;/li&amp;gt;
                    &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;💬 Default Chat Client:&amp;lt;/strong&amp;gt; Standard AI conversation&amp;lt;/li&amp;gt;
                &amp;lt;/ul&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div&amp;gt;
                &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Current Agent:&amp;lt;/strong&amp;gt; &amp;lt;span class=&amp;quot;current-agent-badge&amp;quot;&amp;gt;@AgentOptions.FirstOrDefault(x =&amp;gt; x.Value == CurrentServiceKey)?.Text&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;p class=&amp;quot;help-text&amp;quot;&amp;gt;
                &amp;lt;small&amp;gt;💡 Select an agent from the dropdown above, then type your message to start the conversation!&amp;lt;/small&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/EmptyMessageAreaTemplate&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/a2a/dx-ai-chat-empty-state.png" alt="DevExpress Blazor AI Chat — An Empty State Screen" style="width:1672px;height:1368px;"&gt;
&lt;h2 id="userexperience"&gt;User Experience&lt;/h2&gt;
&lt;p&gt;Users can select Poem, Shakespearean Style, Task, or Researcher agents via a dropdown, utilize manual handoffs by copying output&amp;nbsp;between agents to simulate composition workflows, and enjoy streaming responses that preserve the responsive feel of AI interactions. The video below demonstrates project functionality in action:&lt;/p&gt;
&lt;video controls=""&gt;
    &lt;source src="https://community.devexpress.com/blogs/aspnet/25.2/a2a/dx-ai-chat-agents-in-action.mp4" type="video/mp4"&gt;
    Your browser does not support the video tag.
&lt;/video&gt;
&lt;h2 id="downloadthecompleteexample"&gt;Download the Complete Example&lt;/h2&gt;
&lt;p&gt;To get started with this integration, clone the &lt;a target="_blank" href="https://github.com/DevExpress-Examples/blazor-ai-chat-a2a-mode"&gt;Blazor AI Chat — Communicate with Agents That Implement Agent2Agent (A2A) Protocol&lt;/a&gt; repository from GitHub and configure your AI service provider in &lt;code&gt;appsettings.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;{
  &amp;quot;AzureOpenAISettings&amp;quot;: {
    &amp;quot;Endpoint&amp;quot;: &amp;quot;https://your-instance.openai.azure.com/&amp;quot;,
    &amp;quot;Key&amp;quot;: &amp;quot;your-api-key&amp;quot;,
    &amp;quot;DeploymentName&amp;quot;: &amp;quot;your-model-name&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="configureagentendpoints"&gt;Configure Agent Endpoints&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;AgentsEndpoints&lt;/code&gt; class centralizes agent URLs. The app uses HTTP by default and includes HTTPS alternatives as comments.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static class AgentsEndpoints {
    public const string PoemAgent = &amp;quot;http://localhost:5003/agent&amp;quot;;
    public const string ShakespeareanStyleAgent = &amp;quot;http://localhost:5005/agent&amp;quot;;
    public const string TaskAgent = &amp;quot;http://localhost:5007/agent&amp;quot;;
    public const string ResearcherAgent = &amp;quot;http://localhost:5009/agent&amp;quot;;
    // HTTPS alternatives:
    // public const string PoemAgent = &amp;quot;https://localhost:5004/agent&amp;quot;;
    // public const string ShakespeareanStyleAgent = &amp;quot;https://localhost:5006/agent&amp;quot;;
    // public const string TaskAgent = &amp;quot;https://localhost:5008/agent&amp;quot;;
    // public const string ResearcherAgent = &amp;quot;https://localhost:5010/agent&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the four agent servers first (&lt;code&gt;PoemAgentServer&lt;/code&gt; on &lt;i&gt;http://localhost:5003&lt;/i&gt;, &lt;code&gt;ShakespeareanStyleAgentServer&lt;/code&gt; on &lt;i&gt;http://localhost:5005&lt;/i&gt;, &lt;code&gt;TaskAgentServer&lt;/code&gt; on &lt;i&gt;http://localhost:5007&lt;/i&gt;, &lt;code&gt;ResearcherAgentServer&lt;/code&gt; on &lt;i&gt;http://localhost:5009&lt;/i&gt;). Wait until each reports that it is listening on its port. Then start the Blazor Server app, select an agent in the UI, and start chatting. You can switch agents at runtime. The implementation supports Azure OpenAI, OpenAI API, and local runtimes like Ollama with minimal changes.&lt;/p&gt;
&lt;h2 id="yourfeedbackmatters"&gt;Your Feedback Matters&lt;/h2&gt;
&lt;p&gt;We&amp;#39;re gathering feedback to prioritize future enhancements to our AI agent integration capabilities. Please take a moment to share your experience and help us shape future AI integration features:&lt;/p&gt;
&lt;div data-survey-id="417c2966-5bc4-4437-a986-854132e72d76" data-survey-auth-required="false"&gt;&lt;/div&gt;</description>
      <pubDate>Thu, 08 Jan 2026 01:10:00 Z</pubDate>
      <dc:creator>Dmitry Tokmachev (DevExpress)</dc:creator>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388262</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2025/08/18/devexpress-blazor-ai-chat-tool-call-confirmation.aspx</link>
      <category domain="https://community.devexpress.com/Tags/AI">AI</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Chat+UI">Chat UI</category>
      <category domain="https://community.devexpress.com/Tags/Security">Security</category>
      <category domain="https://community.devexpress.com/Tags/Tool+Calling">Tool Calling</category>
      <title>DevExpress Blazor AI Chat — Tool Call Confirmation</title>
      <description>&lt;p&gt;Modern AI-powered applications often execute tools automatically in response to user queries. While this automation embraces the potential of LLMs and improves user experiences, it can introduce security risks when sensitive operations are invoked without explicit user consent — for example, modifying databases, sending emails, or making API calls to external services.&lt;/p&gt;

&lt;p&gt;This post outlines the purpose of our &lt;strong&gt;Tool Call Confirmation API layer&lt;/strong&gt; and associated customizable interface&amp;nbsp;for the DevExpress Blazor AI Chat component. As you would expect, our solution intercepts AI-initiated function calls, generates&amp;nbsp;detailed confirmation dialogs, and requires user approval before execution. This UI pattern is common in GitHub Copilot Chat, Cursor, Claude, and other AI-powered applications with MCP support.&lt;/p&gt;

&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/ai-tool-call-confirm/blazor-ai-chat-tool-calling-confirmation.png" alt="DevExpress Blazor AI Chat — Tool Calling Confirmation UI" style="width:725px;height:528px;"&gt;



&lt;p&gt;In this post, I&amp;#39;ll walk you through our AI Chat confirmation system, its key components, and customization options (and of course show you how to add this security layer to your AI-powered Blazor applications using the DevExpress Blazor AI Chat control).&lt;/p&gt;&lt;h2 id="the-challenge-balancing-automation-and-security"&gt;The Challenge: Balancing Automation and Security&lt;/h2&gt;

&lt;p&gt;AI function calling is a powerful resource - one that allows models to interact seamlessly with app functionality. However, this power must be used responsibly. Consider the following usage scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AI model deletes user data in response to an ambiguous request.&lt;/li&gt;
&lt;li&gt;Unmanaged API calls generate unexpected costs or trigger external processes.&lt;/li&gt;
&lt;li&gt;Functions modify application state or UI without the user&amp;#39;s knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The technique described in this post strikes a balance between convenience and control: it preserves the benefits of automated tool calling and gives users explicit authority over sensitive or irreversible operations.&lt;/p&gt;

&lt;h2 id="getting-started"&gt;Before You Start&lt;/h2&gt;
&lt;p&gt;Create and configure the DevExpress Blazor Chat (&amp;nbsp;&lt;code&gt;DxAIChat&lt;/code&gt;&amp;nbsp;) component in your Blazor application. For setup instructions, review the following: Blazor Chat&amp;nbsp;&lt;a href="https://docs.devexpress.com/Blazor/DevExpress.AIIntegration.Blazor.Chat.DxAIChat#add-an-ai-chat-to-a-project" target="_blank"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This example targets Azure OpenAI, but the solution is compatible with any AI service that implements the &lt;code&gt;IChatClient&lt;/code&gt; interface from the &amp;quot;Microsoft.Extensions.AI&amp;quot; library.&lt;/p&gt;

&lt;p&gt;The following code in &lt;i&gt;Program.cs&lt;/i&gt; defines the basic configuration:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;

var builder = WebApplication.CreateBuilder(args);

// Replace with your endpoint, API key, and model&amp;#39;s deployment name
string azureOpenAIEndpoint = Environment.GetEnvironmentVariable(&amp;quot;AZURE_OPENAI_ENDPOINT&amp;quot;);
string azureOpenAIKey = Environment.GetEnvironmentVariable(&amp;quot;AZURE_OPENAI_API_KEY&amp;quot;);
string deploymentName = &amp;quot;your-model-deployment-name&amp;quot;;

var azureChatClient = new AzureOpenAIClient(
    new Uri(azureOpenAIEndpoint),
    new AzureKeyCredential(azureOpenAIKey))
    .GetChatClient(deploymentName)
    .AsIChatClient();

builder.Services.AddDevExpressBlazor();
builder.Services.AddDevExpressAI();
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="architecture-overview"&gt;Architecture Overview&lt;/h2&gt;

&lt;ul&gt;
    &lt;li&gt;
        &lt;p&gt;
            &lt;strong&gt;&lt;code&gt;IToolCallFilter&lt;/code&gt;&lt;/strong&gt;
            &lt;br&gt;
            Manages AI tool calls before they are executed.
        &lt;/p&gt;
    &lt;/li&gt;
    
    &lt;li&gt;
        &lt;p&gt;
        	&lt;strong&gt;&lt;code&gt;CustomFunctionInvokingChatClient&lt;/code&gt;&lt;/strong&gt;
        	&lt;br&gt;
        	Extends the default behavior used to invokes tools.
    	&lt;/p&gt;
    &lt;/li&gt;
    
    &lt;li&gt;
    	&lt;p&gt;
            &lt;strong&gt;&lt;code&gt;CustomFunctionInvokingChatClientExtensions&lt;/code&gt;&lt;/strong&gt;
            &lt;br&gt;
            
        The Fluent API allows you to leverage tool call confirmation with a single line of configuration when building the chat client.&lt;/p&gt;
    &lt;/li&gt;
    
    &lt;li&gt;
        &lt;p&gt;
            &lt;strong&gt;Confirmation UI&lt;/strong&gt;
            &lt;br&gt;
            Displays the dialog used to confirm tool calls.
        &lt;/p&gt;
    &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="manage-tool-calls"&gt;Manage Tool Calls (IToolCallFilter)&lt;/h2&gt;

&lt;p&gt;The heart of our confirmation system is the &lt;code&gt;IToolCallFilter&lt;/code&gt; interface - an interface which defines how the chat intercepts and manages function calls.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
public interface IToolCallFilter {
    public event Action&amp;lt;FunctionInvocationContext, TaskCompletionSource&amp;lt;bool&amp;gt;&amp;gt; ToolCalled;

    Task&amp;lt;bool&amp;gt; InvokeFunctionFilter(FunctionInvocationContext context);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;MyToolCallFilter&lt;/code&gt; manages AI-triggered actions and executes&amp;nbsp;sensitive operations only when&amp;nbsp;approved. The API design allows the filter to pause function execution, await user input, and continue or cancel based on the user&amp;#39;s decision. A &lt;code&gt;TaskCompletionSource&amp;lt;bool&amp;gt;&lt;/code&gt; instance enables asynchronous communication between the UI and filter logic.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
public class MyToolCallFilter : IToolCallFilter {
    public event Action&amp;lt;FunctionInvocationContext, TaskCompletionSource&amp;lt;bool&amp;gt;&amp;gt; ToolCalled;

    public Task&amp;lt;bool&amp;gt; InvokeFunctionFilter(FunctionInvocationContext context) {
        if (ToolCalled is null)
            return Task.FromResult(true);

        var tcs = new TaskCompletionSource&amp;lt;bool&amp;gt;();
        ToolCalled.Invoke(context, tcs);
        return tcs.Task;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="chat-client-confirm-tool-calls"&gt;Confirm Tool Calls (CustomFunctionInvokingChatClient)&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;CustomFunctionInvokingChatClient&lt;/code&gt; class extends our Blazor AI Chat control&amp;#39;s default behavior:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;
        Intercepts AI tool calls (defines a custom &lt;code&gt;FunctionInvoker&lt;/code&gt; delegate).
    &lt;/li&gt;
    &lt;li&gt;
    	Checks for confirmation logic (whether the filter has been registered in the service collection).
    &lt;/li&gt;
    &lt;li&gt;
    	Proceeds with user approval. If the user cancels the request, the &lt;code&gt;CustomFunctionInvoker&lt;/code&gt;&amp;nbsp;method call returns a cancellation message instead of executing the tool. The message tells the LLM that the operation was aborted and that the requested information is unavailable.
    &lt;/li&gt;
&lt;/ul&gt;
    
&lt;pre&gt;&lt;code class="language-csharp"&gt;
public class CustomFunctionInvokingChatClient : FunctionInvokingChatClient {
    public CustomFunctionInvokingChatClient(IChatClient innerClient, ILoggerFactory? factory = null,
        IServiceProvider? services = null)
        : base(innerClient, factory, services) {
        if(services == null) {
            throw new ArgumentNullException(nameof(services), &amp;quot;Service provider cannot be null.&amp;quot;);
        }
        FunctionInvoker = CustomFunctionInvoker;
    }

    private async ValueTask&amp;lt;object?&amp;gt; CustomFunctionInvoker(FunctionInvocationContext context,
        CancellationToken cancellationToken) {
        IToolCallFilter? filter = FunctionInvocationServices!.GetService&amp;lt;IToolCallFilter&amp;gt;();

        if(await (filter?.InvokeFunctionFilter(context) ?? Task.FromResult(true))) {
            return await context.Function.InvokeAsync(context.Arguments, cancellationToken);
        }

        return &amp;quot;The tool call was cancelled by the user. Do not attempt to invoke this tool again. Return a message indicating that the call was cancelled and that the weather information is unavailable at this time.&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="create-confirmation-ui"&gt;Create the Confirmation UI&lt;/h2&gt;

&lt;p&gt;The confirmation dialog displays details about the pending tool call, including tool name, description, and arguments. A user can verify the tool call and ensure that retrieved arguments match the request. &lt;strong&gt;Confirm&lt;/strong&gt; and &lt;strong&gt;Cancel&lt;/strong&gt; buttons allow the user to approve or cancel the operation.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-html"&gt;
@if(_pendingTcs != null) {
    &amp;lt;div&amp;gt;
        @if(_pendingContext != null) {
            &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Please confirm the tool call.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
            &amp;lt;blockquote&amp;gt;
                &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Tool Called:&amp;lt;/strong&amp;gt; @_pendingContext.Function.Name&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Description:&amp;lt;/strong&amp;gt; @_pendingContext.Function.Description&amp;lt;/p&amp;gt;
            &amp;lt;/blockquote&amp;gt;
            &amp;lt;blockquote&amp;gt;
                &amp;lt;strong&amp;gt;Arguments:&amp;lt;/strong&amp;gt;
                &amp;lt;ul&amp;gt;
                    @foreach(var arg in _pendingContext.Arguments) {
                        &amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;@arg.Key&amp;lt;/strong&amp;gt;: @arg.Value&amp;lt;/li&amp;gt;
                    }
                &amp;lt;/ul&amp;gt;
            &amp;lt;/blockquote&amp;gt;
        }

        &amp;lt;DxButton Text=&amp;quot;Confirm&amp;quot;
                  RenderStyle=&amp;quot;ButtonRenderStyle.Success&amp;quot;
                  IconCssClass=&amp;quot;oi oi-check&amp;quot;
                  Click=&amp;quot;() =&amp;gt; OnDecisionMade(true)&amp;quot; /&amp;gt;
        &amp;lt;DxButton Text=&amp;quot;Cancel&amp;quot;
                  RenderStyle=&amp;quot;ButtonRenderStyle.Secondary&amp;quot;
                  IconCssClass=&amp;quot;oi oi-x&amp;quot;
                  Click=&amp;quot;() =&amp;gt; OnDecisionMade(false)&amp;quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The following code implements confirmation workflow. The &lt;code&gt;ConfirmationButtons&lt;/code&gt; component subscribes to the &lt;code&gt;ToolCalled&lt;/code&gt; event exposed by &lt;code&gt;IToolCallFilter&lt;/code&gt;. When AI Chat attempts to invoke a tool, the filter fires this event and passes in a &lt;code&gt;FunctionInvocationContext&lt;/code&gt; and a &lt;code&gt;TaskCompletionSource&amp;lt;bool&amp;gt;&lt;/code&gt; that awaits the user&amp;#39;s decision.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
@code {
    private FunctionInvocationContext? _pendingContext;
    private TaskCompletionSource&amp;lt;bool&amp;gt;? _pendingTcs;
    [Inject] IToolCallFilter? ToolCallFilter { get; set; }

    protected override void OnInitialized() {
        if(ToolCallFilter != null) {
            ToolCallFilter.ToolCalled += OnFunctionInvoked;
        }
    }

    private void OnFunctionInvoked(FunctionInvocationContext context, TaskCompletionSource&amp;lt;bool&amp;gt; tcs) {
        _pendingContext = context;
        _pendingTcs = tcs;
        StateHasChanged();
    }

    private void OnDecisionMade(bool decision) {
        _pendingTcs!.SetResult(decision);
        _pendingContext = null;
        _pendingTcs = null;
    }

    public void Dispose() {
        if(ToolCallFilter != null) {
            ToolCallFilter.ToolCalled -= OnFunctionInvoked;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="integration-with-the-blazor-ai-chat-component"&gt;Integrate Confirmation UI with Blazor AI Chat&lt;/h2&gt;

&lt;p&gt;The chat displays the confirmation dialog whenever the LLM is about to execute a tool. The &lt;code&gt;MessageContentTemplate&lt;/code&gt; renders the confirmation dialog while the chat is in a &amp;quot;typing&amp;quot; state (indicating that the tool call is being processed).&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-html"&gt;
&amp;lt;DxAIChat CssClass=&amp;quot;main-content&amp;quot;&amp;gt;
    &amp;lt;MessageContentTemplate Context=&amp;quot;context&amp;quot;&amp;gt;
        @context.Content
        @if(context.Typing) {
            &amp;lt;ConfirmationButtons /&amp;gt;
        }
    &amp;lt;/MessageContentTemplate&amp;gt;
&amp;lt;/DxAIChat&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="service-registration"&gt;Register Services&lt;/h2&gt;

&lt;p&gt;In &lt;i&gt;Program.cs&lt;/i&gt;, register &lt;code&gt;MyToolCallFilter&lt;/code&gt; and &lt;code&gt;IChatClient&lt;/code&gt; within the dependency injection (DI) container for each user session:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
// Register the tool call filter
builder.Services.AddScoped&amp;lt;IToolCallFilter, MyToolCallFilter&amp;gt;();

// Configure the chat client with the confirmation layer
builder.Services.AddScoped(x =&amp;gt; {
    return new ChatClientBuilder(azureChatClient)
        .ConfigureOptions(x =&amp;gt; {
            x.Tools = [CustomAIFunctions.GetWeatherTool];
        })
        .UseMyToolCallConfirmation()
        .Build(x);
});

builder.Services.AddDevExpressAI();
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="fluent-api-extension"&gt;Fluent API Extension&lt;/h2&gt;

&lt;p&gt;A fluent API extension allows you to activate tool call confirmation with a single method call in your chat client configuration:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
public static class CustomFunctionInvokingChatClientExtensions {
    public static ChatClientBuilder UseMyToolCallConfirmation(this ChatClientBuilder builder,
        ILoggerFactory? loggerFactory = null) {
        return builder.Use((innerClient, services) =&amp;gt; {
            loggerFactory ??= services.GetService&amp;lt;ILoggerFactory&amp;gt;();
            return new CustomFunctionInvokingChatClient(innerClient, loggerFactory, services);
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="user-experience-in-action"&gt;User Experience in Action&lt;/h2&gt;

&lt;p&gt;When a user requests weather information, the following sequence occurs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The LLM identifies that a function call is required.&lt;/li&gt;
&lt;li&gt;A confirmation dialog is displayed (instead of executing immediately).&lt;/li&gt;
&lt;li&gt;The confirmation dialog displays tool name, description, and retrieved arguments.&lt;/li&gt;
&lt;li&gt;The user can approve or cancel the operation&lt;/li&gt;
&lt;li&gt;Only approved tools are executed (their results are returned to the LLM).&lt;/li&gt;
&lt;/ol&gt;



&lt;video controls=""&gt;
    &lt;source src="https://community.devexpress.com/blogs/aspnet/25.2/ai-tool-call-confirm/blazor-ai-chat-tool-calling-confirmation_square.mp4" type="video/mp4"&gt;
    Your browser does not support the video tag.
&lt;/video&gt;

&lt;h2 id="advanced-customization-options"&gt;Advanced Customization Options&lt;/h2&gt;

&lt;h3 id="selective-tool-call-confirmation"&gt;Selective Tool Call Confirmation&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;SelectiveToolCallFilter&lt;/code&gt; extends &lt;code&gt;IToolCallFilter&lt;/code&gt; to require user confirmation before calling tools designed to execute&amp;nbsp;sensitive operations (such as deleting data, sending emails, or processing payments):&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
public class SelectiveToolCallFilter : IToolCallFilter {
    private readonly string[] _sensitiveOperations = { &amp;quot;DeleteData&amp;quot;, &amp;quot;SendEmail&amp;quot;, &amp;quot;ChargePayment&amp;quot; };

    public Task&amp;lt;bool&amp;gt; InvokeFunctionFilter(FunctionInvocationContext context) {
        // Only require confirmation for sensitive operations
        if (!_sensitiveOperations.Contains(context.Function.Name)) {
            return Task.FromResult(true);
        }

        // Show confirmation for sensitive operations
        if (ToolCalled is null)
            return Task.FromResult(true);

        var tcs = new TaskCompletionSource&amp;lt;bool&amp;gt;();
        ToolCalled.Invoke(context, tcs);
        return tcs.Task;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="enhanced-ui-styling"&gt;Enhanced Confirmation UI&lt;/h3&gt;
&lt;p&gt;
    Modify the &lt;i&gt;ConfirmationButtons.razor&lt;/i&gt; component to create a confirmation dialog based on UI requirements. You can modify the layout, apply custom styles, and inform users about the tool they are about to execute.
&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
&amp;lt;div class=&amp;quot;tool-confirmation-container&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;confirmation-header&amp;quot;&amp;gt;
        &amp;lt;h5&amp;gt;⚠️ Function Execution Request&amp;lt;/h5&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;function-details&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;detail-row&amp;quot;&amp;gt;
            &amp;lt;span class=&amp;quot;label&amp;quot;&amp;gt;Function:&amp;lt;/span&amp;gt;
            &amp;lt;span class=&amp;quot;value&amp;quot;&amp;gt;@_pendingContext.Function.Name&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;detail-row&amp;quot;&amp;gt;
            &amp;lt;span class=&amp;quot;label&amp;quot;&amp;gt;Description:&amp;lt;/span&amp;gt;
            &amp;lt;span class=&amp;quot;value&amp;quot;&amp;gt;@_pendingContext.Function.Description&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;arguments-section&amp;quot;&amp;gt;
        &amp;lt;h6&amp;gt;Arguments:&amp;lt;/h6&amp;gt;
        @foreach(var arg in _pendingContext.Arguments) {
            &amp;lt;div class=&amp;quot;argument-item&amp;quot;&amp;gt;
                &amp;lt;strong&amp;gt;@arg.Key:&amp;lt;/strong&amp;gt; @arg.Value
            &amp;lt;/div&amp;gt;
        }
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;action-buttons&amp;quot;&amp;gt;
        &amp;lt;DxButton Text=&amp;quot;Approve&amp;quot;
                  RenderStyle=&amp;quot;ButtonRenderStyle.Success&amp;quot;
                  Click=&amp;quot;() =&amp;gt; OnDecisionMade(true)&amp;quot; /&amp;gt;
        &amp;lt;DxButton Text=&amp;quot;Deny&amp;quot;
                  RenderStyle=&amp;quot;ButtonRenderStyle.Danger&amp;quot;
                  Click=&amp;quot;() =&amp;gt; OnDecisionMade(false)&amp;quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="enhanced-ui-styling"&gt;Audit Trail&lt;/h3&gt;

&lt;p&gt;Log user decisions with relevant details (such as tool name, function arguments, timestamp, and user role):&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-csharp"&gt;
public class AuditingToolCallFilter : IToolCallFilter {
    private readonly ILogger&amp;lt;AuditingToolCallFilter&amp;gt; _logger;
 
    public AuditingToolCallFilter(ILogger&amp;lt;AuditingToolCallFilter&amp;gt; logger) {
        _logger = logger;
    }

    public event Action&amp;lt;FunctionInvocationContext, TaskCompletionSource&amp;lt;bool&amp;gt;&amp;gt;? ToolCalled;
    public Task&amp;lt;bool&amp;gt; InvokeFunctionFilter(FunctionInvocationContext context)
    {
        if (ToolCalled is null)
            return Task.FromResult(true);

        var tcs = new TaskCompletionSource&amp;lt;bool&amp;gt;();
        
        // Subscribe to the task completion to log the decision
        tcs.Task.ContinueWith(task =&amp;gt; {
            if (task.IsCompletedSuccessfully)
            {
                OnDecisionMade(task.Result, context);
            }
        }, TaskContinuationOptions.ExecuteSynchronously);

        ToolCalled.Invoke(context, tcs);
        return tcs.Task;
    }
 
    private void OnDecisionMade(bool decision, FunctionInvocationContext context) {
        _logger.LogInformation(&amp;quot;User {Decision} function call: {FunctionName} with args: {Arguments}&amp;quot;,
            decision ? &amp;quot;approved&amp;quot; : &amp;quot;denied&amp;quot;,
            context.Function.Name,
            string.Join(&amp;quot;, &amp;quot;, context.Arguments.Select(a =&amp;gt; $&amp;quot;{a.Key}={a.Value}&amp;quot;)));
 
        // Send decision data to the analytics service
        // TrackUserDecision(decision, context);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="security-considerations"&gt;Security Considerations&lt;/h2&gt;

&lt;p&gt;The confirmation UI enhances function calling transparency and user control. To strengthen security alongside the confirmation UI, implement the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Restrict the number of tool calls per user or session.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permission Checks&lt;/strong&gt;: Verify user permissions before displaying the confirmation dialog.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Function Validation&lt;/strong&gt;: Validate function arguments before execution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit Trails&lt;/strong&gt;: Log all tool calls and user decisions for monitoring and compliance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeout Handling&lt;/strong&gt;: Automatically deny tool execution if the user does not respond within a predefined period of time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="download-the-complete-example"&gt;Download the Example&lt;/h2&gt;

&lt;p&gt;The full source code for this implementation is available on &lt;a target="_blank" href="https://github.com/DevExpress-Examples/blazor-ai-chat-confirm-tool-calls"&gt;GitHub&lt;/a&gt;. Clone the repo and follow the instructions to setup your tool call confirmation system.&lt;/p&gt;

&lt;h2 id="your-feedback-counts-"&gt;Your Feedback Counts!&lt;/h2&gt;
&lt;p&gt;This tool call confirmation system demonstrates one approach to balancing AI automation with user control. We&amp;#39;re considering including similar functionality as a built-in feature in future versions of the DevExpress Blazor AI Chat component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In the meantime, we&amp;#39;d love to hear your thoughts on this implementation:&lt;/strong&gt;&lt;/p&gt;&lt;div data-survey-id="9c4ba6e3-97da-497c-9690-ac353fee6630" data-survey-auth-required="false"&gt;&lt;/div&gt;</description>
      <pubDate>Mon, 18 Aug 2025 01:35:00 Z</pubDate>
      <dc:creator>Dmitry Tokmachev (DevExpress)</dc:creator>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388256</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2025/08/13/blazor-year-end-roadmap-v25-2.aspx</link>
      <category domain="https://community.devexpress.com/Tags/2025">2025</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Featured">Featured</category>
      <category domain="https://community.devexpress.com/Tags/Roadmap">Roadmap</category>
      <category domain="https://community.devexpress.com/Tags/Survey">Survey</category>
      <category domain="https://community.devexpress.com/Tags/v25.2">v25.2</category>
      <title>Blazor — Year-End Roadmap (v25.2)</title>
      <description>&lt;div class="Note"&gt;The information contained within this blog post details our current/projected development plans. Please note that this information is being shared for INFORMATIONAL PURPOSES ONLY and does not represent a binding commitment on the part of Developer Express Inc. This blog post and the features/products listed within it are subject to change. You should not rely or use this information to help make a purchase decision about Developer Express Inc products.&lt;/div&gt;

&lt;h2 id="live-roadmap-updates"&gt;Roadmap Updates&lt;/h2&gt;
&lt;p&gt;Our year-end Blazor roadmap will be updated regularly to reflect development progress. As we get closer to the release, we expect to add additional&amp;nbsp;items and modify the status of planned features. Look for the following labels next to every feature:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="not-started"&gt;Planned&lt;/span&gt; — Feature is scheduled but not yet in development.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="development"&gt;In Development&lt;/span&gt; — Under active development.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in EAP v25.2&lt;/span&gt; — Will be available in our first v25.2 early access preview.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="completed"&gt;Coming in v25.2&lt;/span&gt; — Will be included in the final v25.2 release.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt; — Fully implemented and available.&lt;/p&gt;&lt;/li&gt;
    
&lt;/ul&gt;
&lt;h2 id="-net-10-support"&gt;.NET 10 Support&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;DevExpress Blazor UI components will support .NET 10 following Microsoft’s official release. Preview builds will be available for early adopters.&lt;/p&gt;
&lt;h2 id="content-security-policy-csp-enhancements"&gt;Content Security Policy (CSP) Enhancements&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to enhance Content Security Policy (CSP) support across our Blazor component suite. DevExpress Blazor components will no longer require the &lt;code&gt;unsafe-inline&lt;/code&gt; style source.&lt;/p&gt;
&lt;h2 id="accessibility-enhancements"&gt;Accessibility Enhancements&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Improvements/updates for&amp;nbsp;Data Editors&lt;/li&gt;
&lt;li&gt;Screen reader support for the Rich Text Editor&lt;/li&gt;
&lt;li&gt;Better keyboard support for Popup, DropDown, and Window components&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;DevExpress MCP Server for AI-Powered Documentation Access&lt;/h2&gt;
&lt;p class="tags"&gt;
    &lt;span class="completed"&gt;&lt;a target="_blank" href="https://docs.devexpress.com/GeneralInformation/405551/help-resources/dev-express-documentation-mcp-server-configure-an-ai-powered-assistant"&gt;Available in Preview&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;We will introduce an MCP server that connects GitHub Copilot Chat, Cursor, and other MCP-compatible AI tools directly to our comprehensive documentation database. The server will provide instant access to over 300,000 help topics through natural language queries within your IDE. This will allow you and AI coding agents such as Claude Code to access current DevExpress documentation directly within the AI assistant&amp;#39;s context.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/winforms/20251008-Win-Roadmap-252/dx-mcp-docs-assistant.png" alt="" style="width:862px;height:391px;"&gt;
&lt;h2 id="focus-on-performance"&gt;A Focus on Performance&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We recently introduced significant performance improvements across our Blazor component suite (see &lt;a href="https://www.devexpress.com/subscriptions/whats-new/#blazor-performance-enhancements"&gt;v25.1 release notes&lt;/a&gt; for additional information). In v25.2, we will extend our performance optimizations with new&amp;nbsp;enhancements. We will update this post with additional information as we get closer to release.&amp;nbsp;&lt;/p&gt;
&lt;h3 id="grid-treelist-filter-menu-faster-dropdown-opening"&gt;Blazor Grid &amp;amp; TreeList Filter Menu — Improved Dropdown Responsiveness&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will&amp;nbsp;optimize rendering speed and improve responsiveness for column filter menus used within our Blazor Grid and TreeList components.&lt;/p&gt;
&lt;h2 id="blazor-project-templates-new-sample-views"&gt;Blazor Project Templates — New Sample Views&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The primary Blazor template in our Template Kit will be able to add sample views for complex component libraries (Blazor Charts, Reporting, Scheduler, and AI Chat). &lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Project%20Templates%20—%20New%20Sample%20Views.png" alt="New Sample Views"&gt;
&lt;h2 id="new-blazor-smart-autocomplete"&gt;New Blazor Smart Autocomplete&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We expect to introduce Smart Autocomplete, a new AI-powered Extension designed to accelerate text input. Smart Autocomplete connects to our Blazor Memo and displays gray inline suggestions based on current input. Suggestions appear seamlessly within the Blazor Memo Editor without interrupting typing flow. Users can accept a suggestion or simply continue typing to dismiss it.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20AI%20Autocomplete.gif.gif" class="small" style="width:542px;height:426px;" alt="AI Smart Autocomplete"&gt;
&lt;h2 id="blazor-ai-chat"&gt;Blazor AI Chat&lt;/h2&gt;
&lt;h3 id="tool-api"&gt;Tool API&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;New APIs will allow you to create tools that execute any custom command (apply filters to Grids, modify Chart UI, query databases, or perform business actions). With these tools, users will be able to interact with your DevExpress-powered application directly from AI Chat using natural language.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[AIIntegrationTool(&amp;quot;DxGrid_SetGridFilter&amp;quot;)]
[Description(&amp;quot;Applies a filter to the specified DxGrid component using a DevExpress Criteria Language filter string. This changes the UI to display only rows matching the given criteria.&amp;quot;)]
public static object SetGridFilter(
        [Description(&amp;quot;The DxGrid component to apply the filter to.&amp;quot;)]
        DxGrid grid,
        [Description(&amp;quot;The filter criteria string in DevExpress Criteria Language Syntax. Example: \&amp;quot;[Status] = &amp;#39;Active&amp;#39; AND [HireDate] &amp;gt; #2020-01-01#\&amp;quot;.&amp;quot;)]
        string filter) {
    grid.SetFilterCriteria(CriteriaOperator.Parse(filter ?? string.Empty));
    int visibleRowCount = grid.GetVisibleRowCount();
    return new { success = true, visibleRowCount };
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="tool-call-visualization"&gt;Tool Call Visualization&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The DevExpress Blazor AI Chat control will clearly indicate when it executes tools (declared locally within the app or available via MCP servers). This will increase clarity and transparency and will help users understand what actions are being performed.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20AI%20Chat%20–%20Tool%20Call%20Visualization.png" alt="Tool Call Visualization"&gt;
&lt;div class="Note"&gt;
    &lt;p&gt;Learn more about this feature and take part in our survey:&amp;nbsp;&lt;a href="https://community.devexpress.com/Blogs/aspnet/archive/2025/08/18/devexpress-blazor-ai-chat-tool-call-confirmation.aspx" target="_blank"&gt;DevExpress Blazor AI Chat — Tool Call Confirmation&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id="resources"&gt;Resources&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will introduce a resources feature for our Blazor AI Chat control, allowing users to attach additional context to their chat messages. Unlike file attachments, resources can include files or other types of context supplied by the application (binary files including audio, images and PDFs, database schemas, records, logs, etc). Users will be able to view available resources in a dedicated UI element and attach them to their chat context.&lt;/p&gt;
&lt;h3 id="resizing-for-input-area"&gt;Resizing for Input Area&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Users will be able to resize the chat input area to fit large prompt messages.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20AI%20Chat%20–%20Resizing%20for%20Input%20Area.png" alt="Resizing for Input Area"&gt;
&lt;h2 id="blazor-grid-treelist"&gt;Blazor Grid &amp;amp; TreeList&lt;/h2&gt;
&lt;h3 id="column-virtualization"&gt;Column Virtualization&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As you may know, our Blazor Grid and TreeList support row virtualization. In our next major release (v25.2), we plan to extend this capability and support column virtualization. With this feature enabled, the DevExpress Grid and TreeList will only render columns within the current viewport. This will reduce DOM size and ensure fast rendering even for tables with hundreds of columns.&lt;/p&gt;
&lt;h3 id="integrated-filter-builder"&gt;Integrated Filter Builder&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In our year-end release, we plan to integrate our Filter Builder control directly into the Blazor Grid and TreeList. This integration will reduce the amount of code required to configure filter fields and streamline the process associated with complex filter conditions.&lt;/p&gt;
&lt;p&gt;We also expect to synchronize the Filter Builder, Filter Row, and Filter Menus within the same component. Users will be able to view/modify all active filters from a single, centralized UI.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Grid%20&amp;amp;%20TreeList%20–%20Integrated%20Filter%20Builder.png" alt="Integrated Filter Builder"&gt;
&lt;h3 id="command-column-icons"&gt;Command Column Icons&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With v25.2, the DevExpress Blazor&amp;nbsp;Grid and TreeList will display built-in icons for command actions by default. This change will help reduce column width and provide a cleaner, more modern appearance.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Grid%20&amp;amp;%20TreeList%20–%20New%20Built-in%20Command%20Column%20Action%20Buttons.png" alt="New Built-in Command Column Action Buttons"&gt;
&lt;h3 id="vertical-grid-line-visibility"&gt;Vertical Grid Line Visibility&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;Postponed to v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will add an option to hide column lines (vertical separators between adjacent columns) in our Blazor Grid and TreeList components.&lt;/p&gt;
&lt;p&gt;This change is part of our ongoing effort to deliver a cleaner and more modern UI.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Grid%20&amp;amp;%20TreeList%20–%20Column%20Lines%20Visibility%20Customization.png" alt="Column Lines Visibility Customization"&gt;
&lt;h2 id="blazor-grid"&gt;Blazor Grid&lt;/h2&gt;
&lt;h3 id="context-menu"&gt;Context Menu&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to introduce a built-in context menu for the Blazor Grid component. Our context menu will include a predefined set of actions for each Grid region and allow you to define/customize items. Supported regions will include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Row&lt;/li&gt;
&lt;li&gt;Group Row&lt;/li&gt;
&lt;li&gt;Column&lt;/li&gt;
&lt;li&gt;Footer&lt;/li&gt;
&lt;li&gt;Group panel&lt;/li&gt;
&lt;li&gt;Group footer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also plan to include a set of built-in actions for the column region to simplify common operations.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Grid%20–%20Context%20Menu.png" alt="Context Menu"&gt;
&lt;h2 id="blazor-data-editors"&gt;Blazor Data Editors&lt;/h2&gt;
&lt;h3 id="blazor-tagbox-keyboard-navigation-enhancements"&gt;Blazor TagBox — Keyboard Navigation Enhancements&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Users will be able to focus individual tags, making it easier to manage tag collections using only the keyboard (e.g., to delete a specific tag).&lt;/p&gt;
&lt;h3 id="date-edit-time-edit-time-selection-ui-redesign"&gt;Date Edit &amp;amp; Time Edit — Time Selection UI Redesign&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will rework the popup UI for our Date Edit and Time Edit components to improve usability and make the Accept/Cancel buttons more visible.&lt;/p&gt;
&lt;p&gt;As part of this update, the Clear button will also be removed from the popup and will only remain in the edit box. To clear the editor value, users will be able to execute the new Alt+Del shortcut (like our ComboBox editor).&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Data%20Editors%20–%20Date%20Edit%20&amp;amp;%20Time%20Edit%20—%20Time%20Selection%20UI%20Redesign.png" alt="Time Selection UI Redesign"&gt;
&lt;h3 id="blazor-memo-adaptive-height-support"&gt;Blazor Memo — Adaptive Height Support&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Our Blazor Memo will be able to auto-adjust height based on content (to display text without scrolling).&lt;/p&gt;

&lt;h2 id="filter-builder-official-release"&gt;Filter Builder — Official Release&lt;/h2&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The Filter Builder component (introduced as a CTP in v25.1) will be ready for production use in v25.2. Planned new features/enhancements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keyboard navigation and accessibility (a11y) support&lt;/li&gt;
&lt;li&gt;Improved localization&lt;/li&gt;

&lt;li&gt;Direct integration with the Grid, eliminating the need to configure the Filter Builder separately&lt;/li&gt;
&lt;li&gt;Expanded customization API with support for additional templates and configurable filter operators&lt;/li&gt;
&lt;li&gt;Enhanced performance for large data objects&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="blazor-pivot-table-official-release"&gt;Blazor Pivot Table — Official Release&lt;/h2&gt;
&lt;p&gt;In v25.1, we shipped a number of essential Pivot Table-related features, including API and UI-based filtering, field reordering, persistent layout support, and a built-in field list.&lt;/p&gt;
&lt;p&gt;We initially planned to announce the official release in v25.1. However, two key features — column virtualization and keyboard navigation, were not yet ready for production use, so we decided to postpone the official release announcement.&lt;/p&gt;
&lt;p&gt;With v25.2, we expect to officially ship the DevExpress Blazor Pivot Table control. We will conduct additional testing and apply final polish to ensure the component meets production-ready standards.&lt;/p&gt;
&lt;h3 id="keyboard-navigation"&gt;Keyboard Navigation&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Users will be able to access every UI element in the DevExpress Blazor Pivot Table via the&amp;nbsp;keyboard. In addition, we plan to add keyboard shortcuts for the following actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sorting&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;Expanding/collapsing grouped values&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="column-virtualization"&gt;Column Virtualization&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To ensure optimal performance with large datasets, our Blazor Pivot Table already supports row virtualization. In v25.2, we plan to extend this functionality by adding column virtualization.&lt;/p&gt;
&lt;p&gt;This feature is essential when the Pivot Table displays a large&amp;nbsp;number of columns — for example, when displaying one column per day across an entire year. Column virtualization will allow the component to render visible columns, significantly reducing DOM size and improving responsiveness.&lt;/p&gt;
&lt;p&gt;As part of this effort, we will perform extensive internal testing to verify performance and stability in high density scenarios. Our goal is to ensure reliable operation when working with horizontally large datasets.&lt;/p&gt;
&lt;h2 id="blazor-ribbon"&gt;Blazor Ribbon&lt;/h2&gt;
&lt;h3 id="official-release"&gt;Official Release&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The single-line Ribbon component, previously available as a CTP, will be officially released and ready for production use in v25.2. Planned enhancements include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual improvements, including a redesigned overflow menu with item groups, scrolling support, and a more polished appearance&lt;/li&gt;
&lt;li&gt;Extended APIs for Ribbon items&lt;/li&gt;
&lt;li&gt;Keyboard navigation and accessibility (a11y) support&lt;/li&gt;
&lt;li&gt;Reworked adaptivity logic for better responsiveness&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="rich-text-editor-and-html-editor-integration"&gt;Rich Text Editor and HTML Editor Integration&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Our Rich Text Editor and HTML Editor components will use the new Ribbon internally and will benefit from its enhanced visuals, reworked adaptivity, and extensive customization options.&lt;/p&gt;
&lt;h2 id="blazor-scheduler"&gt;Blazor Scheduler&lt;/h2&gt;
&lt;h3 id="timeline-view-more-compact-cell-height"&gt;Timeline View — More Compact Cell Height&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Currently, cell height in the Scheduler Timeline view cannot be less than 150 pixels. In v25.2, we plan to remove minimum cell height restrictions and calculate it based on appointment size. This change will help you to create more compact interfaces and maximize screen real-estate.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Scheduler%20–%20Timeline%20View%20—%20More%20Compact%20Cell%20Height.png" alt="More Compact Cell Height"&gt;
&lt;h3 id="ui-ux-enhancements"&gt;UI/UX Enhancements&lt;/h3&gt;
&lt;h4 id="time-cells-appointment-hover-effect"&gt;Time Cells &amp;amp; Appointment Hover Effect&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;p&gt;In our year-end release, we expect to introduce a new hover effect for both empty cells and appointments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For an empty area, the hover effect will suggest to create a new appointment&lt;/li&gt;
&lt;li&gt;For existing appointments, the hover effect will signal that the item can be edited or resized.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The hover effect will improve perceived responsiveness by indicating that a hovered element is interactive. It will not appear if appointment creation or editing is disabled.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Empty%20Cells%20and%20Appointment%20Hover%20Effect.gif" alt="Empty Cells and Appointment Hover Effect"&gt;
&lt;h4 id="appointment-tooltip-edit-form-layout-changes"&gt;Appointment Tooltip &amp;amp; Edit Form Layout Changes&lt;/h4&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.1.4&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We plan to introduce minor layout refinements to the Scheduler’s tooltip and edit form. These changes will improve visual structure, reduce unnecessary spacing, and contribute to a cleaner, more consistent UI.&lt;/p&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.2/Roadmap/Blazor%20Scheduler%20–%20Appoitment’s%20Tooltip%20&amp;amp;%20Edit%20Form%20Layout%20Changes%20-%20After.png" alt="Appoitment’s Tooltip &amp;amp; Edit Form Layout Changes"&gt;
&lt;h2 id="blazor-themes"&gt;Blazor Themes&lt;/h2&gt;
&lt;h3 id="theme-api-current-theme"&gt;Theme API — Current Theme&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="development"&gt;Postponed to v26.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will extend our theme APIs to allow developers to check which theme is currently applied.&lt;/p&gt;
&lt;h3 id="fluent-bootstrap-styles"&gt;Fluent — Bootstrap Styles&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.1.4&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Fluent themes will include optional Bootstrap styles for the default color accent (blue) in both light and dark modes. You will be able to include them as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;@DxResourceManager.RegisterTheme(Themes.Fluent.Clone(properties =&amp;gt; {
    properties.UseBootstrapStyles = true;
}))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For styles dependent on Fluent accent colors, we recommend using DevExpress CSS variables instead. Due to Bootstrap&amp;#39;s CSS architecture, Bootstrap styles will not reflect your accent colors, so use them only for non-color customizations.&lt;/p&gt;
&lt;h3 id="fluent-public-css-variables"&gt;Fluent — Public CSS Variables&lt;/h3&gt;
&lt;p class="tags"&gt;&lt;span class="completed"&gt;Released in v25.2&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will document global CSS variables available in Fluent. This documentation will help you customize Fluent themes or simply reference global variables in your own CSS.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-css"&gt;.my-card {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow-wrap: break-word;
    background-color: var(--DS-color-surface-neutral-default-rest);
    background-clip: border-box;
    border: var(--DS-border-radius-10) solid var(--DS-color-border-neutral-default-rest);
    border-radius: 0.25rem;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="blazor-reporting"&gt;Blazor Reporting&lt;/h2&gt;
&lt;p&gt;Learn more:&amp;nbsp;&lt;a href="https://community.devexpress.com/Blogs/reporting/archive/2025/08/13/devexpress-reports-amp-bi-dashboard-november-2025-roadmap-v25-2.aspx" target="_blank"&gt;DevExpress Reports &amp;amp; BI Dashboard — Year-End Roadmap (v25.2)&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="your-feedback"&gt;Your Feedback Matters&lt;/h2&gt;
&lt;div data-survey-id="98baf635-770a-4431-89f9-4408a5d37345" data-survey-auth-required="false"&gt;&lt;/div&gt;</description>
      <pubDate>Wed, 13 Aug 2025 08:30:00 Z</pubDate>
      <dc:creator>Alex Chuev (DevExpress)</dc:creator>
      <dx:excerpt>This post outlines key features/enhancements planned for our next major update (v25.2)</dx:excerpt>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388247</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2025/05/06/devexpress-blazor-ai-chat-build-a-multi-llm-chat-application.aspx</link>
      <category domain="https://community.devexpress.com/Tags/AI">AI</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/LLM">LLM</category>
      <title>DevExpress Blazor AI Chat — Build a Multi-LLM Chat Application </title>
      <description>&lt;p&gt; AI services offer a variety of models tailored to meet user needs, preferences, and resource constraints. As you&amp;#39;d expect, models have strengths/weaknesses — some are optimized for coding tasks, while others are better suited for creative writing or real-time information retrieval.&lt;/p&gt;
&lt;p&gt;To select the appropriate&amp;nbsp;model, you often need to balance performance and cost. For example, advanced models like GPT-4.1 deliver exceptional results but require greater computational resources. In contrast, lighter models such as GPT-4.1 Mini or Nano offer faster response and lower costs, making them ideal for those seeking efficiency and responsiveness.&lt;/p&gt;
&lt;p&gt;For enterprise applications, the ability to connect to a cloud-based AI service — with the option to fallback to offline models running in restricted environments — is often a must.&lt;/p&gt;
&lt;p&gt;In this post, I’ll show you how to build a multi-LLM (Large Language Model) chat application that uses &lt;strong&gt;DevExpress Blazor AI Chat&lt;/strong&gt; and &lt;strong&gt;ComboBox&lt;/strong&gt; components. You’ll learn how to implement the &lt;code&gt;IChatClient&lt;/code&gt; interface to manage multiple chat clients and their respective conversation histories (and how to use the DevExpress Blazor ComboBox to switch between LLMs during a chat session).&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;To get started, you must first integrate the &lt;code&gt;DxAIChat&lt;/code&gt; component into your application (see our official guide for additional information in this regard): &lt;a target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.AIIntegration.Blazor.Chat.DxAIChat#add-an-ai-chat-to-a-project"&gt;Add AI Chat to a Project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this guide, I will use two LLMs: GPT-4o from Azure OpenAI and Phi4 from Ollama running locally. &lt;/p&gt;
&lt;p&gt;Here are my sample registrations for both chat clients from my&amp;nbsp;&lt;code&gt;Program.cs&lt;/code&gt; code unit. Note that I store LLM&amp;nbsp;credentials and settings in the app configuration file (&lt;code&gt;appSettings.Development.json&lt;/code&gt;). You can modify the following code to match your specific requirements:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
...
var openAiServiceSettings = builder.Configuration.GetSection(&amp;quot;OpenAISettings&amp;quot;).Get&amp;lt;OpenAIServiceSettings&amp;gt;();
var ollamaSettings = builder.Configuration.GetSection(&amp;quot;OllamaSettings&amp;quot;).Get&amp;lt;OllamaSettings&amp;gt;();

IChatClient azureChatClient = new AzureOpenAIClient(
    new Uri(openAiServiceSettings.Endpoint),
    new AzureKeyCredential(openAiServiceSettings.Key))
    .AsChatClient(openAiServiceSettings.DeploymentName);

IChatClient ollamaChatClient = new OllamaChatClient(
    new Uri(ollamaSettings.Uri), 
    ollamaSettings.ModelName);&lt;/code&gt;
&lt;/pre&gt;
&lt;div class="Note"&gt;
	&lt;p&gt;NOTE: To install, run, and download Ollama model, refer to the &lt;a target="_blank" href="https://docs.devexpress.com/CoreLibraries/405204/ai-powered-extensions#prerequisites"&gt;DevExpress AI Extensions — Prerequisities&lt;/a&gt; help topic. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next, we will implement infrastructure designed to track each client and its history.&lt;/p&gt;
&lt;h2 id="compositechatclient-implementation"&gt;CompositeChatClient Implementation&lt;/h2&gt;
&lt;p&gt;First, declare the &lt;code&gt;ChatClientSession&lt;/code&gt; class that represents an individual LLM session (includes the user-friendly model/service name, a client instance, and message history):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
using Microsoft.Extensions.AI;
//...
public class ChatClientSession {
    public string Name { get; set; }
    public IChatClient Client { get; }
    public List&amp;lt;BlazorChatMessage&amp;gt; Messages { get; set; }

    public ChatClientSession(IChatClient client, string name) {
        Name = name;
        Client = client ?? throw new ArgumentNullException(nameof(client));
        Messages = new List&amp;lt;BlazorChatMessage&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next,&amp;nbsp;define the &lt;code&gt;CompositeChatClient&lt;/code&gt; class, which implements the &lt;code&gt;IChatClient&lt;/code&gt; interface and serves as a container for multiple chat clients. By wrapping multiple clients within a single interface implementation, we can integrate this functionality with the &lt;strong&gt;DevExpress Blazor AI Chat&lt;/strong&gt; component (which relies on the &lt;code&gt;IChatClient&lt;/code&gt; interface).&lt;/p&gt;
&lt;p&gt;This class is designed to support LLM&amp;nbsp;switch while preserving individual conversation histories. Key considerations include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chat Client Management:&lt;/strong&gt; Stores a list of &lt;code&gt;ChatClientSession&lt;/code&gt; objects, where each object represents an LLM and its associated chat history.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Message Handling:&lt;/strong&gt; Routes messages to the currently selected chat client and retrieves responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
using Microsoft.Extensions.AI;
//...
public class CompositeChatClient : IChatClient	{
    public List&amp;lt;ChatClientSession&amp;gt; AvailableChatClients { get; }
    public ChatClientSession? SelectedSession { get; set; }
    public CompositeChatClient(params ChatClientSession[] chatClients)	{
        AvailableChatClients = chatClients.ToList();
        SelectedSession = AvailableChatClients[0];
    }

    public Task&amp;lt;ChatResponse&amp;gt; GetResponseAsync(IEnumerable&amp;lt;ChatMessage&amp;gt; messages, ChatOptions? options = null,
        CancellationToken cancellationToken = new CancellationToken())	{
        return SelectedSession?.Client.GetResponseAsync(messages, options, cancellationToken);
    }

    public IAsyncEnumerable&amp;lt;ChatResponseUpdate&amp;gt; GetStreamingResponseAsync(IEnumerable&amp;lt;ChatMessage&amp;gt; messages, ChatOptions? options = null,
        CancellationToken cancellationToken = new CancellationToken())	{
        return SelectedSession?.Client.GetStreamingResponseAsync(messages, options, cancellationToken);
    }

    public void Dispose()	{
        for (int i = 0; i &amp;lt; AvailableChatClients.Count; i++)	{
            AvailableChatClients[i].Client.Dispose();
            AvailableChatClients[i].Messages.Clear();
        }
    }
    public object? GetService(Type serviceType, object? serviceKey = null) {
        throw new NotImplementedException();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, switch back to the &lt;code&gt;Program.cs&lt;/code&gt; and register &lt;code&gt;CompositeChatClient&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
// Register both clients within a single instance of the IChatClient
var compositeChatClient = new CompositeChatClient(
    new ChatClientSession(azureChatClient, &amp;quot;Azure Open AI — GPT4o&amp;quot;), 
    new ChatClientSession(ollamaChatClient, &amp;quot;Ollama — Phi 4&amp;quot;));

builder.Services.AddScoped&amp;lt;IChatClient&amp;gt;((provider) =&amp;gt; compositeChatClient);
builder.Services.AddDevExpressAI();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="llm-selection-implementation"&gt;LLM Selection Using a ComboBox&lt;/h2&gt;
&lt;p&gt;To allow LLM selection, open the razor page with the &lt;code&gt;DxAIChat&lt;/code&gt; component and add the &lt;a href="https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxComboBox-2"&gt;DevExpress Blazor ComboBox&lt;/a&gt;. Key considerations include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CompositeChatClient Injection&lt;/strong&gt;: The &lt;code&gt;CompositeChatClient&lt;/code&gt; is injected to the page. It provides a list of available chat clients to the UI application. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM Selection&lt;/strong&gt;: The &lt;code&gt;DxComboBox&lt;/code&gt; component is bound to a list of clients and triggers a callback when selection changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chat Session Management&lt;/strong&gt;: The &lt;code&gt;OnModelChanged&lt;/code&gt; callback handles chat history save and load operations (when a user switches between LLMs).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s a snippet of the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
@page &amp;quot;/&amp;quot;
@using DevExpress.AIIntegration.Blazor.Chat
@using DXBlazorChatSelector.Services
@using Microsoft.Extensions.AI

&amp;lt;div class=&amp;quot;main-container&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;top-container&amp;quot;&amp;gt;
        &amp;lt;DxComboBox Data=&amp;quot;@ModelsList&amp;quot;
        	CssClass=&amp;quot;selector-container-combo-editor&amp;quot;
        	TextFieldName=&amp;quot;@nameof(ChatClientSession.Name)&amp;quot;
            Value=&amp;quot;ChatClientProvider.SelectedSession&amp;quot;
        	ValueChanged=&amp;quot;@((ChatClientSession session) =&amp;gt; OnModelChanged(session))&amp;quot;/&amp;gt;
    	&amp;lt;/div&amp;gt;
    &amp;lt;DxAIChat @ref=&amp;quot;DxAiChat&amp;quot; CssClass=&amp;quot;my-chat&amp;quot;&amp;gt;&amp;lt;/DxAIChat&amp;gt;
&amp;lt;/div&amp;gt;

@code{

    [Inject]
    IChatClient? ChatClient { get; set; }
    CompositeChatClient ChatClientProvider =&amp;gt; ChatClient as CompositeChatClient;
    DxAIChat? DxAiChat { get; set; }
    List&amp;lt;ChatClientSession&amp;gt; ModelsList =&amp;gt; ChatClientProvider?.AvailableChatClients;

    private void OnModelChanged(ChatClientSession value)  {
        SaveLastAssistantMessage(DxAiChat.SaveMessages());
        ChatClientProvider.SelectedSession = value;
        DxAiChat.LoadMessages(ChatClientProvider.SelectedSession.Messages);
    }

    private void SaveLastAssistantMessage(IEnumerable&amp;lt;BlazorChatMessage&amp;gt; saveMessages)	{
        if(ChatClientProvider.SelectedSession != null) {
            ChatClientProvider.SelectedSession.Messages.Clear();
            ChatClientProvider.SelectedSession.Messages.AddRange(saveMessages);
        }
    }
}    
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="start-a-new-chat"&gt;Start New Chat Implementation&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;To clear the message history for the selected LLM and start a new chat session&lt;/span&gt;, we will add a &lt;code&gt;DxButton&lt;/code&gt; and place it near the &lt;code&gt;DxComboBox&lt;/code&gt; in the &lt;code&gt;Index.razor&lt;/code&gt; page. Key considerations include:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Button Style&lt;/strong&gt;: The button uses an SVG icon declared in CSS.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Session Reset&lt;/strong&gt;: On click, the button clears the message history for the selected LLM and updates the chat UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The updated code for the &lt;code&gt;Index.razor&lt;/code&gt; page:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;
@page &amp;quot;/&amp;quot;
@using DevExpress.AIIntegration.Blazor.Chat
@using DXBlazorChatSelector.Services
@using Microsoft.Extensions.AI

&amp;lt;div class=&amp;quot;main-container&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;top-container&amp;quot;&amp;gt;
    	&amp;lt;DxButton RenderStyle=&amp;quot;ButtonRenderStyle.Primary&amp;quot;
        			RenderStyleMode=&amp;quot;ButtonRenderStyleMode.Contained&amp;quot;
                  	IconCssClass=&amp;quot;refresh&amp;quot;
                  	IconPosition=&amp;quot;ButtonIconPosition.BeforeText&amp;quot;
                  	CssClass=&amp;quot;refresh-button&amp;quot;
                  	Text=&amp;quot;Start New Chat&amp;quot;
                  	Click=&amp;quot;ClearHistory&amp;quot;/&amp;gt;
        &amp;lt;DxComboBox Data=&amp;quot;@ModelsList&amp;quot;
        			CssClass=&amp;quot;selector-container-combo-editor&amp;quot;
        			TextFieldName=&amp;quot;@nameof(ChatClientSession.Name)&amp;quot;
            		Value=&amp;quot;ChatClientProvider.SelectedSession&amp;quot;
        			ValueChanged=&amp;quot;@((ChatClientSession session) =&amp;gt; OnModelChanged(session))&amp;quot;/&amp;gt;
    	&amp;lt;/div&amp;gt;
    &amp;lt;DxAIChat @ref=&amp;quot;DxAiChat&amp;quot; CssClass=&amp;quot;my-chat&amp;quot;&amp;gt;&amp;lt;/DxAIChat&amp;gt;
&amp;lt;/div&amp;gt;

@code{

    [Inject]
    IChatClient? ChatClient { get; set; }
    CompositeChatClient ChatClientProvider =&amp;gt; ChatClient as CompositeChatClient;
    DxAIChat? DxAiChat { get; set; }
    List&amp;lt;ChatClientSession&amp;gt; ModelsList =&amp;gt; ChatClientProvider?.AvailableChatClients;

 	private void ClearHistory()	{
        ChatClientProvider.SelectedSession.Messages.Clear();
        DxAiChat.LoadMessages(ChatClientProvider.SelectedSession.Messages);
    }
    private void OnModelChanged(ChatClientSession value)	{
        SaveLastAssistantMessage(DxAiChat.SaveMessages());
        ChatClientProvider.SelectedSession = value;
        DxAiChat.LoadMessages(ChatClientProvider.SelectedSession.Messages);
    }

    private void SaveLastAssistantMessage(IEnumerable&amp;lt;BlazorChatMessage&amp;gt; saveMessages)	{
        if(ChatClientProvider.SelectedSession != null) {
            ChatClientProvider.SelectedSession.Messages.Clear();
            ChatClientProvider.SelectedSession.Messages.AddRange(saveMessages);
        }
    }
}    
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Final Result&lt;/h2&gt;
&lt;p&gt;The following video demonstrates our implementation: &lt;/p&gt;
&lt;video controls=""&gt;
    &lt;source src="https://community.devexpress.com/blogs/aspnet/25.1/ai-multi-llm/blazor-ai-chat-multiple-llms.mp4" type="video/mp4"&gt;
    Your browser does not support the video tag.
&lt;/video&gt;
&lt;h2 id="review-our-github-example"&gt;Review our GitHub Example&lt;/h2&gt;
&lt;p&gt;To download a complete sample and explore the implementation, refer to the following GitHub Repository: &lt;a target="_blank" href="https://github.com/DevExpress-Examples/blazor-ai-chat-with-multiple-llm-services/tree/24.2.6%2B"&gt;DevExpress Blazor AI Chat — Implement Switching Between Multiple AI Services&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By combining the power of the &lt;code&gt;IChatClient&lt;/code&gt; interface, the &lt;strong&gt;DevExpress Blazor ComboBox&lt;/strong&gt;, and the &lt;strong&gt;DevExpress Blazor AI Chat&lt;/strong&gt; component, you can deliver curated user experiences when using multiple LLMs.&lt;/p&gt;
&lt;p&gt;If you have any questions related to this implementation or would like to discuss AI-related integration needs, feel free to submit a support ticket via the &lt;a href="https://devexpress.com/ask"&gt;DevExpress Support Center&lt;/a&gt;. We&amp;#39;ll be happy to follow up.&lt;/p&gt;</description>
      <pubDate>Tue, 06 May 2025 23:40:00 Z</pubDate>
      <dc:creator>Dmitry Tokmachev (DevExpress)</dc:creator>
      <dx:excerpt>In this post, I’ll show you how to build a multi-LLM (Large Language Model) chat application that uses DevExpress Blazor AI Chat and ComboBox components.</dx:excerpt>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388238</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2025/04/23/blazor-early-access-preview-v25-1.aspx</link>
      <category domain="https://community.devexpress.com/Tags/2025">2025</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/EAP">EAP</category>
      <category domain="https://community.devexpress.com/Tags/v25.1">v25.1</category>
      <title>Blazor — Early Access Preview (v25.1)</title>
      <description>&lt;p&gt;
  &lt;span&gt;Our next major update (v25.1) is set for release in early June.&amp;nbsp;&lt;span&gt;In this post, I&amp;#39;ll summarize some of the features/capabilities available in our early access (EAP) build. Before I begin — a quick reminder: If you are an active&lt;/span&gt;&amp;nbsp;&lt;a href="https://www.devexpress.com/buy/net/"&gt;Universal or DXperience&lt;/a&gt;
&lt;span&gt;subscriber and want to explore upcoming v25.1-related features/capabilities before official release, you can download our EAP build via the&lt;/span&gt;&amp;nbsp;&lt;a href="https://www.devexpress.com/ClientCenter/DownloadManager/"&gt;DevExpress Download Manager&lt;/a&gt;&lt;span&gt;. Should you encounter issues with the EAP build, please submit a support ticket using the DevExpress Support Center.&lt;/span&gt;
  &lt;/span&gt;
&lt;/p&gt;
&lt;div class="Note"&gt;Early Access and CTP builds are provided solely for early testing purposes and are not ready for production use. This build can be installed side by side with other major versions of DevExpress products. Please backup your project and other important data before installing Early Access and CTP builds. This EAP does not include all features/products we expect to ship in our v25.1 release cycle. As its name implies, the EAP offers an early preview of what we expect to ship in two months.&lt;/div&gt;
&lt;h2 id="legacy-data-grid-combobox-tag-box-and-list-box-removal"&gt;Legacy Data Grid, ComboBox, Tag Box, and List Box are No Longer Available&lt;/h2&gt;

&lt;p&gt;
  &lt;span&gt;To streamline our distribution, we have removed the following legacy components from our v25.1 update:&lt;/span&gt;
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;code&gt;DxDataGrid&lt;/code&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;code&gt;DxComboBoxLegacy&lt;/code&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;code&gt;DxTagBoxLegacy&lt;/code&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;code&gt;DxListBoxLegacy&lt;/code&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To avoid issues, please check your projects for components from the&amp;nbsp; &lt;code&gt;DevExpress.Blazor.Legacy&lt;/code&gt;&amp;nbsp;namespace and migrate to new DevExpress alternatives. &lt;/p&gt;
&lt;p&gt;Helpful resources:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;a href="https://docs.devexpress.com/Blazor/403162/components/data-grid/migrate-from-data-grid-to-grid"&gt;Migrate from Data Grid to Grid&lt;/a&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href="https://supportcenter.devexpress.com/ticket/details/t1251358/data-grid-and-and-legacy-list-editors-were-moved-to-the-legacy-namespace"&gt;Legacy component deprecation details&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are still using older versions of DevExpress Blazor components, please contact us via the DevExpress&amp;nbsp;&lt;a href="https://supportcenter.devexpress.com/ticket/list" target="_blank"&gt;Support Center&lt;/a&gt; and let us know how we can help you migrate to newer&amp;nbsp;alternatives.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
  &lt;br&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span style="font-family:&amp;#39;Open Sans Condensed&amp;#39;, HelveticaNeue-CondensedBold, Helvetica, &amp;#39;Arial Narrow&amp;#39;, Calibri, Arial, &amp;#39;Lucida Grande&amp;#39;, sans-serif;font-size:30px;"&gt;Blazor Editors&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span style="font-family:&amp;#39;Open Sans Condensed&amp;#39;, HelveticaNeue-CondensedBold, Helvetica, &amp;#39;Arial Narrow&amp;#39;, Calibri, Arial, &amp;#39;Lucida Grande&amp;#39;, sans-serif;font-size:30px;"&gt;
    &lt;br&gt;
  &lt;/span&gt;
  &lt;span style="font-family:&amp;#39;Open Sans Condensed&amp;#39;, HelveticaNeue-CondensedBold, Helvetica, &amp;#39;Arial Narrow&amp;#39;, Calibri, Arial, &amp;#39;Lucida Grande&amp;#39;, sans-serif;color:#656565;font-size:24px;"&gt;Blazor ComboBox —&amp;nbsp;Set Data Load Mode&amp;nbsp;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;The DevExpress Blazor ComboBox allows you to manage data source interactions using &lt;code&gt;DataLoadMode&lt;/code&gt;. Since v24.1, our Blazor ComboBox caches data to reduce frequent database requests. You can now set &lt;code&gt;DataLoadMode&lt;/code&gt; to &lt;code&gt;OnDemand&lt;/code&gt; (similar to behaviors available prior to v24.1). This option allows the ComboBox to fetch data from the data source each time the dropdown activates. When this option is used, our Blazor&amp;nbsp;ComboBox does not preload data when the page loads (improving&amp;nbsp;initial page load speed.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;
    &lt;br&gt;
  &lt;/span&gt;
  &lt;span style="color:#656565;font-family:&amp;#39;Open Sans Condensed&amp;#39;, HelveticaNeue-CondensedBold, Helvetica, &amp;#39;Arial Narrow&amp;#39;, Calibri, Arial, &amp;#39;Lucida Grande&amp;#39;, sans-serif;font-size:24px;"&gt;Blazor&amp;nbsp;List Box — Select All&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;The DevExpress Blazor List Box allows you to display a Select All check box above list box items.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;
    &lt;span&gt;To explore the capabilities of the EAP version of the DevExpress Blazor List Box control, open our locally installed Blazor demo:&amp;nbsp;&lt;strong&gt;ListBox&lt;/strong&gt;
    &lt;/span&gt;
    &lt;strong&gt;&amp;nbsp;&lt;/strong&gt;
    &lt;strong&gt;→ Multiple Selection&lt;/strong&gt;
    &lt;span&gt;.&lt;/span&gt;
  &lt;/span&gt;
&lt;/p&gt;
&lt;h2 id="app-showcase-demo"&gt;App Showcase Demo&lt;/h2&gt;
&lt;p&gt;New CRM, Analytics, and Scheduling/Planning modules (built with our Blazor Grid, TreeList, Scheduler, ListBox, ComboBox, Charts, and other DevExpress Blazor components).&lt;/p&gt;
&lt;p&gt;Our showcase demo utilizes &lt;code&gt;InteractiveAuto&lt;/code&gt; render mode, introduced in .NET 8 —&amp;nbsp;starting with Blazor Server for fast initial load and seamlessly transitioning to WebAssembly (WASM) for optimal speed and responsiveness.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;The application&amp;nbsp;uses our new Fluent theme with light/dark mode support. Each module is built with reusable code, so you can easily adapt/integrate module components into your next DevExpress-powered Blazor project.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
  &lt;img src="https://community.devexpress.com/blogs/aspnet/v25.1/app-showcase.png" alt="app showcase"&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;To explore the capabilities of the demo, open our locally installed showcase demo:&amp;nbsp;&lt;code&gt;DevExpress Demos 25.1\Components\Blazor\BlazorDemo.Showcase&lt;/code&gt;&amp;nbsp;or visit&lt;span&gt;
      &lt;a id="menur7m7o" href="https://demos.devexpress.com/blazor-showcase/" rel="noreferrer noopener" target="_blank" title="https://demos.devexpress.com/blazor-showcase"&gt;https://demos.devexpress.com/blazor-showcase/&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;
&lt;/p&gt;
&lt;h2 id="pivot-table-official-release"&gt;Blazor Pivot Table&lt;/h2&gt;
&lt;h3 id="filter-data"&gt;Filter Data&lt;/h3&gt;
&lt;p style="color:#161616;"&gt;The DevExpress Blazor&amp;nbsp;&lt;a href="https://docs.devexpress.com/Blazor/405245/pivot-table"&gt;Pivot Table&lt;/a&gt;&amp;nbsp;allows you to apply filters to fields displayed in the Filter Header Area. You can define these filter fields in component markup or drag them from other Pivot Table areas. Use the&amp;nbsp; &lt;span&gt;
    &lt;code&gt;FilterHeaderAreaDisplayMode&lt;/code&gt;
  &lt;/span&gt;&amp;nbsp;property to control Filter Header Area visibility. &lt;/p&gt;
&lt;p style="color:#161616;"&gt;Filter field headers display filter menu buttons. Users can press these buttons to open Field Filter Menus. You can use the following properties to control filter menu button visibility:&lt;/p&gt;
&lt;ul style="color:#161616;"&gt;
  &lt;li&gt;
    &lt;span&gt;
      &lt;code&gt;DxPivotTable.FilterMenuButtonDisplayMode&lt;/code&gt;
    &lt;/span&gt;&amp;nbsp;- Specifies whether a filter menu button is visible for all filter field headers.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;span&gt;
      &lt;code&gt;DxPivotTableField.FilterMenuButtonDisplayMode&lt;/code&gt;
    &lt;/span&gt;&amp;nbsp;- Specifies whether a filter menu button is visible for a specific field.
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p style="color:#161616;"&gt;The Field Filter Menu displays a drop-down window with all unique values of the field. Users can select/deselect these values to filter Pivot Table data. &lt;/p&gt;
&lt;p style="color:#161616;"&gt;You can use the following APIs to customize the filter menu as requirements dictate:&lt;/p&gt;
&lt;ul style="color:#161616;"&gt;
  &lt;li&gt;The&amp;nbsp; &lt;code&gt;CustomizeFilterMenu&lt;/code&gt;&amp;nbsp;event fires before the drop-down filter is displayed and allows you to customize filter items. &lt;/li&gt;
  &lt;li&gt;The&amp;nbsp; &lt;code&gt;FilterMenuTemplate&lt;/code&gt;&amp;nbsp;property specifies the template used for the content displayed within the field&amp;#39;s drop-down filter. &lt;/li&gt;
  &lt;li&gt;The&amp;nbsp; &lt;code&gt;FieldFilterMenuTemplate&lt;/code&gt;&amp;nbsp;property specifies the template used for all drop-down filter menus in the Pivot Table. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;img src="https://community.devexpress.com/blogs/aspnet/25.1/EAP/pivot-filter-ui.png" alt="pivot grid filter ui"&gt;
&lt;/p&gt;
&lt;p&gt;In addition to the UI-based filtering options, t&lt;span style="color:#161616;"&gt;he DevExpress Blazor&amp;nbsp;&lt;/span&gt;Pivot Table &lt;span style="color:#161616;"&gt;&amp;nbsp;allows you to filter data in code.&amp;nbsp; &lt;span&gt;To apply filter criteria, create a&amp;nbsp;&lt;/span&gt;
    &lt;a href="https://docs.devexpress.com/CoreLibraries/2129/devexpress-data-library/criteria-operators"&gt;criteria operator&lt;/a&gt;
    &lt;span&gt;&amp;nbsp;object that specifies a filter expression and send this object to the&amp;nbsp;&lt;/span&gt;
    &lt;code&gt;SetFilterCriteria&lt;/code&gt;
    &lt;span&gt;&amp;nbsp;method.&lt;/span&gt;
  &lt;/span&gt;
  &lt;br&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;To explore the capabilities of the EAP version of the DevExpress Blazor Pivot Table control, open our locally installed Blazor demo:&amp;nbsp;&lt;/span&gt;
  &lt;strong&gt;Pivot Table&amp;nbsp;&lt;/strong&gt;
  &lt;strong&gt;→ Filter Data.&lt;/strong&gt;
  &lt;br&gt;
&lt;/p&gt;
&lt;h2 id="grid-treelist"&gt;Blazor Grid &amp;amp; TreeList&lt;/h2&gt;
&lt;h3 id="pdf-export"&gt;PDF Export&lt;/h3&gt;
&lt;div&gt;Our Blazor Grid and TreeList now support PDF export APIs. This feature allows you to generate PDF documents while preserving key data presentation elements, including:&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;Column order, captions, and alignment&lt;/li&gt;
  &lt;li&gt;Value format and alignment&lt;/li&gt;
  &lt;li&gt;Group rows, group summaries, and group totals&lt;/li&gt;
  &lt;li&gt;Total summaries&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;We do not expect to support full WYSIWYG export -&amp;nbsp;some UI elements (such as the group panel, search panel, toolbar, and pager, as well as custom CSS styles) will be excluded.&lt;/div&gt;
&lt;p&gt;
  &lt;img src="https://community.devexpress.com/blogs/aspnet/25.1/EAP/grid-pdf-export.png" alt="grid pdf export"&gt;
&lt;/p&gt;
&lt;div&gt;To explore the capabilities of the EAP version of the DevExpress Blazor Grid control, open our locally installed Blazor demo: &amp;nbsp;&lt;strong&gt;Grid &lt;/strong&gt;
  &lt;strong&gt;→ Export → Export Data&lt;/strong&gt;.
&lt;/div&gt;

&lt;h3 id="keyboard-support-enhancements"&gt;Keyboard Support Enhancements&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Filter Row:&lt;/strong&gt; Users can now enter a new filter value even if a cell editor is inactive (no need to press Enter first).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cell Editing:&lt;/strong&gt; If the EditOnKeyPress property is enabled, users can begin typing to open an editor for a focused cell.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="blazor-context-menu"&gt;Blazor Context Menu&lt;/h2&gt;
&lt;h3 id="performance-enhancements"&gt;Performance Enhancements&lt;/h3&gt;
&lt;p&gt;We optimized our Blazor Context Menu&amp;#39;s rendering logic to reduce the number of messages sent between the server and client. Depending on connection quality and the number of items, DevExpress Context Menus now open 20-50% faster when used in Blazor Server applications.&lt;/p&gt;  
&lt;p&gt;
  &lt;img src="https://community.devexpress.com/blogs/aspnet/25.1/EAP/context-menu-performance-enhancements.png" alt="context menu performance"&gt;
&lt;/p&gt;

&lt;h2&gt;Blazor Rich Text Editor&lt;/h2&gt;
&lt;h3&gt;Zoom Operations&lt;/h3&gt;
&lt;p&gt;
  &lt;span style="color:#2b2b2b;"&gt;With v25.1, the DevExpress Blazor Rich Text Editor allows you to zoom in/out of documents with ease (via the UI and in code). Users can modify document&amp;nbsp;zoom level using a drop-down menu within the View&lt;/span&gt;&amp;nbsp;R&lt;span style="color:#2b2b2b;"&gt;ibbon tab. Available levels range from 50%&lt;/span&gt;
  &lt;span style="color:#2b2b2b;"&gt;&amp;nbsp;to 200%&lt;/span&gt;
  &lt;span style="color:#2b2b2b;"&gt;.&amp;nbsp;Disable the &lt;code&gt;AllowZoom&lt;/code&gt; property to hide the drop-down zoom level menu.&amp;nbsp; &lt;span&gt;Irrespective of the&amp;nbsp;&lt;/span&gt;
    &lt;code&gt;AllowZoom&lt;/code&gt;
    &lt;span&gt;&amp;nbsp;property value, you can use the &lt;span&gt;
        &lt;code&gt;ZoomLevel&lt;/code&gt;
        &lt;span&gt;&amp;nbsp;&lt;/span&gt;
      &lt;/span&gt;
    &lt;/span&gt;
    &lt;span&gt;property to zoom a document at runtime.&lt;/span&gt;
  &lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;img src="https://community.devexpress.com/blogs/aspnet/v25.1/rich-zoom.png" alt="rich text editor zoom"&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;span&gt;To explore the capabilities of the EAP version of the DevExpress Blazor RichEdit control, open the following locally installed Blazor demo: &lt;/span&gt;&lt;strong&gt;Rich Text Editor &lt;/strong&gt;
  &lt;strong&gt;→ Overview&lt;/strong&gt;
  &lt;span&gt;.&lt;/span&gt;
  &lt;br&gt;
&lt;/p&gt;
&lt;h2 id="rich-text-editor"&gt;&lt;/h2&gt;
&lt;h2 id="scheduler"&gt;Blazor Scheduler&lt;/h2&gt;
&lt;h3 id="toolbar-customization"&gt;Toolbar Customization&lt;/h3&gt;
&lt;div&gt;The DevExpress Blazor Scheduler allows you to define custom toolbar items and customize the following built-in items:&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;Today button&lt;/li&gt;
  &lt;li&gt;Next and Previous buttons&lt;/li&gt;
  &lt;li&gt;Date navigator&lt;/li&gt;
  &lt;li&gt;Resource navigator&lt;/li&gt;
  &lt;li&gt;Scheduler View selector&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;Customization options include:&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;Position and alignment of toolbar items.&lt;/li&gt;
  &lt;li&gt;Enabled, Visible, Tooltip, and Render Style properties.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;Use the &lt;span&gt;
    &lt;code&gt;ToolbarItems&lt;/code&gt;
    &lt;span&gt;&amp;nbsp;tag to modify a collection of visible toolbar items. Declare &lt;span&gt;
        &lt;code&gt;DxToolbarItem&lt;/code&gt;
      &lt;/span&gt; objects to add custom items to this collection.&amp;nbsp;Alternatively, disable the &lt;code&gt;ShowToolbarArea&lt;/code&gt; property to hide the toolbar entirely. &lt;/span&gt;
  &lt;/span&gt;
&lt;/div&gt;
&lt;pre&gt;      &lt;code class="language-html"&gt;&amp;lt;DxScheduler ...&amp;gt;
    &amp;lt;ToolbarItems&amp;gt;
        &amp;lt;DxSchedulerPreviousIntervalToolbarItem /&amp;gt;
        &amp;lt;DxSchedulerNextIntervalToolbarItem /&amp;gt;
        &amp;lt;DxSchedulerTodayToolbarItem /&amp;gt;
        &amp;lt;DxSchedulerDateNavigatorToolbarItem /&amp;gt;
        &amp;lt;DxToolbarItem GroupName=&amp;quot;ScaleDuration&amp;quot;
                       Text=&amp;quot;1 day&amp;quot;
                       Click=&amp;quot;@((i) =&amp;gt; CurrentDuration = TimeSpan.FromHours(24))&amp;quot;
                       Checked=&amp;quot;@(CurrentDuration == TimeSpan.FromHours(24))&amp;quot;
                       BeginGroup=&amp;quot;true&amp;quot;
                       Alignment=&amp;quot;ToolbarItemAlignment.Right&amp;quot;&amp;gt;&amp;lt;/DxToolbarItem&amp;gt;
        &amp;lt;DxToolbarItem GroupName=&amp;quot;ScaleDuration&amp;quot;
                       Text=&amp;quot;2 days&amp;quot;
                       Click=&amp;quot;@((i) =&amp;gt; CurrentDuration = TimeSpan.FromHours(48))&amp;quot;
                       Checked=&amp;quot;@(CurrentDuration == TimeSpan.FromHours(48))&amp;quot;&amp;gt;&amp;lt;/DxToolbarItem&amp;gt;
        &amp;lt;DxToolbarItem GroupName=&amp;quot;ScaleDuration&amp;quot;
                       Text=&amp;quot;3 days&amp;quot;
                       Click=&amp;quot;@((i) =&amp;gt; CurrentDuration = TimeSpan.FromHours(72))&amp;quot;
                       Checked=&amp;quot;@(CurrentDuration == TimeSpan.FromHours(72))&amp;quot;&amp;gt;&amp;lt;/DxToolbarItem&amp;gt;
    &amp;lt;/ToolbarItems&amp;gt;
&amp;lt;/DxScheduler&amp;gt;&lt;/code&gt;
&lt;/pre&gt;
&lt;img src="https://community.devexpress.com/blogs/aspnet/25.1/EAP/scheduler-toolbar.png" alt="custom toolbar items"&gt;
&lt;p&gt;To explore the capabilities of the EAP version of the DevExpress Blazor Scheduler control, open the following locally installed Blazor demo: &lt;strong&gt;Scheduler&amp;nbsp;&lt;/strong&gt;
  &lt;strong&gt;→ Customization&amp;nbsp; &lt;strong&gt;→ Custom Toolbar Items&lt;/strong&gt;
  &lt;/strong&gt;. &lt;br&gt;
&lt;/p&gt;
&lt;h3 id="resizing-and-dragging-for-appointment-form"&gt; Appointment Form Resize and Drag Operations&lt;/h3&gt;
&lt;p&gt;You can now drag and resize our&amp;nbsp;&lt;a href="https://docs.devexpress.com/Blazor/404564/components/scheduler/customization/appointment-forms-and-tooltips#default-appointment-form" target="_blank"&gt;Extended Appointment Form&lt;/a&gt;&amp;nbsp;(it is now easier to view the entirety of a&amp;nbsp;calendar without discarding changes).  &lt;/p&gt;
&lt;h3 id="popup-performance-enhancements"&gt;&lt;/h3&gt;
&lt;h3 id="Performance-Enhancements" style="font-weight:600;color:#333333;"&gt;Performance Enhancements&lt;/h3&gt;
&lt;p style="color:#333333;"&gt;We enhanced performance for the following operations/usage scenarios:&lt;/p&gt;
&lt;ul style="color:#333333;"&gt;
  &lt;li&gt;Time needed to open Date Navigator and Resource Navigator popups&lt;/li&gt;
  &lt;li&gt;Appointment dragging&lt;/li&gt;
  &lt;li&gt;Time needed to open an appointment’s tooltip and compact/extended form&lt;/li&gt;
  &lt;li&gt;Creating, updating, and deleting appointments&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="UIUX-Enhancements" style="font-weight:600;color:#333333;"&gt;UI/UX Enhancements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;strong&gt;Icons used for the Today button and each view selector (Day, Work Week, Month, Timeline).&lt;/strong&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;A dropdown button for the view selector.&lt;/strong&gt; In previous versions, we used a button group to select view type. With our new dropdown button, the UI requires less space, improving the user experience and allowing you to add additional custom items to the toolbar.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;A new render style mode for built-in toolbar items.&lt;/strong&gt; We changed the render style mode to Plain.&amp;nbsp;&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;New appointment label colors in the Fluent theme.&lt;/strong&gt;&amp;nbsp;We updated&amp;nbsp;default label colors, replacing bright colors with a pastel palette. This modification was designed to improve visual clarity and reduces eye strain.&amp;nbsp;&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;New status styles for the Fluent theme.&lt;/strong&gt; Appointment statuses now use styles similar to those of Microsoft Outlook. 
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;img src="https://community.devexpress.com/blogs/aspnet/25.1/EAP/scheduler-before.png" data-label-after="Blazor Scheduler (v24.2)" data-label-before="Blazor Scheduler (v25.1)" data-comparer-position="50" data-comparer-theme="dark" data-before-src="https://community.devexpress.com/blogs/aspnet/25.1/EAP/scheduler-after.png" alt=""&gt;
&lt;/p&gt;
&lt;h2 id="scheduler"&gt;Blazor Charts&lt;/h2&gt;
&lt;h3 id="toolbar-customization"&gt;Zoom-related APIs&lt;/h3&gt;
&lt;div&gt;New APIs include:&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;span style="color:#161616;"&gt;&lt;code&gt;ZoomChanged&lt;/code&gt;
    &lt;/span&gt;
    &lt;span style="color:#161616;"&gt; event - triggers whenever a zoom or pan operation is performed.&lt;/span&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code&gt;ResetVisualRange&lt;/code&gt; method - restores the maximum possible visual range. &lt;/li&gt;
  &lt;li&gt;&lt;code&gt;SetArgumentAxisVisualRange&lt;/code&gt;&amp;nbsp;method - applies a specific visual range to the argument axis. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Series Label API Enhancements&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;
  &lt;span style="color:#333333;"&gt;v25.1 includes an expanded the list of series label options. &lt;/span&gt;    &lt;code style="color:#333333;"&gt;DxChartSeriesLabel&lt;/code&gt;
  &lt;span style="color:#333333;"&gt;&amp;nbsp;supports the following pie series label-related properties:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;code&gt;TextOverflow&lt;/code&gt;&amp;nbsp;and&amp;nbsp; &lt;code&gt;WordWrap&lt;/code&gt;&amp;nbsp;- specifies how the Pie Chart displays text for&amp;nbsp;overflowing labels.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;code&gt;RadialOffset&lt;/code&gt; - shifts pie series labels radially away from or towards the center of a chart.
  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Legend Click Events&lt;/h3&gt;
&lt;p&gt;v25.1 ships with&amp;nbsp;&lt;code&gt;DxChart.LegendClick&lt;/code&gt;, &lt;code&gt;DxPolarChart.LegendClick&lt;/code&gt;, and &lt;code&gt;DxPieChart.LegendClick&lt;/code&gt; events. Handle these events to execute custom logic when users click legend items.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:&amp;#39;Open Sans Condensed&amp;#39;, HelveticaNeue-CondensedBold, Helvetica, &amp;#39;Arial Narrow&amp;#39;, Calibri, Arial, &amp;#39;Lucida Grande&amp;#39;, sans-serif;font-size:30px;"&gt;Y&lt;/span&gt;&lt;span style="font-family:&amp;#39;Open Sans Condensed&amp;#39;, HelveticaNeue-CondensedBold, Helvetica, &amp;#39;Arial Narrow&amp;#39;, Calibri, Arial, &amp;#39;Lucida Grande&amp;#39;, sans-serif;font-size:30px;"&gt;our Feedback Counts&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If you have a specific question about our Blazor EAP (v25.1), feel free to submit a support ticket via the DevExpress&amp;nbsp;&lt;a href="https://supportcenter.devexpress.com/ticket/list" target="_blank"&gt;Support Center&lt;/a&gt;. We will be happy to follow-up.&amp;nbsp;&lt;/p&gt;&lt;p&gt;For additional information on what you can expect from our mid-year release, please refer to our&amp;nbsp;&lt;a href="https://community.devexpress.com/blogs/aspnet/archive/2025/02/20/blazor-june-2025-roadmap-v25-1.aspx" target="_blank"&gt;June 2025 Roadmap&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Wed, 23 Apr 2025 11:55:00 Z</pubDate>
      <dc:creator>Slava Khudyakov (DevExpress)</dc:creator>
      <dx:excerpt> In this post, I will summarize some of the features/capabilities available in our early access (EAP) build</dx:excerpt>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388228</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2025/04/09/devexpress-blazor-ai-chat-implement-function-calling.aspx</link>
      <category domain="https://community.devexpress.com/Tags/AI">AI</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Semantic+Kernel">Semantic Kernel</category>
      <category domain="https://community.devexpress.com/Tags/Tool+Calling">Tool Calling</category>
      <title>DevExpress Blazor AI Chat — Implement Function Calling</title>
      <description>&lt;p&gt;Modern AI-powered applications require seamless interaction with external systems or internal app components. Many AI service providers now support &lt;strong&gt;function calling&lt;/strong&gt; (also known as tool calling), which allows AI models to trigger functions at runtime. This capability is of particular value for agentic workflows/applications wherein&amp;nbsp;AI needs to execute actions such as fetching data, invoking APIs, or initiating tasks within an application (from&amp;nbsp;scheduling appointments and modifying database info to updating the app’s appearance). &lt;/p&gt;
&lt;p&gt;The overall flow in this instance looks like this: instead of replying to a user message, the model requests a function invocation with specified arguments. The chat client then invokes the function and supplies the result back to the LLM. At this point, the LLM constructs a response considering the value returned from the function. &lt;/p&gt;
&lt;p&gt;In this guide, we&amp;#39;ll explore how to enable function calling in the DevExpress Blazor &lt;code&gt;DxAiChat&lt;/code&gt; component using: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;IChatClient&lt;/code&gt; interface from the &lt;strong&gt;Microsoft.Extensions.AI&lt;/strong&gt; library. &lt;/li&gt;
&lt;li&gt;Plugins from Microsoft&amp;#39;s Semantic Kernel. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;To get started, you must first integrate&amp;nbsp;the &lt;code&gt;DxAiChat&lt;/code&gt; component into your application (see our official guide for additional information): &lt;a target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.AIIntegration.Blazor.Chat.DxAIChat#add-an-ai-chat-to-a-project"&gt;Add AI Chat to a Project&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Next, register your AI service. In this example, we’ll use Azure OpenAI. Here’s a sample &lt;code&gt;Program.cs&lt;/code&gt; setup: &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;

using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
...
var builder = WebApplication.CreateBuilder(args);
...
// Replace with your endpoint, API key, and deployed AI model name
string azureOpenAIEndpoint = Environment.GetEnvironmentVariable(&amp;quot;AZURE_OPENAI_ENDPOINT&amp;quot;);
string azureOpenAIKey = Environment.GetEnvironmentVariable(&amp;quot;AZURE_OPENAI_API_KEY&amp;quot;);
string deploymentName = string.Empty;
...
var azureChatClient = new AzureOpenAIClient(
    new Uri(azureOpenAIEndpoint),
    new AzureKeyCredential(azureOpenAIKey));

IChatClient chatClient = azureChatClient.AsChatClient(deploymentName);

builder.Services.AddDevExpressBlazor();
builder.Services.AddChatClient(chatClient);
builder.Services.AddDevExpressAI();
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the project to confirm that you can send messages and receive AI responses. &lt;/p&gt;
&lt;h2 id="tool-calling-with-ichatclient"&gt;Tool Calling with IChatClient&lt;/h2&gt;
&lt;p&gt;First, define a simple function to retrieve weather information for a specified city. In this example, this is &lt;code&gt;GetWeatherTool&lt;/code&gt;. To help AI understand how to invoke the &lt;code&gt;GetWeatherTool&lt;/code&gt; function, use the &lt;code&gt;System.ComponentModel.Description&lt;/code&gt; attribute for both the method and its parameters. LLM uses parameters to figure out the most suitable method call and plan a call sequence:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.ComponentModel;
using Microsoft.Extensions.AI;

public class CustomAIFunctions
{
    public static AIFunction GetWeatherTool =&amp;gt; AIFunctionFactory.Create(GetWeather); 
    [Description(&amp;quot;Gets the current weather in the city&amp;quot;)]
    public static string GetWeather([Description(&amp;quot;The name of the city&amp;quot;)] string city)
    {
        switch (city)
        {
            case &amp;quot;Los Angeles&amp;quot;:
            case &amp;quot;LA&amp;quot;:
                return GetTemperatureValue(20);
            case &amp;quot;London&amp;quot;:
                return GetTemperatureValue(15);
            default:
                return $&amp;quot;The information about the weather in {city} is not available.&amp;quot;;
        }
    }
    static string GetTemperatureValue(int value)
    {
        var valueInFahrenheits = value * 9 / 5 + 32;
        return $&amp;quot;{valueInFahrenheits}\u00b0F ({value}\u00b0C)&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify the chat client registration as shown below to provide a list of available functions and allow the client to invoke functions when responding to user questions. Ensure that you configure the chat client options first, as the method call sequence is crucial here:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
...
IChatClient chatClient = new ChatClientBuilder(azureChatClient)
    .ConfigureOptions(opt =&amp;gt;
    {
        opt.Tools = [CustomAIFunctions.GetWeatherTool];
    })
    .UseFunctionInvocation()
    .Build();

builder.Services.AddChatClient(chatClient);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, when the user asks the AI service about the weather, the service will automatically trigger the &lt;code&gt;GetWeatherTool&lt;/code&gt; function and add results into its response.&lt;/p&gt;
&lt;img class="small" src="https://community.devexpress.com/blogs/aspnet/25.1/ai-tool-calling/ext/blazor-ai-chat-tool-calling-meai1.png" alt="" style="width:600px;height:630px;"&gt;
&lt;h2&gt;Integrating Semantic Kernel Plugins&lt;/h2&gt;
&lt;p&gt;Microsoft Semantic Kernel  allows developers to incorporate advanced AI capabilities into their apps (including reasoning, workflow orchestration, and dynamic prompt engineering). Microsoft&amp;#39;s framework enhances AI-powered solutions by allowing apps to interact with plugins and manage memory more effectively. &lt;/p&gt;
&lt;p&gt;To begin, add the following NuGet packages to your project: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.nuget.org/packages/Microsoft.SemanticKernel"&gt;Microsoft.SemanticKernel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.nuget.org/packages/Microsoft.SemanticKernel.Plugins.Core/1.38.0-alpha"&gt;Microsoft.SemanticKernel.Plugins.Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.nuget.org/packages/Microsoft.SemanticKernel.Connectors.OpenAI"&gt;Microsoft.SemanticKernel.Connectors.OpenAI&lt;/a&gt; (or an appropriate connector for your AI provider)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are already using the Semantic Kernel in your app and are familiar with the &lt;a target="_blank" href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/plugins/?pivots=programming-language-csharp"&gt;Plugin&amp;nbsp;concept&lt;/a&gt;, you can easily connect it to our Blazor &lt;code&gt;DxAiChat&lt;/code&gt; control. &lt;/p&gt;
&lt;p&gt;Since DevExpress AI-powered APIs operate with LLMs using the &lt;code&gt;IChatClient&lt;/code&gt; interface, you need to implement the interface manually and invoke &lt;code&gt;IChatCompletionService&lt;/code&gt; methods from the Semantic Kernel: &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
...
public class SemanticKernelPluginCallingChatClient : IChatClient {
	private IChatCompletionService _chatCompletionService;
    private Kernel _kernel;
    private OpenAIPromptExecutionSettings _executionSettings;
    public SemanticKernelPluginCallingChatClient(Kernel kernel)
    {
    	_kernel = kernel;
        _chatCompletionService = _kernel.GetRequiredService();
        _executionSettings = new OpenAIPromptExecutionSettings() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions };
    }

	public async Task GetResponseAsync(IEnumerable chatMessages, ChatOptions? options = null, CancellationToken cancellationToken = default)
    {
        var history = GetChatHistory(chatMessages);
        ChatMessageContent message = await _chatCompletionService.GetChatMessageContentAsync(history, _executionSettings, _kernel, cancellationToken);
        return new ChatResponse(new ChatMessage(ChatRole.Assistant, message.Content));
    }

	public async IAsyncEnumerable GetStreamingResponseAsync(IEnumerable chatMessages, ChatOptions? options = null, CancellationToken cancellationToken = default)
	{
        var history = GetChatHistory(chatMessages);
        await foreach(var item in _chatCompletionService.GetStreamingChatMessageContentsAsync(history, _executionSettings, _kernel, cancellationToken)) {
        	yield return new ChatResponseUpdate(ChatRole.Assistant, item.Content);
    	}
    }
    
    AuthorRole GetRole(ChatRole chatRole) {
        if(chatRole == ChatRole.User) return AuthorRole.User;
        if(chatRole == ChatRole.System) return AuthorRole.System;
        if(chatRole == ChatRole.Assistant) return AuthorRole.Assistant;
        if(chatRole == ChatRole.Tool) return AuthorRole.Tool;
        throw new Exception();
    }

	private ChatHistory GetChatHistory(IEnumerable chatMessages)
    {
    	var history = new ChatHistory(chatMessages.Select(x =&amp;gt; new ChatMessageContent(GetRole(x.Role), x.Text)));
        return history;
    }
...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Implement a Semantic Kernel plugin similar to the previous function, but decorate the main function method with the &lt;code&gt;Microsoft.SemanticKernel.KernelFunction&lt;/code&gt; attribute: &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using Microsoft.SemanticKernel;
using System.ComponentModel;
...

public class WeatherPlugin {
        [KernelFunction]
        [Description(&amp;quot;Gets the current weather in the city&amp;quot;)]
        public static string GetWeather([Description(&amp;quot;The name of the city&amp;quot;)] string city) {
            switch(city) {
                case &amp;quot;Los Angeles&amp;quot;:
                case &amp;quot;LA&amp;quot;:
                    return GetTemperatureValue(20);
                case &amp;quot;London&amp;quot;:
                    return GetTemperatureValue(15);
                default:
                    return $&amp;quot;The information about the weather in {city} is not available.&amp;quot;;
            }
        }
        static string GetTemperatureValue(int value)
        {
            var valueInFahrenheits = value * 9 / 5 + 32;
            return $&amp;quot;{valueInFahrenheits}\u00b0F ({value}\u00b0C)&amp;quot;;
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, register the Semantic Kernel and the chat client during app startup: &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.Core;
...
    
var semanticKernelBuilder = Kernel.CreateBuilder();
semanticKernelBuilder.AddAzureOpenAIChatCompletion(
    deploymentName,
    azureOpenAIEndpoint,
    azureOpenAIKey);

// Add plugins from Microsoft.SemanticKernel.Plugins.Core
#pragma warning disable SKEXP0050
semanticKernelBuilder.Plugins.AddFromType&amp;lt;TimePlugin&amp;gt;(); // this is a built-in plugin
semanticKernelBuilder.Plugins.AddFromType&amp;lt;WeatherPlugin&amp;gt;(); // this is our custom plugin
#pragma warning restore SKEXP0050

var globalKernel = semanticKernelBuilder.Build();
builder.Services.AddChatClient(new SemanticKernelPluginCallingChatClient(globalKernel));

builder.Services.AddDevExpressAI();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once configured, your application will use the Semantic Kernel plugin to intelligently&amp;nbsp;handle requests:&lt;/p&gt;
&lt;img class="small" src="https://community.devexpress.com/blogs/aspnet/25.1/ai-tool-calling/ext/blazor-ai-chat-tool-calling-semantic-kernel1.png" alt="" style="width:600px;height:630px;"&gt;

&lt;h2 id="additional-resources"&gt;Additional Resources&lt;/h2&gt;
&lt;p&gt;For an in-depth understanding of Semantic Kernel tool calling, please review the following YouTube video: &lt;a target="_blank" href="https://youtu.be/0eM3CfHFM1c?si=Sr-imaYBBATTKuD9"&gt;Core GenAI Techniques - Function Calling&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;You can also explore the following DevExpress GitHub sample: &lt;a target="_blank" href="https://github.com/DevExpress-Examples/blazor-ai-chat-function-calling" title="DevExpress Blazor AI Chat — Implement Function/Tool Calling"&gt;DevExpress Blazor AI Chat — Implement Function/Tool Calling&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Wed, 09 Apr 2025 00:15:00 Z</pubDate>
      <dc:creator>Dmitry Tokmachev (DevExpress)</dc:creator>
    </item>
    <item>
      <guid isPermaLink="false">bd716303-653c-428d-8b8a-a7d998cde032:388227</guid>
      <link>https://community.devexpress.com/Blogs/aspnet/archive/2025/03/24/devexpress-blazor-ai-chat-ai-assistants-for-report-viewer-and-grid.aspx</link>
      <category domain="https://community.devexpress.com/Tags/AI">AI</category>
      <category domain="https://community.devexpress.com/Tags/Assistant">Assistant</category>
      <category domain="https://community.devexpress.com/Tags/Blazor">Blazor</category>
      <category domain="https://community.devexpress.com/Tags/Grid">Grid</category>
      <category domain="https://community.devexpress.com/Tags/Reporting">Reporting</category>
      <title>DevExpress Blazor AI Chat — AI Assistants for Report Viewer and Grid</title>
      <description>&lt;p&gt;In this post, I will detail how you can&amp;nbsp;integrate our Blazor &lt;code&gt;DxAIChat&lt;/code&gt; component in your project and provide Copilot-like AI assistants to your end-users.&lt;/p&gt;
&lt;p&gt;First, a quick word about this implementation...by incorporating the DevExpress AI-powered Chat component and associated natural language processing services, your DevExpress-powered Blazor app can efficiently filter/analyze/summarize data, generate document insights, ask contextual questions, and obtain answers related to reports, tables, and more. &lt;/p&gt;
&lt;img src="https://community.devexpress.com:443/blogs/aspnet/25.1/grid-report-assistants/blazor-grid-ai-assistant.png" alt="DevExpress Blazor Grid – AI Assistant" style="width:1578px;height:792px;"&gt;
&lt;h2 id="explore-the-github-example"&gt;Review our&amp;nbsp;GitHub Example&lt;/h2&gt;
&lt;p&gt;To help illustrate what&amp;#39;s possible, we published a working example on GitHub. This example&amp;nbsp;features the DevExpress Blazor Report Viewer and Grid control and includes a detailed README with implementation guidelines and practical usage scenarios:&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://github.com/DevExpress-Examples/blazor-grid-and-report-viewer-integrate-ai-assistant/tree/24.2.6%2B"&gt;Blazor Grid and Report Viewer — Incorporate an AI Assistant (Azure OpenAI) in your next DevExpress-powered Blazor app&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="expanding-ai-integration-to-winforms-and-wpf"&gt;Expanding AI Integration to WinForms and WPF&lt;/h2&gt;
&lt;p&gt;A similar AI assistant implementation can also be applied to WinForms and WPF-powered applications (apps&amp;nbsp;that support data export to Excel and/or PDF). If you are looking to introduce a Copilot-like assistant into your next great WinForms/WPF app, please refer to our Blazor &lt;code&gt;DxAIChat&lt;/code&gt; component implementation guide in the following example: &lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://github.com/DevExpress-Examples/devexpress-ai-chat-samples"&gt;Blazor AI Chat - How to add the DevExpress Blazor AI Chat component to your next Blazor, MAUI, WPF, and WinForms application&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.devexpress.com/subscriptions/i/24.2/24-2-blazor-new-ai-chat-multiple-platforms.png" alt="DevExpress AI Chat Controls"&gt;&lt;/p&gt;
&lt;h2&gt;Share Your Feedback&lt;/h2&gt;
&lt;p&gt;We appreciate your continued support and enthusiasm for DevExpress tools. If you have any questions or would like to discuss AI-related integration needs, please submit a support ticket via the &lt;a target="_blank" href="https://devexpress.com/ask"&gt;DevExpress Support Center&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Mon, 24 Mar 2025 03:35:00 Z</pubDate>
      <dc:creator>Dmitry Tokmachev (DevExpress)</dc:creator>
    </item>
  </channel>
</rss>