Search code examples
c#winui-3winuidialogresultcontentdialog

WinUI 3 Custom Dialog Result for Content Dialogs


I'm trying to create a password content dialog, but I don't want to use the built-in dialog result as it seems limited. For example, I want the user to be able to press the Enter key as a substitute for clicking the primary button, and I cannot seem to get a result from the dialog if I close the dialog when someone presses Enter.

I've tried mimicking the answer here WinUI 3: Change ContentDialog Size Programmatically While ContentDialog is Open for creating a custom dialog result, but using a content dialog instead of a window. I wouldn't want to use a window for such a small dialog. I'd prefer the built-in content dialog.

My code is below, but I'm receiving a stack overflow error on the line: _taskCompletionSource = new();

Essentially, I want control over the dialog result for a content dialog, especially when closing the dialog when the Enter key is pressed. So, any solutions that achieve this would be appreciated, including fixing the approach in the code below.

public sealed partial class PasswordDia : ContentDialog
{
    protected TaskCompletionSource<ContDialogResult>? _taskCompletionSource;
    public static ContDialogResult DialogResult = ContDialogResult.None;

    public enum ContDialogResult
    {
        None,
        Cancel,
        OK,
    }


    public PasswordDia()
    {
        this.InitializeComponent();
        this.IncorrectPinTextBlock.Visibility = Visibility.Collapsed;
        this.Closed += ContentDialog_Closed;
    }

    private void ContentDialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs e)
    {
        _taskCompletionSource?.SetResult(DialogResult);
    }

    public Task<ContDialogResult> ShowAsync()
    {
        _taskCompletionSource = new();
        this.ShowAsync();

        return _taskCompletionSource.Task;
    }

    private async void Ok_Click(object sender, RoutedEventArgs e)
    {
        if (this.AdminPasswordBox.Password == ConfigurationManager.AppSettings["AdminPassword"].ToString())
        {
            DialogResult = ContDialogResult.OK;
            this.Hide();
        }
        else
        {
            this.IncorrectPinTextBlock.Visibility = Visibility.Visible;
        }
    }

    private void Cancel_Click(object sender, RoutedEventArgs e)
    {
        DialogResult = ContDialogResult.Cancel;
        this.Hide();
    }

}

Solution

  • IMHO, you don't need TaskCompletionSource for this. Here's a simple example:

    public sealed partial class PasswordDialog : ContentDialog
    {
        public PasswordDialog() : base()
        {
            StackPanel content = new();
            UserIdControl = new TextBox();
            PasswordControl = new PasswordBox();
            content.Children.Add(UserIdControl);
            content.Children.Add(PasswordControl);
            this.Content = content;
            this.Closed += ContentDialog_Closed;
        }
    
        public enum ContDialogResult
        {
            None,
            Cancel,
            OK,
        }
    
        public ContDialogResult DialogResult { get; private set; } = ContDialogResult.None;
    
        public string UserId { get => UserIdControl.Text; }
    
        public string Password { get => PasswordControl.Password; }
    
        private TextBox UserIdControl { get; set; }
    
        private PasswordBox PasswordControl { get; set; }
    
        private void ContentDialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs e)
        {
            if (e.Result == ContentDialogResult.Primary &&
                PasswordControl.Password == "1234")
            {
                DialogResult = ContDialogResult.OK;
                return;
            }
    
            DialogResult = ContDialogResult.Cancel;
        }
    }
    
    var dialog = new PasswordDialog()
    {
        Title = "Admin Password",
        PrimaryButtonText = "OK",
        CloseButtonText = "Cancel",
        XamlRoot = this.XamlRoot,
    };
    
    await dialog.ShowAsync();
    
    if (dialog.DialogResult == PasswordDialog.ContDialogResult.OK)
    {
        System.Diagnostics.Debug.WriteLine("Login successed.");
        return;
    }
    
    System.Diagnostics.Debug.WriteLine($"Login failed. [UserId: {dialog.UserId}, Password: {dialog.Password} ]");
    

    BTW, your code doesn't work because this.ShowAsync() will call it self. I guess you mean base.ShowAsync().