wpf-405434-ai-powered-extensions-ai-chat-control.md
Note
The DevExpress AI Chat Control (AIChatControl) can only be used in WPF applications that target the .NET 8+ and newer frameworks.
The AI Chat Control allows you to embed an interactive, Copilot-inspired chat interface in your WPF application. It uses BlazorWebView to host the DevExpress Blazor AI Chat component (DxAIChat).
To use the AIChatControl:
Install DevExpress.AIIntegration.Wpf.Chat.See the following help topics for information on how to obtain the DevExpress NuGet Feed and install DevExpress NuGet packages:
Change Project SDKUpdate the project SDK to Microsoft.NET.Sdk.Razor:
<Project Sdk="Microsoft.NET.Sdk.Razor">
Register AI Client
See the following help topic for information on required NuGet packages and system requirements: Register an AI Client.
The following code snippet registers an Azure OpenAI client at application startup within the AIExtensionsContainerDesktop container:
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 = "Win11Light";
// For example, ModelId = "gpt-4o-mini"
IChatClient azureChatClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(AzureOpenAIEndpoint),
new System.ClientModel.ApiKeyCredential(AzureOpenAIKey)).GetChatClient(ModelId).AsIChatClient();
AIExtensionsContainerDesktop.Default.RegisterChatClient(azureChatClient);
}
}
}
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 = "Win11Light"
' For example, ModelId = "gpt-4o-mini"
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
Note
The AI Chat Control does not support design-time rendering.
The following code snippet creates the AIChatControl with default settings:
<dx:ThemedWindow
x:Class="DXApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxaichat="http://schemas.devexpress.com/winfx/2008/xaml/aichat"
Title="MainWindow" Height="800" Width="1000">
<Grid>
<dxaichat:AIChatControl
x:Name="aiChatControl"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10">
</dxaichat:AIChatControl>
</Grid>
</dx:ThemedWindow>
Use AI Chat Application and AI Chat (RAG) Application CLI project 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.
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 integrates the DevExpress AI Chat Control. You can optionally enable DevExpress-specific guidance by adding an mcp.json configuration file to integrate the DevExpress MCP Server.
Supported AI providers:
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:
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 setting to activate this feature:
<dxaichat:AIChatControl
x:Name="aiChatControl"
UseStreaming="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10">
</dxaichat:AIChatControl>
Play the following animation to see the result:
To enable Markdown message rendering:
ContentFormat property to Markdown to receive responses as Markdown.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 Markdown processing library to convert Markdown text into HTML.
<dxaichat:AIChatControl
x:Name="aiChatControl"
UseStreaming="True"
ContentFormat="Markdown"
MarkdownConvert="AiChatControl_MarkdownConvert"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10">
</dxaichat:AIChatControl>
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'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;
}
}
}
The following screenshot shows the result:
Users can now attach files directly to their chat messages. The AI analyzes document content (such as text files, PDFs, images) and delivers more context-aware responses.
To activate file upload:
FileUploadEnabled property to allow users to attach files.xmlns:dxaichat="http://schemas.devexpress.com/winfx/2008/xaml/aichat"
xmlns:chat="clr-namespace:DevExpress.AIIntegration.Blazor.Chat;assembly=DevExpress.AIIntegration.Blazor.Chat.v25.1"
xmlns:system="clr-namespace:System;assembly=mscorlib"
<dxaichat:AIChatControl
x:Name="aiChatControl"
FileUploadEnabled="True"
UseStreaming="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10">
<dxaichat:AIChatControl.FileUploadSettings>
<chat:DxAIChatFileUploadSettings MaxFileSize="5000000" MaxFileCount="5">
<chat:DxAIChatFileUploadSettings.AllowedFileExtensions>
.png
.pdf
.txt
</chat:DxAIChatFileUploadSettings.AllowedFileExtensions>
<chat:DxAIChatFileUploadSettings.FileTypeFilter>
image/png
application/pdf
text/plain
</chat:DxAIChatFileUploadSettings.FileTypeFilter>
</chat:DxAIChatFileUploadSettings>
</dxaichat:AIChatControl.FileUploadSettings>
</dxaichat:AIChatControl>
Tip
See the following article for more information on MIME types (FileTypeFilter): Common Media Types.
To help users get started or explore new possibilities, the AI Chat Control can display prompt suggestions.
Use the SetPromptSuggestions method to supply intelligent suggestions:
<dxaichat:AIChatControl>
<dxaichat:AIChatControl.PromptSuggestions>
<chat:DxAIChatPromptSuggestion
Title="Birthday Wish"
Text="A warm and cheerful birthday greeting message."
PromptMessage="Write a heartfelt birthday message for a close friend." />
<chat:DxAIChatPromptSuggestion
Title="Thank You Note"
Text="A polite thank you note to express gratitude."
PromptMessage="Compose a short thank you note to a colleague who helped with a project." />
</dxaichat:AIChatControl.PromptSuggestions>
</dxaichat:AIChatControl>
To manually process messages sent to an AI service, handle the MessageSent 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:
<dxaichat:AIChatControl x:Name="aiChatControl"
MessageSent="AiChatControl_MessageSent"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10">
</dxaichat:AIChatControl>
async void AiChatControl_MessageSent(object sender, AIChatControlMessageSentEventArgs e) {
await e.SendMessage($"Processed: {e.Content}", ChatRole.Assistant);
}
Use the following methods to manage chat history:
SaveMessages – Returns an IEnumerable<ChatMessage> collection of messages.LoadMessages – 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:
public partial class MainWindow : ThemedWindow {
List<BlazorChatMessage> chatHistory;
public MainWindow() {
InitializeComponent();
}
void ButtonSave_Click(object sender, EventArgs e) {
chatHistory = (List<BlazorChatMessage>)aiChatControl.SaveMessages();
}
void ButtonLoad_Click(object sender, EventArgs e) {
if(chatHistory != null)
aiChatControl.LoadMessages(chatHistory);
}
}
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.
Use the following properties to customize the chat message container (including paddings and inner content alignment) or the message content:
AIChatControl.MessageTemplateAIChatControl.MessageContentTemplateNote
The AIChatControl.MessageTemplate property takes priority over the AIChatControl.MessageContentTemplate property if both templates are specified.
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.
using DevExpress.AIIntegration.Blazor.Chat;
using DevExpress.Mvvm.UI;
using Microsoft.AspNetCore.Components;
using System.Windows;
namespace DXChatApplication {
public partial class MainWindow {
RenderFragment<BlazorChatMessage> MyMessageTemplate;
readonly NotificationService notificationService;
public MainWindow() {
InitializeComponent();
// Configure DevExpress NotificationService.
notificationService = new NotificationService() {
ApplicationId = "DXChatApplication",
ApplicationName = "DXChatApplication",
PredefinedNotificationTemplate = NotificationTemplate.LongText
};
MyMessageTemplate = message => builder => {
builder.OpenComponent<Message>(0);
builder.AddAttribute(1, "message", message);
builder.AddAttribute(2, "OnButtonClick", EventCallback.Factory.Create<BlazorChatMessage>(this, CustomButtonClick));
builder.CloseComponent();
};
aiChatControl.MessageTemplate = MyMessageTemplate;
}
void CustomButtonClick(BlazorChatMessage message) {
Dispatcher.Invoke(() => {
try { Clipboard.SetText(message.Content ?? string.Empty); } catch { }
var notification = notificationService.CreatePredefinedNotification("Message Copied to Clipboard", message.Content, null);
_ = notification.ShowAsync();
});
}
}
}
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);
}
}
You can modify the message or empty area displayed when a chat has yet to start.
Use one of the following properties:
EmptyStateText - Specifies the empty area message.EmptyStateTemplate - Specifies a custom template (RenderFragment) to customize the entire area.Note
If both EmptyStateText and EmptyStateTemplate properties are set, the chat control uses the EmptyStateTemplate and ignores the EmptyStateText.
<dxaichat:AIChatControl x:Name="aiChatControl"
EmptyStateText="AI Assistant is ready to answer your questions."
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10">
</dxaichat:AIChatControl>
using Microsoft.AspNetCore.Components;
public MainWindow() {
InitializeComponent();
aiChatControl.EmptyStateTemplate = MyEmptyStateTemplate;
}
RenderFragment MyEmptyStateTemplate = builder => {
builder.OpenComponent<EmptyArea>(0);
builder.CloseComponent();
};
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>
Use the AIChatControl.ErrorMessageBackground property to specify the background color of error messages:
<dxaichat:AIChatControl x:Name="aiChatControl"
ErrorMessageBackground="LightCoral"
Margin="10">
</dxaichat:AIChatControl>
The AIChatControl can display the header. The header contains a customizable chat title and the Clear Chat button (removes all messages from the conversation history, except for system messages).
Use the AIChatControl.ShowHeader option to display the chat header. The AIChatControl.HeaderText property specifies the chat title.
Enable the AIChatControl.AllowResizeInput option to allow users 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.
The AIChatControl can access external or dynamically generated data through resources. A resource is an instance of the AIChatResource class that supplies text or binary content to the AI model at request time.
Resources extend the 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.
See the following help topic for more information: Chat Resources.
AI Tool Calling API integrates application logic with natural language interaction. It allows the 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.
See the following help topics for more information:
When integrating the AI Chat Control with the OpenAI Assistant API, you can configure it to retain and reference a specific context (for example, a text file or a PDF document). By providing a supplementary document as a context source, the assistant is primed with background information. OpenAI automatically parses the document and searches through it to retrieve relevant content to better respond to user queries.
See the following help topic for more information: Chat with Your Own Data.
The WPF AIChatControl supports multiple AI services in a single application, which enables you to:
See the following help topic for more information: Manage Multiple Chat Clients.
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. However, earlier versions of Windows and Windows Server may not have it pre-installed. To ensure compatibility,refer to the following help topic for information on distributing the WebView2 runtime with your WPF application to operating systems other than Windows 11: Distribute your app and the WebView2 Runtime.