Displaying a Custom Dialog

Problem

Navigation Extensions currently supports displaying a MessageDialog. However, MessageDialog cannot be styled and requires a bit more work to set the text of its actions.

Solution

Setting up a Generic Dialog Model

We can first create our own DialogInfo object that can hold whatever useful information we want to display:

public partial record DialogInfo
{
    public DialogInfo(string title, string content)
    {
        Title = title;
        Content = content;
    }

    public string Title { get; init; }
    public string Content { get; init; }
}

We can then create a generic dialog that will have its own DialogInfo:

public partial record GenericDialogModel(DialogInfo DialogInfo);

We should then create a GenericDialog.xaml file which will take care of the bindings. We will be able to re-use this ContentDialog throughout the app:

<ContentDialog x:Class="Chefs.Views.GenericDialog"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:local="using:Chefs.Presentation.Dialogs"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               Title="{Binding DialogInfo.Title}"
               Background="{ThemeResource SurfaceBrush}"
               CloseButtonText="Close"
               Content="{Binding DialogInfo.Content}"
               Style="{StaticResource MaterialContentDialogStyle}" />

Using the GenericDialogModel with the Uno Navigation Extension

For the navigation to work, we first have to add a ViewMap and RouteMap to our App.xaml.host.cs file. You can find more information about registering routes here.

public class App : Application
{
    ...

    private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
    {
        views.Register(
            /* other ViewMaps */,
            new ViewMap<GenericDialog, GenericDialogModel>(Data: new DataMap<DialogInfo>())
        );

        routes.Register(
            new RouteMap("", View: views.FindByViewModel<ShellModel>(),
                Nested: new RouteMap[]
                {
                    /* other RouteMaps */,
                    new RouteMap("Dialog", View: views.FindByView<GenericDialog>())
                }
            )
        );
    }
}

We add our own ShowDialog method to INavigatorExtensions:

public static class INavigatorExtensions
{
    public static Task<NavigationResponse?> ShowDialog(this INavigator navigator, object sender, DialogInfo dialogInfo, CancellationToken ct)
    {
        return navigator.NavigateDataAsync(sender, new DialogInfo(dialogInfo.Title, dialogInfo.Content), cancellation: ct);
    }
}

We are now ready to show a dialog with custom DialogInfo wherever we are using navigation. Here's an example where we show the user an error message in our dialog under a condition:

public partial class MyViewModel
{
    private readonly INavigator _navigator;

    public async ValueTask Submit(CancellationToken ct)
    {
        if (...)
        {
            var response = await ...
            await _navigator.NavigateBackWithResultAsync(this, data: response);
        }
        else
        {
            await _navigator.ShowDialog(this, new DialogInfo("Error", "Please write a cookbook name and select one recipe."), ct);
        }
    }
}

Source Code

Documentation