Skip to content

UI Design Guidelines

bclothier edited this page Feb 1, 2019 · 8 revisions

Rubberduck's UI can be broken down in two categories:

  • Dockable toolwindows
  • Modal dialogs

Each have their own set of guidelines.

Dockable Toolwindows

All dockable toolwindows involve a WinForms control that hosts a WPF UserControl. The WinForms part must involve a class derived from DockableToolwindowPresenter. The role of that class is to merely pass down a specific user control to the base constructor - hence, these classes should not contain any application logic:

public class ToDoExplorerDockablePresenter : DockableToolwindowPresenter
{
    public ToDoExplorerDockablePresenter(VBE vbe, AddIn addin, IDockableUserControl window)
        : base(vbe, addin, window)
    {
    }
}

The IDockableUserControl is the WinForms UserControl host. Its role is to property-inject a ViewModel into the hosted WPF component, and specify a ClassId and a Caption. The ClassId is a GUID that will be registered as a COM component upon installation.

As an example, here's the ToDoExplorerWindow implementation:

public partial class ToDoExplorerWindow : UserControl, IDockableUserControl
{
    private const string ClassId = "8B071EDA-2C9C-4009-9A22-A1958BF98B28";
    string IDockableUserControl.ClassId { get { return ClassId; } }
    string IDockableUserControl.Caption { get { return RubberduckUI.ToDoExplorer_Caption; } }

    public ToDoExplorerWindow()
    {
        InitializeComponent();
    }

    private ToDoExplorerViewModel _viewModel;
    public ToDoExplorerViewModel ViewModel
    {
        get { return _viewModel; }
        set
        {
            _viewModel = value;
            TodoExplorerControl.DataContext = _viewModel;
            if (_viewModel != null)
            {
                _viewModel.RefreshCommand.Execute(null);
            }
        }
    }
}

Naming conventions

  • The dockable presenter implementation must be named [FeatureName]DockablePresenter.
  • The WinForms host control must be named [FeatureName]Window.
  • The WPF hosted control must be named [FeatureName]Control.
  • The main window ViewModel class must be named [FeatureName]ViewModel.

Grid Views

As of v2.0, toolwindows that involve a grid should use the XAML control GroupingGrid, under the Rubberduck.UI.Controls.GroupingGrid namespace.

Except for the Code Explorer (which uses a tree view), all dockable toolwindows should use a grid view.


Toolbars

All toolwindows that require parser state should include a toolbar that contains a "Refresh" button as the left-most control, followed by a separator.


Dialogs

Modal dialogs should be implemented as a WinForms form containing only a single control which is a WPF ElementHost. It is not possible to expose dialogs as a WPF window in a VBA Addin. Thus, they need to be wrapped in a WinForms dialog. The WinForms dialog should use the MVP pattern while the WPF XAML contained within the ElementHost control should use the MVVM pattern.

Due to that requirement, the project must implement two-layered approach for managing the UI. We make use of generic classes to help abstract out the common tasks and setups, enabling us to focus on the custom behaviors for each refactoring. Thus, to implement a dialog for a feature xxx that is invoked by a menu command, we require those classes:

  • xxxCommand.cs == Implements ICommand interface
  • xxxView.xaml == XAML form for WPF, used in the WinForm's ElementHost and is the V of WPF's MVVM pattern
  • xxxPresenter.cs == The (P)resenter for the WinForm's MVP pattern, should derive from RefactoringPresenterBase class
  • xxxViewModel.cs == The ViewModel (VM) for the WPF's MVVM pattern; should contain all of UI logic specific to the feature and must derive from RefactoringViewModelBase class.
  • xxxModel.cs == The (M)odel containing only data for both WinForms' MVP and WPF's MVVM pattern. Note that the WinForms dialog shouldn't use the model directly; it should be simply passed through to the WPF.

Note that normally you do not need to create a WinForms dialog yourself. The RefactoringPresenterBase will use the generic RefactoringDialogBase which is generally suitable for all purposes, allowing you to focus mainly on the WPF layer. The presenter also should provide the static DialogData, which includes customizations for the dialog's caption, the initial/minimum sizes allowed for the dialog.

The ViewModel must derive from the RefactoringViewModelBase so that the RefactoringPresenterBase and RefactoringDialogBase will be able to use the view model in their internal operations.

WinForms' presenter should be the starting point, created from a factory. The model should be created by the Refactoring class and then provided to the presenter. The presenter should in turn pass in the model to the WPF's MVVM pattern so that it has the data and instantiate the ViewModel for the contained WPF ElementHost control. Furthermore, the presenter will listen for the dialog's Close event so that it can then return the DialogResult which should be used by the invoking Refactoring class to decide whether to proceed with the refactoring. The ViewModel should be the one responsible for performing the validation of data in the model and dictating what actions are allowable. Thus, the WinForms dialog should be simply a container to the WPF UI and pass through the model and returning the result from the WPF.

Note that most dialogs are typically a refactoring, so we usually also implement a xxxRefactoring class which is then invoked by the xxxCommand class, calling the Refactor method when there is a valid result from the dialog.

In order to maintain a similar look & feel across the entire application, here are design guidelines for dialogs:

The "Rename" dialog

  • Use ducky.ico as an icon for all dialogs
  • Include a 64px White banner at the top including a TitleLabel and an InstructionsLabel.
  • TitleLabel font should be Microsoft Sans Serif, 9pt, bold
  • InstructionsLabel font should be Microsoft Sans Serif, 8.25pt. The label should wrap and provide enough space for non-English localizations.
  • Include a 43px ControlDark banner at the bottom including one of two sets of 23px (height) by a minimum of 75px (width) buttons:
  • OkButton and CancelButton controls for any action that can be cancelled.
  • CloseButton control, for any dialog that is merely displayed, and then closed by the user.
  • Textboxes that contain user input that requires validation, should include a [ValidationReason]ValidationIcon 16x16px containing resource image Rubberduck.Properties.Resources.cross_circle; the OkButton should be disabled when this icon is visible, and the icon should only be visible when the textbox contains invalid data.
  • Textboxes should be 20px in height, and should have a label beside them, with enough room to accomodate non-English localizations.
  • All controls should have a meaningful name.
Clone this wiki locally