This 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 our WinForms 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.
Tip
You can migrate this example to .NET 8 or later. Review the following help topic for instructions: Migrate DevExpress-powered .NET Framework Apps to the Latest .NET Version.
- DevExpress WinForms Controls
GridControl
- Displays the conversation historyHtmlContentControl
- Renders styled messagesMemoEdit
- Captures user input
- DevExpress AI-powered Extensions
- HTML & CSS Styling: DevExpress HTML & CSS Support
- Visual Studio 2022 or later
- .NET Framework 4.6.2 or later
- DevExpress.AI.WinForms.HtmlChat Project - Implements the UI and associated logic
- DevExpress.AI.WinForms.HtmlChat.Demo Project - Hosts the chat control and registers the AI client.
Register the AI Chat client at application startup:
static void Main() {
AzureOpenAIClient azureOpenAIClient = new AzureOpenAIClient(AzureOpenAIEndpoint, AzureOpenAIKey, new AzureOpenAIClientOptions() {
Transport = new PromoteHttpStatusErrorsPipelineTransport()
});
IChatClient chatClient = azureOpenAIClient.GetChatClient("gpt-4o-mini").AsIChatClient();
var container = AIExtensionsContainerDesktop.Default;
container.RegisterChatClient(chatClient);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
Capture user input, update the message list, and retrieve a response from the LLM:
// A collection of chat messages bound to a GridControl.
BindingList<ChatMessage> messages = new BindingList<ChatMessage>();
async void TypingBox_ElementMouseClick(object sender, Utils.Html.DxHtmlElementMouseEventArgs e)
{
if (e.ElementId == "btnSend")
{
string message = messageEdit.Text;
messageEdit.BeginInvoke(new Action(() => messageEdit.Text = string.Empty));
await SendMessage(message);
}
if (e.ElementId == "btnRemove")
{
ClearMessages();
}
}
// Send a message and append a reply.
public async Task SendMessage(string userContent)
{
if (string.IsNullOrEmpty(userContent))
return;
IChatClient service = AIExtensionsContainerDesktop.Default.GetService<IChatClient>();
messages.Add(new ChatMessage(ChatRole.User, userContent));
messagesItemsView.MoveLast();
// Display an overlay form while waiting for the response.
AIOverlayForm form = new AIOverlayForm();
var cancellationTokenSource = new CancellationTokenSource();
form.ShowLoading(this, cancellationTokenSource);
try
{
ChatResponse chatResponse = await service.GetResponseAsync(messages, cancellationToken: cancellationTokenSource.Token);
messages.AddMessages(chatResponse);
messagesItemsView.MoveLast();
form.Close();
form.Dispose();
}
catch (Exception e)
{
form.ShowError(this, e.Message, true);
}
}
The example uses the Markdig Markdown processing library to convert Markdown text into HTML:
void OnQueryItemTemplate(object sender, QueryItemTemplateEventArgs e) {
var message = e.Row as ChatMessage;
if(message == null)
return;
// Apply a template based on the chat role (User or Assistant).
if(message.Role == ChatRole.User)
Styles.MyMessage.Apply(e.Template);
else
Styles.Message.Apply(e.Template);
// Convert Markdown to HTML.
string htmlString = Markdig.Markdown.ToHtml(message.Text);
e.Template.Template = e.Template.Template.Replace("${Text}", htmlString);
}
- DevExpress AI-powered Extensions for WinForms
- WinForms AI Chat Control for .NET
- HTML and CSS Support
(you will be redirected to DevExpress.com to submit your response)