Skip to content

Adam-Gladstone/DialogService

Repository files navigation


Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Roadmap
  5. License
  6. Contact
  7. Acknowledgments

About The Project

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:

Windows Forms 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:

WinUI MessageBox

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 ...      
    }

Project structure

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.

DialogServiceWRC

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:

  1. The MIDL compiler complained about not knowing the types of ContentDialogResult (this is a simple enum int32, and which we could work around by creating our own type), and more importantly the FrameworkElement which is essential for determining the XamlRoot. 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 as IInspectable and box and unbox them as required. So calling the API requires boxing and unboxing values.

  2. 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.

DialogServiceWinUI

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.

WinUI MessageBox

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

(back to top)

Built With

  • Visual Studio 2022
  • C++20
  • C++/WinRT
  • WinUI 3.0

(back to top)

Getting Started

The project can be downloaded from the GitHub repository in the usual way.

Prerequisites

Installation

(back to top)

Usage

(back to top)

Roadmap

See the open issues for a full list of proposed features (and known issues).

(back to top)

License

Distributed under the GPL-3.0 License. See LICENSE.md for more information.

(back to top)

Contact

Adam Gladstone - (https://www.linkedin.com/in/adam-gladstone-b6458b156/)

Project Link: https://github.com/Adam-Gladstone/DialogService

(back to top)

Acknowledgments

Helpful resources

(back to top)

Issues GPL-3 License LinkedIn

Releases

No releases published

Packages

No packages published