winui-3winui

In WinUI3, how can I put a TeachingTip in a ContentDialog?


The code below does not show a teaching tip-does anyone know how to make one show?

<Window
    x:Class="WinUI3App.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="using:WinUI3App"
    xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
    mc:Ignorable="d"
    Title="WinUI3App" Height="450" Width="800">

    <Grid>
        <Button Content="Open Dialog" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click"/>
    </Grid>
</Window>
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace WinUI3App
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            ContentDialog dialog = new()
            {
                Title = "Sample Content Dialog",
                CloseButtonText = "Close",
                XamlRoot = (sender as Button).XamlRoot
            };

            TextBlock text = new() { Text = "Hello world" };

            TeachingTip teachingTip = new()
            {
                Content = "This is a sample teaching tip.",
                Title = "Sample Teaching Tip",
                Target = text,
            };

            dialog.Content = new StackPanel()
            {
                Children =
                {
                    text,
                    teachingTip
                }
            };

            dialog.Opened += (s, _) => teachingTip.IsOpen = true;
            dialog.Closed += (s, _) => teachingTip.IsOpen = false;

            await dialog.ShowAsync();
        }
    }
}


Solution

  • I guess you can't show a ContentDialog and a TeachingTip at the same time. Let me show you another way to do this by creating a custom dialog based on a Window:

    CustomDialog.cs

    using Microsoft.UI.Xaml;
    using System.Threading.Tasks;
    using WinUIEx;
    
    namespace App1;
    
    public enum CustomDialogResult
    {
        Cancel,
        OK,
    }
    
    public partial class CustomDialog : WindowEx
    {
        private TaskCompletionSource<CustomDialogResult>? _taskCompletionSource;
    
        public CustomDialog()
        {
            this.InitializeComponent();
            this.IsAlwaysOnTop = true;
            this.CenterOnScreen();
            this.SetWindowStyle(WindowStyle.Caption);
        }
        public new object? Content { get; set; }
    
        public Task<CustomDialogResult> ShowAsync()
        {
            this.ContentPresenter.Content = Content;
            _taskCompletionSource = new();
            this.Activate();
            return _taskCompletionSource.Task;
        }
    
        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            _taskCompletionSource?.SetResult(CustomDialogResult.OK);
            this.Close();
        }
    
        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            _taskCompletionSource?.SetResult(CustomDialogResult.Cancel);
            this.Close();
        }
    }
    

    CustomDialog.xaml.cs

    <ex:WindowEx
        x:Class="App1.CustomDialog"
        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:ex="using:WinUIEx"
        xmlns:local="using:App1"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Width="300"
        Height="200"
        mc:Ignorable="d">
    
        <Grid
            RowDefinitions="*,Auto">
            <ContentPresenter
                x:Name="ContentPresenter"
                Grid.Row="0" />
            <Grid
                Grid.Row="1"
                ColumnDefinitions="*,*">
                <Button
                    Grid.Column="0"
                    Click="OKButton_Click"
                    Content="OK" />
                <Button
                    Grid.Column="1"
                    Click="CancelButton_Click"
                    Content="Cancel" />
            </Grid>
        </Grid>
    </ex:WindowEx>
    

    then use it like this:

    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }
    
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
    
            TextBlock text = new() { Text = "Hello world" };
    
            TeachingTip teachingTip = new()
            {
                Content = "This is a sample teaching tip.",
                Title = "Sample Teaching Tip",
                Target = text,
                IsOpen = true,
            };
    
            CustomDialog dialog = new();
            dialog.Content = new StackPanel()
            {
                Children =
                {
                    text,
                    teachingTip
                }
            };
    
            CustomDialogResult result = await dialog.ShowAsync();
        }
    }
    

    NOTE:

    I'm using the WinUIEx NuGet package here but you should be able to do this without it.