Skip to content

UI Design Guidelines

bclothier edited this page Nov 22, 2017 · 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 WPF ElementHost. It is not possible to expose dialogs as a WPF windows, so for that reason, they need to be wrapped in a WinForm control. Due to that requirement, the project must implement two-layered approach for managing the UI. The WinForms dialog should use the MVP pattern while the WPF ElementHost should use the MVVM pattern. Thus, to implement a dialog that is invoked by a menu command, we require those classes:

xxxCommand.cs == Implements ICommand interface xxxDialog.cs == WinForms dialog itself, the V of the WinForms MVP pattern 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 xxxViewModel.cs == The ViewModel (VM) for the WPF's MVVM pattern; should contain all of UI logic specific to the feature xxxModel.cs == The model containing only data to satisfy M for both WinForm's MVP and WPF's MVVM pattern

WinForms' presenter should be the starting point, created from a factory. The model and other dependencies such as parser state or indenter should be then passed to the factory's Create method. The presenter should in turn pass in the model to the WPF's MVVM architecture so that it has the data and instantiate the ViewModel for the contained WPF ElementHost control. Furthermore, the presenter should listen for the dialog's Close event so that it can then return the DialogResult which should be used by the invoking Command 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 Winform 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 implement a xxxRefactoring class which is then invoked by the xxxCommand class if it determines to have a valid result from the dialog by calling the Refactor method.

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