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)
