skip to Main Content

I’m getting started with WinUI3. Having no previous experience with UWP or WPF or even much C#, it’s tough going.

Today’s question is how to display a simple dialog at startup. Consider that we start with a simple app, as generated by Visual Studio. We have a MainWindow class defined in MainWindow.xaml.cs and its associated XAML (MainWindow.xaml). I believe the class is called a code-behind class.

So I want to do something as simple as display a dialog when the (desktop) app runs. It looks as though a ContentDialog is the way to go. But how to display it? As I understand it, I’m going to need to set the XamlRoot, so naively I tried this:

public MainWindow()
{
    this.InitializeComponent();
    DisplayDialog();
}

private async void DisplayDialog()
{
    var dlg = new ContentDialog();
    dlg.XamlRoot = this.Content.XamlRoot; // <-- set the XAML root here, but it's null
    dlg.Content = "Hello World";
    await dlg.ShowAsync();
}

This doesn’t work. When called, the main window’s XAML root is null and trying to show the dialog throws an exception:

enter image description here

How do I detect when it’s ok to use the main window’s XAML root? This issue seems to hint at an OnLoaded event, but I can’t find anything about OnLoaded events in WinUI. Did I miss something?

This only way I can get this to work is to hook into the window’s button and respond to its Loaded event, i.e.

public MainWindow()
{
    this.InitializeComponent();
    myButton.Loaded += MyButton_Loaded; // <-- hack
}

private void MyButton_Loaded(object sender, RoutedEventArgs e)
{
    DisplayDialog();
}

private async void DisplayDialog()
{
    var dlg = new ContentDialog();
    dlg.XamlRoot = this.Content.XamlRoot; // <-- this is non-null now!
    dlg.Content = "Hello World";
    await dlg.ShowAsync();
}

But this feels really dirty. I don’t even know if it’s guaranteed that the XamlRoot will be non-null just because a button has loaded. And anyway, latching onto the button load seems very much like a hack. It relies on there being a button for one thing!

So how should I achieve the simple task of putting a dialog on the screen when all I have is the main window?

All help very gratefully received. Please try to make any answers as newbie-friendly as possible.

2

Answers


  1. Unfortunately, the MainWindow has no Loaded events. What you can do is to work with a Page instead. So basically use the MainWindow as a "Window" and use Pages for your contents.

    MainPage.xaml.cs

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.Loaded += MainPage_Loaded;
        }
    
        private async void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            var dlg = new ContentDialog();
            dlg.XamlRoot = this.XamlRoot;
            dlg.Content = "Hello World";
            await dlg.ShowAsync();
        }
    }
    

    MainWindow.xaml

    <Window
        x:Class="ContentDialogs.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="using:ContentDialogs"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <local:MainPage />
    
    </Window>
    
    Login or Signup to reply.
  2. A WinUI Window is just an abstraction of each of the low-level window implementations used by supported (UWP and desktop) app models.

    The "trick" is to handle the Loaded event of the window’s root element. The default template includes a Grid root element. A "generic" solution could be implemented something like this:

    public MainWindow()
    {
        this.InitializeComponent();
        var root = this.Content as FrameworkElement;
        if (root != null)
            root.Loaded += async (s, e) => await DisplayDialog();
    
    }
    
    private async Task DisplayDialog()
    {
        var dlg = new ContentDialog();
        dlg.XamlRoot = this.Content.XamlRoot;
        dlg.Content = "Hello World";
        await dlg.ShowAsync();
    }
    

    Also tote that an async method, with the exception of event handlers, should return a Task or a Task<T> and be awaited by the caller.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search