Table of Contents
In a previous WinUI 3 project (WordleWinUI) I wanted a simple dialog box to display a message to the user. I was rather hoping for an equivalent to the Windows Forms (C#) MessageBox, which is easy to code:
string message = "Are you sure you want to do that, Dave?";
string caption = "Message Box Test";
DialogResult result = MessageBox.Show(message, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == System.Windows.Forms.DialogResult.Yes) {
// Open the airlock ...
}
And it produces a functional MessageBox:
This doesn't seem to exist in WinUI 3 (The MessageDialog class seems to be deprecated in WinUI). So I thought it would be a nice idea to develop a reusable widget that worked similarly to the Windows Forms Message Box. Furthermore, I was inspired by XamlBrewer. This sounded very similar to what I wanted and I thought it might be a good idea to port this to C++/WinRT as a reusable component. In the end, I simplified the DialogService to a single API call:
The client code is not much more involved than the C# code above:
hstring title = App::Window().Title();
auto result = co_await DialogServiceWRC::ModalView::MessageDialogAsync(
winrt::box_value<Windows::Foundation::IInspectable>(*this),
title,
L"Are you sure you want to do that, Dave?",
DialogServiceWRC::MessageBoxButtonType::YesNo,
DialogServiceWRC::MessageBoxIconType::Question);
auto dialogResult = winrt::unbox_value<Microsoft::UI::Xaml::Controls::ContentDialogResult>(result);
if (dialogResult == Microsoft::UI::Xaml::Controls::ContentDialogResult::Primary) {
// Open the airlock ...
}
There are three projects in the DialogService.sln solution file:
- DialogServiceWRC - this is a C++ Windows Runtime Component.
- DialogServiceProjection - this is a .NET project to generate the interop ('projection') and package it as a NuGet package.
- DialogServiceWinUI. - this is a standard WinUI 3.0 C++/WinRT Windows Desktop Application that consumes the component.
The DialogServiceWRC defines a simple API in ModalView.idl for calling the MessageDialog:
static Windows.Foundation.IAsyncOperation<IInspectable>
MessageDialogAsync(
IInspectable value,
String title,
String message,
MessageBoxButtonType buttons,
MessageBoxIconType icon
);
This API is mostly a simple wrapper around the WinUI 3 ContentDialog control (https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.contentdialog?view=windows-app-sdk-1.4). However, I wanted to be able to set the buttons and the icons. This involved creating some Xaml controls on-the-fly, and adding this to the ContentDialog Content property. In a previous application (WordleWinUI), the dialog service was part of the main application. The MessageDialogAsync call wrapped a call to the ContentDialog plus some custom XAML to display the MessageBox with the icons.
However, there were two issues when moving this code to be part of a reusable Windows Runtime Component:
-
The MIDL compiler complained about not knowing the types of
ContentDialogResult
(this is a simple enumint32
, and which we could work around by creating our own type), and more importantly theFrameworkElement
which is essential for determining theXamlRoot
. The header file with the correctly generated types was present. For some reason the MIDL compiler couldn't see it. The solution adopted was to pass both these types asIInspectable
and box and unbox them as required. So calling the API requires boxing and unboxing values. -
A more serious problem arose when I added the XAML page markup to represent the 'inner' message box of the content dialog. Again the MIDL compiler failed with an unhelpful error message. After various failed attempts, the workaround I adopted was to define the MessageBox content entirely in C++/WinRT code. Luckily there was no need for this to be a separate
runtimeclass
as the content is just passed to the ContentDialog container. There was not much markup and all the binding (the icon resource and the message text) could be handled in code. Phew.
The DialogServiceWinUI consumes the DialogServiceWRC. This involves generating the NuGet package (using the projection project), then adding a project reference to this solution and including the generated header file in pch.h: #include <winrt/DialogServiceWRC.h>
.
The DialogServiceWinUI exercises the MessageDialog functionality with a simple button in the Content page.
The DialogServiceWinUI also demonstrates a number of additional features
- support for light/dark mode via a toggle button on the menu.
- navigation view with 2 pages and a settings page. This provides a simple extensible framework
- support for application wide and user settings via a simple runtimeclass.
- resizing the app window
- setting the application icon for the main window
- Visual Studio 2022
- C++20
- C++/WinRT
- WinUI 3.0
The project can be downloaded from the GitHub repository in the usual way.
See the open issues for a full list of proposed features (and known issues).
Distributed under the GPL-3.0 License. See LICENSE.md
for more information.
Adam Gladstone - (https://www.linkedin.com/in/adam-gladstone-b6458b156/)
Project Link: https://github.com/Adam-Gladstone/DialogService
Helpful resources