Skip to content

[API Proposal]: File dialog controls #7733

Open
@miloush

Description

@miloush

Before API review: see whether we could reuse the classes for TaskDialog

Background and motivation

WPF applications are lacking the ability to customize file dialogs, which has become a common requirement.

Notepad:

Visual Studio:

Office:

This is a proposal for API to enable this functionality in WPF, notably IFileDialogCustomize.

There is a prototype PR using the proposed API, see #7256.

API Proposal

For class diagram see PR.

Changes to existing:

namespace Microsoft.Win32
{
    public abstract partial class CommonItemDialog : CommonDialog
    {
        protected CommonItemDialog() { }
+       public FileDialogCancelButton CancelButton { get; }
+       public FileDialogCustomControls CustomControls { get; }
        public IList<FileDialogCustomPlace> CustomPlaces { get; set; }
        public bool DereferenceLinks { get; set; }
        public string InitialDirectory { get; set; }
        public string FileName { get; set; }
        public string[] FileNames { get; }
+       public FileDialogOkButton OkButton { get; }
        public bool RestoreDirectory { get; set; }
        public string SafeFileName { get; }
        public string[] SafeFileNames { get; }
        public string Title { get; set; }
        public bool ValidateNames { get; set; }
        public event CancelEventHandler FileOk { add; remove; }
        protected void OnFileOk(CancelEventArgs e) { }
        public override void Reset() { }
        protected override bool RunDialog(IntPtr hwndOwner);
        public override string ToString();
    }
}

All new, including the namespace:

namespace Microsoft.Win32.Controls
{
    public sealed partial class FileDialogCancelButton
    {
        public string CustomLabel { get; set; }
    }
    public sealed partial class FileDialogCheckButton : FileDialogText
    {
        public FileDialogCheckButton();
        public FileDialogCheckButton(string label);
        public FileDialogCheckButton(string label, bool isChecked);
        public bool IsChecked { get; set; }
        public event EventHandler Checked { add; remove; }
        public event EventHandler Unchecked { add; remove; }
        public override object Clone();
    }
    public sealed partial class FileDialogComboBox : FileDialogSelectorControl
    {
        public FileDialogComboBox();
        public FileDialogComboBox(params string[] items);
    }
    public abstract partial class FileDialogControl : FileDialogControlBase, ICloneable
    {
        protected FileDialogControl();
    }
    public abstract partial class FileDialogControlBase : ICloneable
    {
        public FileDialogControlBase();
        public int ID { get; }
        public bool IsEnabled { get; set; }
        public bool IsVisible { get; set; }
        public object Tag { get; set; }
        public abstract object Clone();
        public void HideAndDisable();
        public virtual void ShowAndEnable();
    }
    public partial class FileDialogControlCollection : Collection<FileDialogControl>
    {
        public FileDialogControlCollection();
        public FileDialogControlCollection(params FileDialogControl[] controls);
        public FileDialogCheckButton AddCheckButton(string label, bool isChecked = false);
        public FileDialogComboBox AddComboBox(params string[] items);
        public FileDialogEditBox AddEditBox(string text);
        public FileDialogMenu AddMenu(string label, params string[] items);
        public FileDialogPushButton AddPushButton(string label);
        public FileDialogRadioButtonList AddRadioButtonList(params string[] items);
        public FileDialogSeparator AddSeparator();
        public FileDialogText AddText(string label);
        protected override void ClearItems();
        protected override void InsertItem(int index, FileDialogControl item);
        protected override void RemoveItem(int index);
        protected override void SetItem(int index, FileDialogControl item);
    }
    public sealed partial class FileDialogControlItem : FileDialogControlBase
    {
        public FileDialogControlItem();
        public FileDialogControlItem(string text);
        public string Text { get; set; }
        public override object Clone();
    }
    public partial class FileDialogControlItemCollection : Collection<FileDialogControlItem>
    {
        internal FileDialogControlItemCollection();
        public FileDialogControlItem Add(string itemText);
        protected override void ClearItems();
        protected override void InsertItem(int index, FileDialogControlItem item);
        protected override void RemoveItem(int index);
        protected override void SetItem(int index, FileDialogControlItem item);
    }
    public partial class FileDialogCustomControls : FileDialogControlCollection
    {
        internal FileDialogCustomControls();
        public FileDialogControl Prominent { get; set; }
        public FileDialogComboBox AddComboBoxWithLabel(string label, params string[] items);
        public FileDialogEditBox AddEditBoxWithLabel(string label, string text);
        public FileDialogRadioButtonList AddRadioButtonListWithLabel(string label, params string[] items);
        public FileDialogVisualGroup AddVisualGroup(string label, params FileDialogControl[] controls);
    }
    public sealed partial class FileDialogEditBox : FileDialogControl
    {
        public FileDialogEditBox();
        public FileDialogEditBox(string text);
        public string Text { get; set; }
        public override object Clone();
    }
    public partial class FileDialogItemEventArgs : EventArgs
    {
        public FileDialogItemEventArgs(FileDialogControlItem item);
        public FileDialogControlItem Item { get; }
    }
    public abstract partial class FileDialogItemsControl : FileDialogControl
    {
        protected FileDialogItemsControl();
        protected FileDialogItemsControl(string[] items);
        public FileDialogControlItemCollection Items { get; }
        public sealed override object Clone();
    }
    public sealed partial class FileDialogMenu : FileDialogItemsControl
    {
        public FileDialogMenu();
        public FileDialogMenu(string label, params string[] items);
        public string Label { get; set; }
        public event EventHandler Activating { add; remove; }
        public event EventHandler<FileDialogItemEventArgs> ItemSelected { add; remove; }
    }
    public sealed partial class FileDialogOkButton
    {
        public string CustomLabel { get; set; }
        public FileDialogControlItemCollection Items { get; }
        public bool RequiresInteraction { get; set; }
        public int SelectedIndex { get; }
        public FileDialogControlItem SelectedItem { get; }
        public event EventHandler Activating { add; remove; }
    }
    public sealed partial class FileDialogPushButton : FileDialogText
    {
        public FileDialogPushButton();
        public FileDialogPushButton(string label);
        public event EventHandler Click { add; remove; }
        public override object Clone();
    }
    public sealed partial class FileDialogRadioButtonList : FileDialogSelectorControl
    {
        public FileDialogRadioButtonList()
        public FileDialogRadioButtonList(params string[] items)
    }
    public abstract partial class FileDialogSelectorControl : FileDialogItemsControl
    {
        protected FileDialogSelectorControl(params string[] items);
        public int SelectedIndex { get; set; }
        public FileDialogControlItem SelectedItem { get; set; }
        public event EventHandler<FileDialogItemEventArgs> ItemSelected { add; remove; }
    }
    public sealed partial class FileDialogSeparator : FileDialogControl
    {
        public FileDialogSeparator();
        public override object Clone();
    }
    public partial class FileDialogText : FileDialogControl
    {
        public FileDialogText();
        public FileDialogText(string label);
        public string Label { get; set; }
        public override object Clone();
    }
    public sealed partial class FileDialogVisualGroup : FileDialogText
    {
        public FileDialogVisualGroup(string label);
        public FileDialogVisualGroup(string label, params FileDialogControl[] controls);
        public FileDialogControlCollection Controls { get; }
        public void Add(FileDialogControl control);
        public override object Clone();
    }
}

API Usage

var dialog = new SaveFileDialog();
dialog.Filter = "All files (*.*)|*.*";

var radioGroup = new FileDialogVisualGroup("RadioButtonList");
radioGroup.Add(new FileDialogRadioButtonList("_radio 1", "r_adio 2", "ra_dio 3"));
dialog.CustomControls.Add(radioGroup);

var checkGroup = new FileDialogVisualGroup("CheckButtons");
checkGroup.Add(new FileDialogCheckButton("_check 1"));
checkGroup.Add(new FileDialogCheckButton("c_heck 2", false));
checkGroup.Add(new FileDialogCheckButton("chec_k 3", true));
dialog.CustomControls.Add(checkGroup);

var menuGroup = new FileDialogVisualGroup("Menus and separator");
menuGroup.Add(new FileDialogMenu("_menu 1", "item _1", "item _2"));
menuGroup.Add(new FileDialogSeparator());
menuGroup.Add(new FileDialogMenu("m_enu 2", "item"));
dialog.CustomControls.Add(menuGroup);

var comboGroup = new FileDialogVisualGroup("ComboBoxes");
comboGroup.Add(new FileDialogComboBox("item _1", "item &2", "item 3"));
comboGroup.Add(new FileDialogComboBox("item _1", "item &2", "item 3") { SelectedIndex = 1 });
dialog.CustomControls.Add(comboGroup);

var textGroup = new FileDialogVisualGroup("Text and EditBo_x");
textGroup.Add(new FileDialogText("Generic _label"));
textGroup.Add(new FileDialogEditBox("a text box 😊"));
dialog.CustomControls.Add(textGroup);

var pushButton = new FileDialogPushButton("A button not in a _group");
pushButton.Click += delegate { MessageBox.Show("Clicked!"); };
dialog.CustomControls.Add(pushButton);
            
var prominentGroup = new FileDialogVisualGroup("Pro_minent control", new FileDialogComboBox());
dialog.CustomControls.Add(prominentGroup);
dialog.CustomControls.Prominent = prominentGroup;

dialog.OkButton.Items.Add("_Add");
dialog.OkButton.Items.Add("Add a_ll");
dialog.CancelButton.CustomLabel = "_Done";

dialog.ShowDialog();

Alternative Designs

No response

Risks

Low from the API surface perspective, this is all new functionality not changing the existing behavior.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions