windowsforms-405218-ai-powered-extensions-ai-chat-control.md
Note
The DevExpress AI Chat Control (AIChatControl) can only be used in Windows Forms applications that target the .NET 8+ framework.
The AI Chat Control (AIChatControl) allows you to incorporate an interactive, Copilot-inspired chat-based UI within your WinForms application. This control leverages BlazorWebView to reuse the DevExpress Blazor AI Chat component (DxAIChat).
Tip
The following example extends the DevExpress WinForms Chat Client App demo. It creates a Copilot-inspired, AI-powered chat interface without using BlazorWebView. The example uses ‘native’ DevExpress UI controls (such as the GridControl, MemoEdit, and HtmlContentControl). The app targets .NET Framework 4.6.2 or later and integrates with the Azure OpenAI service to support conversational engagement, Markdown rendering, and structured message display within a fully native WinForms environment.
View Example: WinForms Chat for .NET Framework
DevExpress.AIIntegration.WinForms.ChatDevExpress.Win.Design (enables design-time features for DevExpress UI controls)Update the project SDK to Microsoft.NET.Sdk.Razor:
<Project Sdk="Microsoft.NET.Sdk.Razor">
See the following help topic for information on required NuGet packages and system requirements: Register an AI Client.
The following code snippet registers the Azure OpenAI client:
using Microsoft.Extensions.AI;
using DevExpress.AIIntegration;
internal static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
IChatClient azureChatClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(AzureOpenAIEndpoint),
new System.ClientModel.ApiKeyCredential(AzureOpenAIKey))
.GetChatClient(ModelId).AsIChatClient();
AIExtensionsContainerDesktop.Default.RegisterChatClient(azureChatClient);
Application.Run(new Form1());
}
static string AzureOpenAIEndpoint { get { return Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT"); } }
static string AzureOpenAIKey { get { return Environment.GetEnvironmentVariable("AZURE_OPENAI_APIKEY"); } }
static string ModelId { get { return "gpt-4o-mini"; } }
}
Imports Microsoft.Extensions.AI
Imports DevExpress.AIIntegration
Friend Module Program
<STAThread>
Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
' Register Azure.OpenAI
Dim azureChatClient As IChatClient = New Azure.AI.OpenAI.AzureOpenAIClient(New Uri(AzureOpenAIEndpoint),
New System.ClientModel.ApiKeyCredential(AzureOpenAIKey)).
GetChatClient(ModelId).AsIChatClient()
AIExtensionsContainerDesktop.Default.RegisterChatClient(azureChatClient)
Application.Run(New Form1())
End Sub
Private ReadOnly Property AzureOpenAIEndpoint() As String
Get
Return Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
End Get
End Property
Private ReadOnly Property AzureOpenAIKey() As String
Get
Return Environment.GetEnvironmentVariable("AZURE_OPENAI_APIKEY")
End Get
End Property
Private ReadOnly Property ModelId As String
Get
Return "gpt-4o-mini"
End Get
End Property
End Module
Drop the AIChatControl from the toolbox onto a Form.
Note
The AI Chat Control does not support design-time rendering.
The following code snippet creates the AIChatControl with default settings:
using DevExpress.AIIntegration.WinForms.Chat;
public partial class Chat : XtraForm {
public Chat() {
InitializeComponent();
AIChatControl chat = new AIChatControl(){
Name = "aiChatControl1",
Dock = DockStyle.Fill
};
this.Controls.Add(chat);
}
}
Imports DevExpress.AIIntegration.WinForms.Chat
Partial Public Class Chat
Inherits XtraForm
Public Sub New()
InitializeComponent()
Dim chat As New AIChatControl() With {
.Name = "aiChatControl1",
.Dock = DockStyle.Fill
}
Me.Controls.Add(chat)
End Sub
End Class
Use the following AI Chat project templates to create a chat application that integrates the AI Chat Control:
AI Chat ApplicationCreates a WinForms chat app that integrates the AI Chat Control.AI Chat (RAG) ApplicationCreates a desktop WinForms application with the AI Chat Control and built-in Retrieval-Augmented Generation (RAG) for document-grounded conversations.
Both templates support .NET 8 / .NET 9 and integrate the DevExpress MCP Server for DevExpress-specific guidance.
See the following help topic for more information: AI Chat Project Templates.
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 streaming:
aiChatControl1.UseStreaming = DevExpress.Utils.DefaultBoolean.True;
aiChatControl1.UseStreaming = DevExpress.Utils.DefaultBoolean.True
Play the following animation to see the result:
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.
using DevExpress.AIIntegration.Blazor.Chat;
using DevExpress.AIIntegration.Blazor.Chat.WebView;
using Ganss.Xss;
using Markdig;
var sanitizer;
public Chat() {
InitializeComponent();
sanitizer = new HtmlSanitizer();
aiChatControl1.ContentFormat = ResponseContentFormat.Markdown
aiChatControl1.MarkdownConvert += AiChatControl1_MarkdownConvert
}
void AiChatControl1_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;
}
Imports DevExpress.AIIntegration.Blazor.Chat
Imports DevExpress.AIIntegration.Blazor.Chat.WebView
Imports Ganss.Xss
Imports Markdig
Public Class Chat
Private sanitizer As HtmlSanitizer
Public Sub New()
InitializeComponent()
sanitizer = New HtmlSanitizer()
aiChatControl1.ContentFormat = ResponseContentFormat.Markdown
AddHandler aiChatControl1.MarkdownConvert, AddressOf AiChatControl1_MarkdownConvert
End Sub
Private Sub AiChatControl1_MarkdownConvert(sender As Object, e As AIChatControlMarkdownConvertEventArgs)
' Convert Markdown to HTML.
Dim html As String = Markdown.ToHtml(e.MarkdownText)
' WARNING: The AI agent's content may be untrusted.
' Developers must sanitize all HTML before rendering to prevent XSS attacks.
Dim safeHtml As String = sanitizer.Sanitize(html)
' Assign sanitized HTML for rendering
e.HtmlText = DirectCast(safeHtml, Microsoft.AspNetCore.Components.MarkupString)
End Sub
End Class
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:
Enable the FileUploadEnabled property to allow users to attach files.
Configure additional settings based on your project requirements (the maximum file size, allowed file types/extensions, the maximum number of files that users can attach to a message):
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 DevExpress AI Chat Control can display prompt suggestions.
Use the SetPromptSuggestions method to supply relevant prompts:
using DevExpress.AIIntegration.Blazor.Chat.WebView;
aiChatControl1.SetPromptSuggestions(new List<PromptSuggestion>(){
new PromptSuggestion(
title: "Birthday Wish",
text: "A warm and cheerful birthday greeting message.",
prompt: "Write a heartfelt birthday message for a close friend."),
new PromptSuggestion(
"Thank You Note",
"A polite thank you note to express gratitude.",
"Compose a short thank you note to a colleague who helped with a project.")
});
The AI Chat Control automatically inserts a selected prompt suggestion into the chat prompt box so users can edit it before sending. Activate the PromptSuggestion.SendOnClick option to send a prompt suggestion to the AI service immediately after it is clicked.
Use the EmptyStateText property to specify the message displayed when a chat has yet to start:
aiChatControl1.EmptyStateText = "AI Assistant is ready to answer your questions.";
aiChatControl1.EmptyStateText = "AI Assistant is ready to answer your questions."
The following screenshot shows the result:
The AIChatControl supports appearance customization through Razor-based templates. You can customize chat messages, prompt suggestions, and the text displayed when the chat has no message history.
Use the following methods to customize chat message container (including paddings and inner content alignment) and content:
SetMessageTemplate(RenderFragment<BlazorChatMessage> template)SetMessageContentTemplate(RenderFragment<BlazorChatMessage> template)Note
The Message Template property takes priority over the Message Content Template if both templates are specified.
The following example displays a custom button within chat messages. When a user clicks the button, a message box appears.
using DevExpress.XtraEditors;
using Microsoft.AspNetCore.Components;
using using DevExpress.AIIntegration.Blazor.Chat;
public Form1() {
InitializeComponent();
aiChatControl1.SetMessageTemplate(message => builder => {
builder.OpenComponent<Message>(0);
builder.AddAttribute(1, "message", message);
builder.AddAttribute(2, "OnButtonClick", EventCallback.Factory.Create<BlazorChatMessage>(this, CustomButtonClick));
builder.CloseComponent();
});
}
void CustomButtonClick(BlazorChatMessage message) {
XtraMessageBox.Show($"Message: {message.Content}");
}
The Message.razor file:
@using Microsoft.AspNetCore.Components.Web
@using DevExpress.AIIntegration.Blazor.Chat
<style>
.my-custom-button {
background-color: #007acc;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
}
.my-custom-button:hover {
background-color: #005fa3;
}
</style>
<div class="@GetMessageClasses(message)">
@if (message.Typing) {
<span>Loading...</span>
}
else {
<div class="demo-chat-content">
@message.Content
<button class="my-custom-button" @onclick="OnButtonClicked">Click me</button>
</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);
}
}
Use the SetPromptSuggestionContentTemplate(RenderFragment<IPromptSuggestion> template) method to customize prompt suggestions.
Use the SetEmptyMessageAreaTemplate(RenderFragment template) method to customize the UI when the chat has no message history.
aiChatControl1.SetEmptyMessageAreaTemplate(builder => {
builder.OpenComponent<EmptyArea>(0);
builder.CloseComponent();
});
The EmptyArea.razor file:
<style>
.demo-chat-ui-description {
font-weight: bold;
font-size: 20px;
text-align: center;
}
</style>
<div class="demo-chat-ui-description">
AI Assistant is ready to answer your questions.
</div>
The AIChatControl can display the header. The header contains a customizable chat title and a Clear Chat button (removes all messages from the conversation history except system messages).
Use the AIChatControl.ShowHeader option to display the chat header. The AIChatControl.HeaderText property specifies the chat title.
aiChatControl1.ShowHeader = DevExpress.Utils.DefaultBoolean.True;
aiChatControl1.HeaderText = "AI Assistant";
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.
aiChatControl1.AllowInputResize = DevExpress.Utils.DefaultBoolean.True;
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:
using DevExpress.AIIntegration.Blazor.Chat.WebView;
using Microsoft.Extensions.AI;
public Chat() {
InitializeComponent();
aiChatControl1.MessageSent += AiChatControl1_MessageSent;
}
async void AiChatControl1_MessageSent(object sender, AIChatControlMessageSentEventArgs e) {
await e.SendMessage($"Processed: {e.Content}", ChatRole.Assistant);
}
Imports DevExpress.AIIntegration.Blazor.Chat.WebView
Imports Microsoft.Extensions.AI
Public Sub New()
InitializeComponent()
AddHandler aiChatControl1.MessageSent, AddressOf AiChatControl1_MessageSent
End Sub
Async Sub AiChatControl1_MessageSent(ByVal sender As Object, ByVal e As AIChatControlMessageSentEventArgs)
Await e.SendMessage($"Processed: {e.Content}", ChatRole.Assistant)
End Sub
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 Chat : XtraForm {
List<BlazorChatMessage> chatHistory;
public Chat() {
InitializeComponent();
buttonSave.Click += ButtonSave_Click;
buttonLoad.Click += ButtonLoad_Click;
}
void ButtonSave_Click(object sender, EventArgs e) {
chatHistory = (List<BlazorChatMessage>)aiChatControl1.SaveMessages();
}
void ButtonLoad_Click(object sender, EventArgs e) {
if(chatHistory != null)
aiChatControl1.LoadMessages(chatHistory);
}
}
Partial Public Class Chat
Inherits XtraForm
Private chatHistory As List(Of BlazorChatMessage)
Public Sub New()
InitializeComponent()
AddHandler buttonSave.Click, AddressOf ButtonSave_Click
AddHandler buttonLoad.Click, AddressOf ButtonLoad_Click
End Sub
Private Sub ButtonSave_Click(ByVal sender As Object, ByVal e As EventArgs)
chatHistory = CType(aiChatControl1.SaveMessages(), List(Of BlazorChatMessage))
End Sub
Private Sub ButtonLoad_Click(ByVal sender As Object, ByVal e As EventArgs)
aiChatControl1.LoadMessages(chatHistory)
End Sub
End Class
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: Resources.
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 WinForms 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 WinForms 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 WinForms 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 WinForms application to operating systems other than Windows 11: Distribute your app and the WebView2 Runtime.
See Also