# AI Chat Control | WPF Controls | DevExpress Documentation

Note

The DevExpress AI Chat Control (`AIChatControl`) can only be used in WPF applications that target .NET 8+ and newer frameworks.

The AI Chat Control allows you to embed an interactive, Copilot-inspired chat interface in your WPF application. The control uses `BlazorWebView` to host the DevExpress Blazor AI Chat component ([DxAIChat](/Blazor/DevExpress.AIIntegration.Blazor.Chat.DxAIChat)). 

![WPF AI Chat Control, DevExpress](/WPF/images/aichatcontrol.png)

[Run Demo: AI Chat Control](dxdemo://Wpf/DXAI/MainDemo/AIChatPromptSuggestionsModule) [View Example: AI Chat](https://github.com/DevExpress-Examples/devexpress-ai-chat-samples)

## Getting Started

To use the `AIChatControl`:

- Install `DevExpress.AIIntegration.Wpf.Chat`.

    - Refer to the following help topics for more information:
- [How to Install DevExpress Products](/GeneralInformation/116042/installation/install-products)
- [Install Packages from NuGet.org: Visual Studio, VS Code, and JetBrains Rider](https://go.devexpress.com/DevExpress_NuGet_Documentation.aspx)

- Change Project SDK

    - Update the project SDK to `Microsoft.NET.Sdk.Razor`:
- .csproj

<section id="tabpanel_pnymqbTxWI_tabid-xaml" role="tabpanel" data-tab="tabid-xaml">
<pre><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk.Razor&quot;&gt;
</code></pre></section>

- Register the AI Client

    - See the following help topic for information on required NuGet packages and system requirements: [Register an AI Client](/WPF/405223/ai-powered-extensions).

The following code snippet registers an Azure OpenAI client at application startup within the [AIExtensionsContainerDesktop](/CoreLibraries/DevExpress.AIIntegration.AIExtensionsContainerDesktop) container:

- C#
- VB.NET

<section id="tabpanel_-uorw8kNaQ_tabid-csharp" role="tabpanel" data-tab="tabid-csharp">
<pre><code data-code-links="{&quot;/ (DevExpress.AIIntegration)(?:;|$)/&quot;:&quot;/CoreLibraries/DevExpress.AIIntegration&quot;,&quot;/ (DevExpress.Xpf.Core)(?:;|$)/&quot;:&quot;/WPF/DevExpress.Xpf.Core&quot;,&quot;/ (System)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/system&quot;,&quot;/ (System.Windows)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/system.windows&quot;}" data-highlight-lines="[[19,22]]" class="lang-csharp">using Azure.AI.OpenAI;
using DevExpress.AIIntegration;
using DevExpress.Xpf.Core;
using Microsoft.Extensions.AI;
using System;
using System.Windows;

namespace AIAssistantApp {
    public partial class App : Application {
        static App() {
            CompatibilitySettings.UseLightweightThemes = true;
        }

        protected override void OnStartup(StartupEventArgs e) {
            base.OnStartup(e);
            ApplicationThemeHelper.ApplicationThemeName = &quot;Win11Light&quot;;

            // For example, ModelId = &quot;gpt-4o-mini&quot;
            IChatClient azureChatClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(AzureOpenAIEndpoint),
                new System.ClientModel.ApiKeyCredential(AzureOpenAIKey)).GetChatClient(ModelId).AsIChatClient();
            AIExtensionsContainerDesktop.Default.RegisterChatClient(azureChatClient);
        }
    }
}
</code></pre></section>
<section id="tabpanel_-uorw8kNaQ_tabid-vb" role="tabpanel" data-tab="tabid-vb" aria-hidden="true" hidden="hidden">
<pre><code data-code-links="{&quot;/ (DevExpress.AIIntegration)(?:;|$)/&quot;:&quot;/CoreLibraries/DevExpress.AIIntegration&quot;,&quot;/ (DevExpress.Xpf.Core)(?:;|$)/&quot;:&quot;/WPF/DevExpress.Xpf.Core&quot;,&quot;/ (System)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/system&quot;,&quot;/ (System.Windows)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/system.windows&quot;}" class="lang-vb">Imports Azure.AI.OpenAI
Imports DevExpress.AIIntegration
Imports DevExpress.Xpf.Core
Imports Microsoft.Extensions.AI
Imports System
Imports System.Windows

Namespace AIAssistantApp
    Partial Public Class App
        Inherits Application

        Shared Sub New()
            CompatibilitySettings.UseLightweightThemes = True
        End Sub

        Protected Overrides Sub OnStartup(ByVal e As StartupEventArgs)
            MyBase.OnStartup(e)
            ApplicationThemeHelper.ApplicationThemeName = &quot;Win11Light&quot;

            &#39; For example, ModelId = &quot;gpt-4o-mini&quot;
            Dim azureChatClient As IChatClient = New AzureOpenAIClient(New Uri(AzureOpenAIEndpoint), 
                New System.ClientModel.ApiKeyCredential(AzureOpenAIKey)).GetChatClient(ModelId).AsIChatClient()
            AIExtensionsContainerDesktop.Default.RegisterChatClient(azureChatClient)
        End Sub
    End Class
End Namespace
</code></pre></section>

## Create the AI Chat Control

Note

The AI Chat Control does not support design-time rendering.

The following code snippet creates the `AIChatControl` with default settings:

- XAML

<section id="tabpanel_pnymqbTxWI-1_tabid-xaml-1" role="tabpanel" data-tab="tabid-xaml-1">
<pre><code data-highlight-lines="[[6],[9]]" class="lang-xaml">&lt;dx:ThemedWindow 
    x:Class=&quot;DXApplication3.MainWindow&quot;
    xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:dx=&quot;http://schemas.devexpress.com/winfx/2008/xaml/core&quot;
    xmlns:dxaichat=&quot;http://schemas.devexpress.com/winfx/2008/xaml/aichat&quot;
    Title=&quot;MainWindow&quot; Height=&quot;800&quot; Width=&quot;1000&quot;&gt;
    &lt;Grid&gt;
        &lt;dxaichat:AIChatControl
            x:Name=&quot;aiChatControl&quot;
            HorizontalAlignment=&quot;Stretch&quot; 
            VerticalAlignment=&quot;Stretch&quot; 
            Margin=&quot;10&quot;&gt;
        &lt;/dxaichat:AIChatControl&gt;
    &lt;/Grid&gt;
&lt;/dx:ThemedWindow&gt;
</code></pre></section>

### CLI Project Templates

Use AI Chat Application and AI Chat (RAG) Application [CLI project templates](/WPF/405220/dotnet-core-support/project-template-kit#cli-templates) to create a chat application that integrates the AI Chat Control. Both templates support .NET 8 / .NET 9 / .NET10 and integrate the **DevExpress MCP Server** for DevExpress-specific guidance.

#### AI Chat Application

- The **AI Chat Application** template creates a WPF chat app that integrates the AI Chat Control.

    - `dx.wpf.aichat`

**Parameter**: `--ai-provider` | **Values**: `azureopenai`, `openai`, `ollama`

Creates a WPF chat app that uses the DevExpress AI Chat Control. 

If you plan to use an AI coding assistant in your IDE, we recommend that you setup the **DevExpress MCP Server** (adds DevExpress-specific guidance to assistant responses). Create a configuration file (*mcp.json*) and specify server settings.

Supported AI providers:

- Azure OpenAI
- OpenAI
- Ollama

#### AI Chat (RAG) Application

- The **AI Chat (RAG) Application** template creates a desktop WPF application with the AI Chat Control and built-in Retrieval-Augmented Generation (RAG) for document-grounded conversations.

    - `dx.wpf.aichatrag`

**Parameter**: `--ai-provider` | **Values**: `azureopenai`, `openai`, `ollama`

**Parameter**: `--vectorstore` | **Values**: `sqlite`, `inmemory`

Creates a desktop WPF application with the DevExpress AI Chat Control and built-in Retrieval-Augmented Generation (RAG) for document-grounded conversations:

- Uses local document data for context-aware answers.
- Scans user Documents folders and indexes PDF, DOCX, TXT, RTF, and HTML files.
- Extracts, embeds, and semantically searches document text.
- Stores vectors in In-Memory (rebuilds each run) or SQLite (persistent database).
- Merges retrieved content with user prompts to improve accuracy.
- Optionally integrates the **DevExpress MCP Server** for DevExpress-specific guidance.

## Streaming

The AI Chat Control can display responses from the AI assistant as they are generated in a natural, conversational flow (rather than waiting for the entire message to complete before showing it to the user). Enable the [UseStreaming](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.UseStreaming) setting to activate this feature:

- XAML

<section id="tabpanel_pnymqbTxWI-2_tabid-xaml-2" role="tabpanel" data-tab="tabid-xaml-2">
<pre><code data-highlight-lines="[[3]]" class="lang-xaml">&lt;dxaichat:AIChatControl
    x:Name=&quot;aiChatControl&quot;
    UseStreaming=&quot;True&quot;
    HorizontalAlignment=&quot;Stretch&quot; 
    VerticalAlignment=&quot;Stretch&quot; 
    Margin=&quot;10&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

Play the following animation to see the result:

![Streaming - AI Chat Control for WPF, DevExpress](/WPF/images/aichatcontrol-streaming.gif)

## Markdown Message Rendering

To enable Markdown message rendering:

1. Set the [ContentFormat](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.ContentFormat) property to `Markdown` to receive responses formatted using Markdown.
2. Handle the [MarkdownConvert](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MarkdownConvert) event to convert markdown text into HTML and make responses more readable, structured, and visually appealing.

Warning

Always sanitize AI-generated content before rendering it in the UI.

The following example enables Markdown message rendering. The example uses the [Markdig](https://www.nuget.org/packages/Markdig/) Markdown processing library to convert Markdown text into HTML.

- XAML

<section id="tabpanel_pnymqbTxWI-3_tabid-xaml-3" role="tabpanel" data-tab="tabid-xaml-3">
<pre><code data-highlight-lines="[[4]]" class="lang-xaml">&lt;dxaichat:AIChatControl
    x:Name=&quot;aiChatControl&quot;
    UseStreaming=&quot;True&quot;
    ContentFormat=&quot;Markdown&quot;
    MarkdownConvert=&quot;AiChatControl_MarkdownConvert&quot;
    HorizontalAlignment=&quot;Stretch&quot; 
    VerticalAlignment=&quot;Stretch&quot; 
    Margin=&quot;10&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

- C#

<section id="tabpanel_pnymqbTxWI-4_tabid-csharp-1" role="tabpanel" data-tab="tabid-csharp-1">
<pre><code data-code-links="{&quot;/ (DevExpress.Xpf.Core)(?:;|$)/&quot;:&quot;/WPF/DevExpress.Xpf.Core&quot;,&quot;/ (Microsoft.AspNetCore.Components)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.components&quot;}" class="lang-csharp">using DevExpress.Xpf.Core;
using DevExpress.AIIntegration.Blazor.Chat.WebView;
using Microsoft.AspNetCore.Components;
using Ganss.Xss;
using Markdig;

namespace DXApplication {
    var sanitizer;

    public partial class MainWindow : ThemedWindow {
        public MainWindow() {
            InitializeComponent();
            sanitizer = new HtmlSanitizer();
        }

        void AiChatControl_MarkdownConvert(object sender, AIChatControlMarkdownConvertEventArgs e) {
            // Convert Markdown to HTML.
            string html = Markdown.ToHtml(e.MarkdownText);

            // WARNING: The AI agent&#39;s content may be untrusted. 
            // Developers must sanitize all HTML before rendering to prevent XSS attacks.
            string safeHtml = sanitizer.Sanitize(html);

            // Assign sanitized HTML for rendering.
            e.HtmlText = (MarkupString)safeHtml;
        }
    }
}
</code></pre></section>

The following screenshot demonstrates the result:

![Markdown Message Rendering](/WPF/images/aichatcontrol-markdown.png)

## File Attachments

Users can now attach files directly to their chat messages. AI analyzes document content (such as text files, PDFs, images) and delivers more context-aware responses.

![File Upload - WPF AI Chat Control, DevExpress](/WPF/images/25-1-wpf-aichatcontrol-file-upload.png)

[Run Demo: File Attachments — AI Chat](dxdemo://Wpf/DXAI/MainDemo/AIChatFileAttachmentsModule)

To activate file upload:

1. Enable the [FileUploadEnabled](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.FileUploadEnabled) property to allow users to attach files.
2. Configure additional settings based on your project requirements (maximum file size, allowed file types/extensions, maximum number of files that users can attach to a message).

- XAML

<section id="tabpanel_pnymqbTxWI-5_tabid-xaml-4" role="tabpanel" data-tab="tabid-xaml-4">
<pre><code data-highlight-lines="[[7]]" class="lang-xaml">xmlns:dxaichat=&quot;http://schemas.devexpress.com/winfx/2008/xaml/aichat&quot;
xmlns:chat=&quot;clr-namespace:DevExpress.AIIntegration.Blazor.Chat;assembly=DevExpress.AIIntegration.Blazor.Chat.v25.1&quot;
xmlns:system=&quot;clr-namespace:System;assembly=mscorlib&quot;

&lt;dxaichat:AIChatControl
    x:Name=&quot;aiChatControl&quot;
    FileUploadEnabled=&quot;True&quot;
    UseStreaming=&quot;True&quot;
    HorizontalAlignment=&quot;Stretch&quot; 
    VerticalAlignment=&quot;Stretch&quot; 
    Margin=&quot;10&quot;&gt;
    &lt;dxaichat:AIChatControl.FileUploadSettings&gt;
        &lt;chat:DxAIChatFileUploadSettings MaxFileSize=&quot;5000000&quot; MaxFileCount=&quot;5&quot;&gt;
            &lt;chat:DxAIChatFileUploadSettings.AllowedFileExtensions&gt;
                &lt;system:String&gt;.png&lt;/system:String&gt;
                &lt;system:String&gt;.pdf&lt;/system:String&gt;
                &lt;system:String&gt;.txt&lt;/system:String&gt;
            &lt;/chat:DxAIChatFileUploadSettings.AllowedFileExtensions&gt;
            &lt;chat:DxAIChatFileUploadSettings.FileTypeFilter&gt;
                &lt;system:String&gt;image/png&lt;/system:String&gt;
                &lt;system:String&gt;application/pdf&lt;/system:String&gt;
                &lt;system:String&gt;text/plain&lt;/system:String&gt;
            &lt;/chat:DxAIChatFileUploadSettings.FileTypeFilter&gt;
        &lt;/chat:DxAIChatFileUploadSettings&gt;
    &lt;/dxaichat:AIChatControl.FileUploadSettings&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

Tip

See the following article for more information on MIME types (`FileTypeFilter`): [Common Media Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types/Common_types).

## Prompt Suggestions

To help users get started or explore new possibilities, the AI Chat Control can display prompt suggestions.

![Prompt Suggestions - WPF AI Chat Control](/WPF/images/25-1-wpf-aichatcontrol-prompt-suggestions.png)

[Run Demo: Prompt Suggestions — AI Chat](dxdemo://Wpf/DXAI/MainDemo/AIChatPromptSuggestionsModule)

Use the [PromptSuggestions](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.PromptSuggestions) property to supply intelligent suggestions:

- XAML

<section id="tabpanel_pnymqbTxWI-6_tabid-xaml-5" role="tabpanel" data-tab="tabid-xaml-5">
<pre><code class="lang-xaml">&lt;dxaichat:AIChatControl&gt;
    &lt;dxaichat:AIChatControl.PromptSuggestions&gt;
        &lt;chat:DxAIChatPromptSuggestion
            Title=&quot;Birthday Wish&quot;
            Text=&quot;A warm and cheerful birthday greeting message.&quot;
            PromptMessage=&quot;Write a heartfelt birthday message for a close friend.&quot; /&gt;
        &lt;chat:DxAIChatPromptSuggestion
            Title=&quot;Thank You Note&quot;
            Text=&quot;A polite thank you note to express gratitude.&quot;
            PromptMessage=&quot;Compose a short thank you note to a colleague who helped with a project.&quot; /&gt;
    &lt;/dxaichat:AIChatControl.PromptSuggestions&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

## Handle Chat Messages

To manually process messages sent to an AI service, handle the [MessageSending](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageSending) event. For example, you can manually call the AI client or service of choice, and return its responses to the chat. The following example adds responses to user questions:

- XAML

<section id="tabpanel_pnymqbTxWI-7_tabid-xaml-6" role="tabpanel" data-tab="tabid-xaml-6">
<pre><code data-highlight-lines="[[2]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                MessageSending=&quot;AiChatControl_MessageSending&quot;
                HorizontalAlignment=&quot;Stretch&quot; 
                VerticalAlignment=&quot;Stretch&quot; 
                Margin=&quot;10&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

- C#

<section id="tabpanel_pnymqbTxWI-8_tabid-csharp-2" role="tabpanel" data-tab="tabid-csharp-2">
<pre><code class="lang-csharp">async void AiChatControl_MessageSending(object sender, AIChatControlMessageSendingEventArgs e) {
    e.Cancel = true; 
    await e.Chat.SendMessageAsync($&quot;Processed: {e.Content}&quot;, ChatRole.User);
}
</code></pre></section>

Note

Set the `e.Cancel` event parameter to `true` to block automatic message delivery and use the [AIChatControl.SendMessageAsync](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.SendMessageAsync.overloads) method to send the message to the AI service manually.

## Conversation History

### Stateless and Stateful Chat Services

The AI Chat Control supports both stateless and stateful AI services. This distinction affects how conversation history is managed and how the control behaves when users clear chat content.

- Stateless Services

    - Stateless services do not retain conversation context between requests. To preserve context, the AI Chat Control sends the full message history with each user request. In this mode:

- The service does not store conversation state.
- The AI Chat Control maintains conversation history.
- Clearing chat content removes all context.

- Stateful Services

    - Stateful services maintain conversation history on the server. These services return a `ConversationId` value that identifies the current conversation session. When the AI Chat Control detects `ConversationId`, it automatically switches to stateful mode behavior:

- The AI Chat Control stores the conversation identifier.
- Subsequent requests send only new messages instead of the entire chat history.
- The AI service reconstructs conversation context automatically.

### Clear Chat Behavior for Stateful Services

For stateful services, clearing messages in the user interface does not automatically remove the server-side conversation state. To ensure that the **Clear** button starts a completely new conversation, the AI Chat Control reinitializes `IChatResponseProvider`.

Register `IChatResponseProvider` as a transient dependency. This ensures that the AI Chat Control creates a new provider instance when chat content is cleared.

```
serviceCollection.AddTransient<IChatResponseProvider>(
    new AzureOpenAIClient("endpoint", "apiKey")
    .GetResponsesClient()
    .AsIChatClient("modelId")
    .AsIChatResponseProvider());
```

### Append Message in Chat History

Call the [AppendMessageAsync](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.AppendMessageAsync%28String--ChatRole--List-IAIChatMessageContextItem-%29) method to add a message to chat history without sending it to the AI service. Use this method in the `MessageSending` event to append a system prompt or supplemental context.

The following code snippet adds a system instruction to the chat:

- C#

<section id="tabpanel_pnymqbTxWI-9_tabid-csharp-3" role="tabpanel" data-tab="tabid-csharp-3">
<pre><code class="lang-csharp">async void AiChatControl_MessageSending(object sender,  AIChatControlMessageSendingEventArgs e)
{
    await e.Chat.AppendMessageAsync(&quot;Translate message to Spanish&quot;, ChatRole.System);
}
</code></pre></section>

### Save and Load Chat History

Use the following methods to manage chat history:

- [SaveMessages](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.SaveMessages) – Returns an `IEnumerable<ChatMessage>` collection of messages.
- [LoadMessages](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.LoadMessages%28System.Collections.Generic.IEnumerable-DevExpress.AIIntegration.Blazor.Chat.BlazorChatMessage-%29) – Loads messages from the specified `IEnumerable<ChatMessage>` collection to the AI Chat Control and refreshes the control.

The following example saves/loads chat history when the user clicks the Save/Load button:

- C#

<section id="tabpanel_pnymqbTxWI-10_tabid-csharp-4" role="tabpanel" data-tab="tabid-csharp-4">
<pre><code data-highlight-lines="[[9],[14]]" class="lang-csharp">public partial class MainWindow : ThemedWindow {
    List&lt;BlazorChatMessage&gt; chatHistory;

    public MainWindow() {
        InitializeComponent();
    }

    void ButtonSave_Click(object sender, EventArgs e) {
        chatHistory = (List&lt;BlazorChatMessage&gt;)aiChatControl.SaveMessages();
    }

    void ButtonLoad_Click(object sender, EventArgs e) {
        if(chatHistory != null)
            aiChatControl.LoadMessages(chatHistory);
    }
}
</code></pre></section>

## Display and Hide the Loading Indicator

Use the following methods to display a loading indicator while the application performs a long-running operation, such as preparing additional context before sending a request to the AI service:

- [ShowLoadingIndicatorAsync(string)](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.ShowLoadingIndicatorAsync%28System.String%29) – Displays the loading indicator with an optional caption.
- [HideLoadingIndicatorAsync()](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.HideLoadingIndicatorAsync) – Hides the loading indicator.

The following example displays the indicator with a custom caption when the user sends a message and hides it after the operation completes:

![Loading Indicator - WPF AIChatControl, DevExpress](/WPF/images/ai-agent-loading-Indicator.png)

- C#

<section id="tabpanel_pnymqbTxWI-11_tabid-csharp-20" role="tabpanel" data-tab="tabid-csharp-20">
<pre><code data-highlight-lines="[[3],[6]]" class="lang-csharp">async void AiChatControl_MessageSending(object sender, AIChatControlMessageSendingEventArgs e)
{
    await aiChatControl.ShowLoadingIndicatorAsync(&quot;Working on it...&quot;);
    // Perform a long-running operation (load data, call an external API, etc.).
    await PrepareContextAsync();
    await aiChatControl.HideLoadingIndicatorAsync();
}
</code></pre></section>

## Customize Chat UI and Appearance

The `AIChatControl` supports appearance customization through Razor-based templates. You can customize chat messages, errors, the empty message area, and text displayed when the chat has no message history.

### Message Templates

Use the following properties to customize the chat message container (including paddings and inner content alignment) or message content:

- [AIChatControl.MessageTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageTemplate)
- [AIChatControl.MessageContentTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageContentTemplate)

Note

- The [AIChatControl.MessageTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageTemplate) property takes priority over the [AIChatControl.MessageContentTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageContentTemplate) property if both templates are specified.
- The [MessageContentTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageContentTemplate) does not support messages when [ContentFormat](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.ContentFormat) is set to `Markdown`. When using [MessageContentTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.MessageContentTemplate), implement markdown rendering logic within your custom template.

The following example displays a copy icon within chat messages. When a user clicks the icon, the message text is copied to the Clipboard, and a confirmation toast notification is displayed.

![Message Template - WPF AIChatControl, DevExpress](/WPF/images/wpf-aichatcontrol-message-template.png)

- C#

<section id="tabpanel_pnymqbTxWI-12_tabid-csharp-5" role="tabpanel" data-tab="tabid-csharp-5">
<pre><code data-code-links="{&quot;/ (DevExpress.AIIntegration.Blazor.Chat)(?:;|$)/&quot;:&quot;/Blazor/DevExpress.AIIntegration.Blazor.Chat&quot;,&quot;/ (DevExpress.Mvvm.UI)(?:;|$)/&quot;:&quot;/WPF/DevExpress.Mvvm.UI&quot;,&quot;/ (Microsoft.AspNetCore.Components)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.components&quot;,&quot;/ (System.Windows)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/system.windows&quot;}" class="lang-csharp">using DevExpress.AIIntegration.Blazor.Chat;
using DevExpress.Mvvm.UI;
using Microsoft.AspNetCore.Components;
using System.Windows;

namespace DXChatApplication {
    public partial class MainWindow {
        RenderFragment&lt;BlazorChatMessage&gt; MyMessageTemplate;
        readonly NotificationService notificationService;

        public MainWindow() {
            InitializeComponent();

            // Configure DevExpress NotificationService.
            notificationService = new NotificationService() {
                ApplicationId = &quot;DXChatApplication&quot;,
                ApplicationName = &quot;DXChatApplication&quot;,
                PredefinedNotificationTemplate = NotificationTemplate.LongText
            };

            MyMessageTemplate = message =&gt; builder =&gt; {
                builder.OpenComponent&lt;Message&gt;(0);
                builder.AddAttribute(1, &quot;message&quot;, message);
                builder.AddAttribute(2, &quot;OnButtonClick&quot;, EventCallback.Factory.Create&lt;BlazorChatMessage&gt;(this, CustomButtonClick));
                builder.CloseComponent();
            };

            aiChatControl.MessageTemplate = MyMessageTemplate;
        }

        void CustomButtonClick(BlazorChatMessage message) {
            Dispatcher.Invoke(() =&gt; {
                try { Clipboard.SetText(message.Content ?? string.Empty); } catch { }
                var notification = notificationService.CreatePredefinedNotification(&quot;Message Copied to Clipboard&quot;, message.Content, null);
                _ = notification.ShowAsync();
            });
        }
    }
}
</code></pre></section>

The *Message.razor* file:

```
@using Microsoft.AspNetCore.Components.Web
@using DevExpress.AIIntegration.Blazor.Chat

<style>
    .demo-chat-content {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 8px;
        flex-direction: row;
    }

    .copy-icon {
        cursor: pointer;
        font-size: 16px;
        color: #555;
        transition: color 0.2s ease-in-out;
    }
</style>

<div class="@GetMessageClasses(message)">
    @if (message.Typing)
    {
        <span>Loading...</span>
    }
    else
    {
        <div class="demo-chat-content">
            <span>@message.Content</span>
            <span class="copy-icon" title="Copy" @onclick="OnButtonClicked">📋</span>
        </div>
    }
</div>

@code {
    [Parameter]
    public BlazorChatMessage message { get; set; }

    [Parameter]
    public EventCallback<BlazorChatMessage> OnButtonClick { get; set; }

    string GetMessageClasses(BlazorChatMessage message) {
        switch (message.Role) {
            case ChatMessageRole.Assistant:
                return "dxbl-chatui-message dxbl-chatui-message-assistant";
            case ChatMessageRole.User:
                return "dxbl-chatui-message dxbl-chatui-message-user";
            case ChatMessageRole.Error:
                return "dxbl-chatui-message dxbl-chatui-message-error";
            default:
                return "dxbl-chatui-message";
        }
    }

    async Task OnButtonClicked() {
        if (OnButtonClick.HasDelegate)
            await OnButtonClick.InvokeAsync(message);
    }
}
```

### Empty Area Text and Template

You can modify the empty area text or template displayed when a chat has is started.

Use one of the following properties:

- [EmptyStateText](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.EmptyStateText) - Specifies the empty area message.
- [EmptyStateTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.EmptyStateTemplate) - Specifies a template ([RenderFragment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.renderfragment?view=aspnetcore-9.0)) to customize the empty area.

Note

If both [EmptyStateText](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.EmptyStateText) and [EmptyStateTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.EmptyStateTemplate) properties are set, the chat control uses the [EmptyStateTemplate](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.EmptyStateTemplate) and ignores the [EmptyStateText](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.EmptyStateText).

#### Customize Empty Area Text

![Custom Empty State Text - WPF AI Chat Control, DevExpress](/WPF/images/ai-chat-empty-text.png)

- XAML

<section id="tabpanel_pnymqbTxWI-13_tabid-xaml-7" role="tabpanel" data-tab="tabid-xaml-7">
<pre><code data-highlight-lines="[[2]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                EmptyStateText=&quot;AI Assistant is ready to answer your questions.&quot;
                HorizontalAlignment=&quot;Stretch&quot; 
                VerticalAlignment=&quot;Stretch&quot; 
                Margin=&quot;10&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

#### Customize Empty Area Appearance

![Customize Empty Area - WPF AI Chat, DevExpress](/WPF/images/aichatcontrol-custom-empty-area.png)

- C#

<section id="tabpanel_pnymqbTxWI-14_tabid-csharp-6" role="tabpanel" data-tab="tabid-csharp-6">
<pre><code data-code-links="{&quot;/ (Microsoft.AspNetCore.Components)(?:;|$)/&quot;:&quot;https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.components&quot;}" data-highlight-lines="[[5]]" class="lang-csharp">using Microsoft.AspNetCore.Components;

public MainWindow() {
    InitializeComponent();
    aiChatControl.EmptyStateTemplate = MyEmptyStateTemplate;
}

RenderFragment MyEmptyStateTemplate = builder =&gt; {
    builder.OpenComponent&lt;EmptyArea&gt;(0);
    builder.CloseComponent();
};
</code></pre></section>

The *EmptyArea.razor* file:

```
<style>
    .emptyarea-box {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 100%;
        color: #666;
        font-family: sans-serif;
        text-align: center;
        padding: 40px;
    }
    .emptyarea-icon {
        font-size: 48px;
        margin-bottom: 16px;
    }
    .emptyarea-title {
        font-size: 18px;
        font-weight: 500;
    }
    .emptyarea-description {
        font-size: 14px;
        margin-top: 8px;
    }
</style>

<div class="emptyarea-box">
    <div class="emptyarea-icon">
        💬
    </div>
    <div class="emptyarea-title">
        No messages yet
    </div>
    <div class="emptyarea-description">
        Start the conversation by sending a message.
    </div>
</div>
```

### User and Assistant Message Background

Use the following properties to specify the background color for user and assistant messages:

- [AIChatControl.UserMessageBackground](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.UserMessageBackground) – specifies the background color for user messages.
- [AIChatControl.AssistantMessageBackground](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.AssistantMessageBackground) – specifies the background color for assistant messages.

- XAML

<section id="tabpanel_pnymqbTxWI-15_tabid-xaml-11" role="tabpanel" data-tab="tabid-xaml-11">
<pre><code data-highlight-lines="[[2],[3]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                        UserMessageBackground=&quot;#FF7AB8FF&quot;
                        AssistantMessageBackground=&quot;#FFB8E6C1&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

![User and Assistant Message Background - WPF AIChatControl, DevExpress](/WPF/images/ai-chat-message-background.png)

### Error Message Background

Use the [AIChatControl.ErrorMessageBackground](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.ErrorMessageBackground) property to specify the background color of error messages:

- XAML

<section id="tabpanel_pnymqbTxWI-16_tabid-xaml-8" role="tabpanel" data-tab="tabid-xaml-8">
<pre><code data-highlight-lines="[[2]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                ErrorMessageBackground=&quot;LightCoral&quot;
                Margin=&quot;10&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

![Error Message Background - WPF AIChatControl, DevExpress](/WPF/images/wpf-aichatcontrol-error-background.png)

### Title and Clear Chat Button

The `AIChatControl` can display a header. This header contains a customizable chat title and the **Clear Chat** button (that removes all messages from conversation history, except for system messages).

![Title and Clear Chat Button - WPF AIChatControl, DevExpress](/WPF/images/wpf-aichatcontrol-display-header.png)

Use the [AIChatControl.ShowHeader](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.ShowHeader) option to display the chat header. The [AIChatControl.HeaderText](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.HeaderText) property specifies the chat title.

### Input Area Placeholder

Use the [InputBoxNullText](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.InputBoxNullText) property to display the prompt text in the AI Chat input box when it is empty.

If there is no property specified, the component uses the `AIChat_InputPlaceholder` localization string as the prompt text.

![Input Area Placeholder - WPF AIChatControl, DevExpress](/WPF/images/wpf-aichatcontrol-input-placeholder.png)

- XAML

<section id="tabpanel_pnymqbTxWI-17_tabid-xaml-9" role="tabpanel" data-tab="tabid-xaml-9">
<pre><code data-highlight-lines="[[2]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                InputBoxNullText=&quot;Ask me something...&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

### Input Area Background

Use the [AIChatControl.InputBackground](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.InputBackground) property to specify the background color of the input area (the prompt input box and the surrounding submit area):

- XAML

<section id="tabpanel_pnymqbTxWI-18_tabid-xaml-12" role="tabpanel" data-tab="tabid-xaml-12">
<pre><code data-highlight-lines="[[2]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                        InputBackground=&quot;#FFFFF0B3&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

![Input Area Background - WPF AIChatControl, DevExpress](/WPF/images/ai-chat-input-background.png)

### Resize Input Area

Enable the [AIChatControl.AllowResizeInput](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.AllowResizeInput) option to allow users to resize the input area. Users can drag the top edge up to enlarge the input area or down to display a more detailed chat history.

![Resize Input Area - WPF AIChatControl, DevExpress](/WPF/images/wpf-ai-chat-control-resize-input-area.gif)

### Change Border Appearance

Use `BorderBrush` and `BorderThickness` properties to customize chat control border appearance. To round chat control corners, specify the [CornerRadius](/WPF/DevExpress.AIIntegration.Wpf.Chat.AIChatControl.CornerRadius) property.

![Change Border Appearance - WPF AIChatControl, DevExpress](/WPF/images/wpf-aichatcontrol-change-border-appearance.png)

- XAML

<section id="tabpanel_pnymqbTxWI-19_tabid-xaml-10" role="tabpanel" data-tab="tabid-xaml-10">
<pre><code data-highlight-lines="[[2,4]]" class="lang-xaml">&lt;dxaichat:AIChatControl x:Name=&quot;aiChatControl&quot;
                BorderBrush=&quot;ForestGreen&quot;
                BorderThickness=&quot;5&quot;
                CornerRadius=&quot;20&quot;&gt;
&lt;/dxaichat:AIChatControl&gt;
</code></pre></section>

## Resources

The `AIChatControl` can access external or dynamically generated data through resources. A resource is an instance of the [AIChatResource](/Blazor/DevExpress.AIIntegration.Blazor.Chat.AIChatResource) class that supplies text or binary content to the AI model at request time.

Resources extend chat context with additional input (for example, local documents, logs, or images). The AI model uses this data to generate more accurate and context-aware responses.

After you assign resources, the `AIChatControl` displays the “Attach Context” (+) button. Users can select one or more resources to include in the chat request.

![Resources - WPF AI Chat Control, DevExpress](/WPF/images/ai-chat-resources.png)

[Run Demo: Resources — AI Chat](dxdemo://Wpf/DXAI/MainDemo/AIChatResourcesModule)

See the following help topic for additional information: [Chat Resources](/WPF/405613/ai-powered-extensions/ai-chat-control/resources).

## Tool Calling

[AI Tool Calling API](/CoreLibraries/405585/ai-integration/ai-tool-calling) integrates application logic with natural language interaction. It allows AI to analyze requests, select appropriate tools, resolve target instances, and invoke application methods at runtime in response to user prompts. Developers expose functionality as AI tools by annotating methods with metadata attributes. Each tool describes its purpose, input parameters, and (optionally) the target object on which it operates.

![Tool Calling - WPF AI Chat Control, DevExpress](/WPF/images/wpf-aichatcontrol-grid-tool-calling.gif)

[Run Demo: AI Tool Calling](dxdemo://Wpf/DXAI/MainDemo/AIToolsModule)

See the following help topics for additional information:

- [AI Tool Calling](/CoreLibraries/405585/ai-integration/ai-tool-calling)
- [AIToolsBehavior](/WPF/DevExpress.AIIntegration.Wpf.AIToolsBehavior)

## Create an Assistant That Chats Using Your Own Data

When integrating the AI Chat Control with AI Assistant API (for example, the [OpenAI Responses API](https://openai.com/index/new-tools-for-building-agents/) or [Azure AI Projects](https://learn.microsoft.com/en-us/azure/foundry/quickstarts/get-started-code?tabs=csharp)), you can configure the control to work with external data sources (for example, text files or PDF documents).

Refer to the following help topic for additional information: [Chat with Your Own Data](/WPF/405606/ai-powered-extensions/ai-chat-control/chat-with-your-own-data).

Warning

[OpenAI Assistants API will be deprecated in August 2026](https://learn.microsoft.com/en-us/answers/questions/5571874/openai-assistants-api-will-be-deprecated-in-august).

## Manage Multiple Chat Client Services

The WPF AIChatControl supports multiple AI services in a single application that enable you to:

- Run several independent chat UIs side by side powered by different AI services.
- Use one chat UI and dynamically switch between AI services or AI agents.

See the following help topic for additional information: [Manage Multiple Chat Clients](/WPF/405607/ai-powered-extensions/ai-chat-control/manage-multiple-chat-clients).

## Troubleshooting

### Deploy to Windows Server

When deploying WPF applications with the AI Chat Control to Windows Server or earlier versions of Windows, you may encounter the following error:

Warning

**Microsoft.Web.WebView2.Core.WebView2RuntimeNotFoundException**: “Could not find a compatible WebView2 Runtime installation to host WebViews.”

The WPF AI Chat Control leverages `BlazorWebView` to reuse the DevExpress Blazor `DxAIChat` component. This integration requires the WebView2 runtime to be installed on the target machine.

Windows 11 includes WebView2. Earlier versions of Windows and Windows Server may not have it preinstalled. To ensure compatibility, see [Distribute your app and the WebView2 Runtime](https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution?tabs=dotnetcsharp) for information on how to distribute the WebView2 Runtime with your WPF application on operating systems other than Windows 11.

### Airspace Issue in .NET 9 and Earlier: WPF Controls are Overlapped by the AI Chat Control

In .NET 9 and earlier, WPF popups, dialogs, flyouts, tooltips, menus, dock panels, and other overlay UI elements are always invisible when displayed over the AI Chat Control.

This rendering issue occurs because the AI Chat Control hosts the DevExpress Blazor `DxAIChat` component in `BlazorWebView`, which in turn relies on an HWND-hosted `WebView2` control. The resulting native window appears above the WPF visual tree and overlaps any WPF content rendered in the same area.

#### Solution: Upgrade to .NET 10

The *Airspace issue* no longer occurs in .NET 10 because `Microsoft.AspNetCore.Components.WebView.Wpf` uses [WebView2CompositionControl](https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.wpf.webview2compositioncontrol) instead of the HWND-hosted `WebView2` control. This composition-based control integrates properly with the WPF visual tree and allows overlapping WPF UI elements to be displayed on top of the AI Chat Control.

To avoid the *Airspace issue*:

1. Update the target framework to .NET 10.
2. Update the `Microsoft.AspNetCore.Components.WebView.Wpf` package to `10.0.x` or newer.

- .csproj

<section id="tabpanel_pnymqbTxWI-20_tabid-csproj-airspace" role="tabpanel" data-tab="tabid-csproj-airspace">
<pre><code class="lang-xml">&lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net10.0-windows&lt;/TargetFramework&gt;
&lt;/PropertyGroup&gt;

&lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.Components.WebView.Wpf&quot; Version=&quot;10.0.*&quot; /&gt;
&lt;/ItemGroup&gt;
</code></pre></section>

Refer to the following help topics for additional information:

- [WebView2CompositionControl Specification](https://github.com/MicrosoftEdge/WebView2Feedback/blob/main/specs/WPF_WebView2CompositionControl.md)
- [WebView2CompositionControl API Reference](https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.wpf.webview2compositioncontrol)
- [WPF and HwndHost Descendant Interoperation Limitations](/WPF/7551/controls-and-libraries/layout-management/dock-windows/wpf-and-winforms-interoperation-limitations)