Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Dialogs API spec #94

Closed
amwx opened this issue Feb 11, 2022 · 5 comments
Closed

Generic Dialogs API spec #94

amwx opened this issue Feb 11, 2022 · 5 comments
Labels
API enhancement New feature or request

Comments

@amwx
Copy link
Owner

amwx commented Feb 11, 2022

I've had a couple requests for enhancements to the existing ContentDialog functionality. As a result, I'm drafting up an API spec for generic dialogs. As ContentDialog is a WinUI item (reverse engineered instead of ported, but still) I don't want to modify the API in a way that makes it different to the upstream version other than bug fixes or minor feature additions.

The biggest issue from the comments is that the lack of button options in ContentDialog make it somewhat limiting, something the old vista-style TaskDialog solves (to an extent). This proposal, therefore, seeks a new control and API spec. Note that this is just an API spec to layout the general thought and get feedback of desired features - and is not yet a firm proposal or commitment.

EDIT: Final spec laid out here - PR created

public class TaskDialog : ContentControl
{
    /// <summary>
        /// Gets or sets the title of the dialog
        /// </summary>
        /// <remarks>
        /// This is the window caption of the dialog displayed in the title bar. For platforms 
        /// where windowing is not supported, this property has no effect.
        /// </remarks>
        public string Title { get; set; }

        /// <summary>
        /// Gets or sets the dialog header text
        /// </summary>
        public string Header { get; set; }

        /// <summary>
        /// Gets or sets the dialog sub header text
        /// </summary>
        public string SubHeader { get; set; }

        /// <summary>
        /// Gets or sets the dialog Icon
        /// </summary>
        public IconSource IconSource { get; set; }

        /// <summary>
        /// Gets the list of buttons that display at the bottom of the TaskDialog
        /// </summary>
        public IList<TaskDialogButton> Buttons { get; set; }

        /// <summary>
        /// Gets the list of Commands displayed in the TaskDialog
        /// </summary>
        public IList<TaskDialogCommand> Commands { get; set; }

        /// <summary>
        /// Gets or sets the visibility of the Footer area
        /// </summary>
        public TaskDialogFooterVisibility FooterVisibility  { get; set; }

        /// <summary>
        /// Gets or sets whether the footer is visible
        /// </summary>
        public bool IsFooterExpanded { get; set; }

        /// <summary>
        /// Gets or sets the footer content
        /// </summary>
        public object Footer { get; set; }

        /// <summary>
        /// Gets or sets the IDataTemplate for the footer content
        /// </summary>
        public IDataTemplate FooterTemplate { get; set; }

        /// <summary>
        /// Gets or sets whether this TaskDialog shows a progress bar
        /// </summary>
        public bool ShowProgressBar { get; set; }

        /// <summary>
        /// Gets or sets the root visual that should host this dialog
        /// </summary>
        /// <remarks>
        /// For TaskDialogs declared in Xaml, this is automatically set. If you declare a 
        /// TaskDialog in C#, you MUST set this property before showing the dialog to prevent
        /// and error. For desktop platforms, set it to the Window that should own the dialog.
        /// For others, set it to the root TopLevel.
        /// </remarks>
        public IVisual XamlRoot { get; set; }

        /// <summary>
        /// Raised when the TaskDialog is beginning to open, but is not yet visible
        /// </summary>
        public event TypedEventHandler<TaskDialog, EventArgs> Opening;

        /// <summary>
        /// Raised when the TaskDialog is opened and ready to be shown on screen
        /// </summary>
        public event TypedEventHandler<TaskDialog, EventArgs> Opened;

        /// <summary>
        /// Raised when the TaskDialog is beginning to close
        /// </summary>
        public event TypedEventHandler<TaskDialog, TaskDialogClosingEventArgs> Closing;

        /// <summary>
        /// Raised when the TaskDialog is closed
        /// </summary>
        public event TypedEventHandler<TaskDialog, EventArgs> Closed;



        /// <summary>
        /// Shows the TaskDialog
        /// </summary>
        /// <param name="showHosted">Optional parameter that specifies whether this dialog should show in the OverlayLayer even on windowing platforms. Defaults to false</param>
        /// <returns>The TaskDialog result corresponding to the command/button used to close the dialog</returns>
        /// <remarks>
        /// Before calling this method, you MUST set <see cref="XamlRoot"/> property to the TopLevel/Window that should
        /// own or host this Dialog. If you declare the dialog in Xaml, this is done automatically since 
        /// the dialog is already attached to the visual tree
        /// </remarks>
        public async Task<object> ShowAsync(bool showHosted = false) { }

        /// <summary>
        /// Hides the TaskDialog with a <see cref="TaskDialogStandardResult.None"/> result
        /// </summary>
        public void Hide() { }

        /// <summary>
        /// Hides the dialog with the specified dialog result
        /// </summary>
        public void Hide(object result) { }
}

public enum TaskDialogFooterVisibility
{
    Never, // Footer is never visible
    Auto, // Footer title is visible with expand option to show/hide
    Always // Footer is always visible and the footer title is hidden
}

// Base class for controls that can be added to a TaskDialog
// These would be abstractions and not actual controls, TaskDialog would create/manage that
public abstract class TaskDialogControl
{
    public TaskDialogControl() { }

    public TaskDialogControl(string text, object result) { }

    // The text caption of the control
    public string Text { get; set; }

    // The result the button corresponds to and is returned when the dialog
    // closes. Can be TaskDialogStandardResult or an user chosen object
    // May not be null
    public object DialogResult { get; set; }

    // Gets/sets whether the control is enabled
    public bool IsEnabled { get; set; }

    // Is this the default button - will only be settable on one control
    // error thrown if multiple are set
    public bool IsDefault { get; set; }
}

// Represents a regular button in a TaskDialog, can be placed in Buttons or
// Options area of dialog
// Buttons placed in the Buttons area will trigger an automatic close of the dialog,
// Buttons placed in the Options area will not, call Hide() if you wish to do so
public class TaskDialogButton : TaskDialogControl
{
    public TaskDialogButton() { }

    public TaskDialogButton(string text, object result)
        :base(text, result) { }
   
    // An optional icon
    public IconSource IconSource { get; set; }

        /// <summary>
        /// Gets or sets the command that is invoked when the button is clicked
        /// </summary>
        public ICommand Command { get; set; }

        /// <summary>
        /// Gets or sets the command parameter for the <see cref="Command"/>
        /// </summary>
        public object CommandParameter { get; set; }
            
    // Event fired when button is clicked/tapped (occurs before dialog close)
    public event TypedEventHandler<TaskDialogButton, EventArgs> Click;

    // Default Buttons - these will be immutable (no properties / event handlers / commands can be set on them)
    public static readonly TaskDialogButton OKButton = new TaskDialogButton("OK", TaskDialogStandardResult.OK);
    public static readonly TaskDialogButton CancelButton = new TaskDialogButton("Cancel", TaskDialogStandardResult.Close);
    public static readonly TaskDialogButton YesButton = new TaskDialogButton("Yes", TaskDialogStandardResult.Yes);
    public static readonly TaskDialogButton NoButton = new TaskDialogButton("No", TaskDialogStandardResult.No);
    public static readonly TaskDialogButton RetryButton = new TaskDialogButton("Retry", TaskDialogStandardResult.Retry);
    public static readonly TaskDialogButton CloseButton = new TaskDialogButton("Close", TaskDialogStandardResult.Close);
}

// Set of standard results you can use
public enum TaskDialogStandardResult
{
    None,
    OK,
    Cancel,
    Yes,
    No,
    Retry,
    Close
}

// Represents a extra options command - can only be placed in the options area
public class TaskDialogCommand : TaskDialogButton
{
    // Additional caption for this command button
    public string Description { get; set; }

        /// <summary>
        /// Gets or sets whether invoking this command should also close the dialog
        /// </summary>
        public bool ClosesOnInvoked { get; set; }
}

// Represents an radiobutton within a TaskDialog - can only be placed in options area
public class TaskDialogRadioButton : TaskDialogCommand
{
    public bool? IsChecked { get; set; }
}

public class TaskDialogCheckBox : TaskDialogRadioButton
{
}

Final design:
image

@amwx amwx added enhancement New feature or request API labels Feb 11, 2022
@Maruhl
Copy link

Maruhl commented Feb 12, 2022

Looks really good and it allows to make our applications even better.

@sabuto
Copy link

sabuto commented Feb 14, 2022

I really like this, as it adds more customisation options. alot like the material.avalonia custom dialogs

@robloo
Copy link
Contributor

robloo commented Feb 17, 2022

FYI, similar ideas were discussed in WinUI here: microsoft/microsoft-ui-xaml#2313. You might find some inspiration/good-ideas there as well.

@robloo
Copy link
Contributor

robloo commented Feb 17, 2022

Looks like Avalonia has a discussion as well: AvaloniaUI/Avalonia#670 (comment)

@amwx amwx mentioned this issue Feb 22, 2022
@nlogozzo
Copy link
Contributor

nlogozzo commented Mar 3, 2022

Can be closed as #104 has merged

@amwx amwx closed this as completed Mar 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants