-
Notifications
You must be signed in to change notification settings - Fork 301
UI Design Guidelines
Rubberduck's UI can be broken down in two categories:
- Dockable toolwindows
- Modal dialogs
Each have their own set of guidelines.
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);
}
}
}
}
- 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
.
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.
All toolwindows that require parser state should include a toolbar that contains a "Refresh" button as the left-most control, followed by a separator.
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
== ImplementsICommand
interface -
xxxView.xaml
== XAML form for WPF, used in the WinForm'sElementHost
and is the V of WPF's MVVM pattern -
xxxPresenter.cs
== The (P)resenter for the WinForm's MVP pattern, should derive fromRefactoringPresenterBase
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 fromRefactoringViewModelBase
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:
- Use
ducky.ico
as an icon for all dialogs - Include a 64px
White
banner at the top including aTitleLabel
and anInstructionsLabel
. -
TitleLabel
font should beMicrosoft Sans Serif, 9pt, bold
-
InstructionsLabel
font should beMicrosoft 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
andCancelButton
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 imageRubberduck.Properties.Resources.cross_circle
; theOkButton
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.
rubberduckvba.com
© 2014-2021 Rubberduck project contributors
- Contributing
- Build process
- Version bump
- Architecture Overview
- IoC Container
- Parser State
- The Parsing Process
- How to view parse tree
- UI Design Guidelines
- Strategies for managing COM object lifetime and release
- COM Registration
- Internal Codebase Analysis
- Projects & Workflow
- Adding other Host Applications
- Inspections XML-Doc
-
VBE Events