Go to content

Bulletcode.NET

Shell

The Shell is a set of services and controls which make is possible to navigate between pages displayed in the main window, and display modal dialogs in overlays within the main window.

In order to enable shell, call the AddNavigationProvider() extension method of the service provider. It returns an INavigationProviderBuilder interface, which has the following extension methods:

  • AddPage() — registers a specific page, which must inherit BasePage
  • AddDialog() — registers a specific dialog, which must inherit BaseDialog
  • RegisterPagesAndDialogs() — registers all pages and dialogs defined in the specified assembly, i.e. all non-abstract classes which inherit BasePage and BaseDialog
  • UseStartPage() — sets the initial page which is displayed when the application starts

For example:

    services.AddNavigationProvider()
        .RegisterPagesAndDialogs( typeof( Program ).Assembly )
        .UseStartPage( "ExamplePage" );

The INavigationProvider service should can used to navigate between pages and to display modal dialogs. It contains the following methods:

  • NavigateTo() — shows the specified page in the <shell:PageContainer> control. The page must be registered using AddPage() or RegisterPagesAndDialogs(). The previously displayed page is hidden, but it is not destroyed, so its state is preserved when it’s shown again.
  • Destroy() — destroys the specified page.
  • DestroyAll() — destroys all pages which have been created.
  • ShowDialog() — shows the specified modal dialog in the <shell:DialogContainer> control. The dialog must be registered using AddDialog() or RegisterPagesAndDialogs(). This method doesn’t return until the dialog is closed. Multiple dialogs can be displayed simultaneously, but only the topmost dialog is active at a given time and can receive mouse and keyboard input.
  • CloseDialog() — closes the topmost modal dialog. This method should be called by the dialog’s view model. An optional result (true or false) can be passed to this method; it is then returned by the corresponding ShowDialog() method. The dialog is destroyed when it’s closed.
  • ShowBusy() — displays a busy indicator inside the <shell:DialogContainer> control and executes the specified asynchronous function. The application remains responsive while the function is being executed, but mouse and keyboard input are blocked. This method doesn’t return until the function callback finishes executing. If the callback function returns a value, it is passed as the result of this method.

INavigationProvider also contains the CurrentPageName property and the Navigated event which is triggered when a new page is displayed.

Additional options can be passed as the second parameter of the ShowDialog() method. The dialog’s view model can receive those options using the IDialogOptions interface with the correct type, for example:

public class ExampleDialogViewModel : BaseViewModel
{
    private readonly ExampleDialogOptions _options;

    public ExampleDialogViewModel( IDialogOptions<ExampleDialogOptions> options )
    {
        _options = options.Value;
    }
}

Shell controls

The root element of the application’s main window should be a <shell:DialogContainer> control. It is used to display the dialogs and busy indicators. Inside it, there should be a <shell:PageContainer> control which displays the current page. Optionally, the dialog container can also contain other elements, such as navigation buttons used to navigate between pages.

The <shell:NavigationButton> control can be used to navigate between pages. The Page property specifies the target page. The checked state of the button is automatically toggled when the target page is displayed.

The following example demonstrates a basic layout of a main window:

<Window
    x:Class="App.Desktop.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ctrl="clr-namespace:Bulletcode.Desktop.Controls;assembly=Bulletcode.Desktop.Wpf"
    xmlns:shell="clr-namespace:Bulletcode.Desktop.Shell;assembly=Bulletcode.Desktop.Wpf"
    >
    <shell:DialogContainer>
        <Grid ctrl:GridLayout.Columns="Auto,*">
            <shell:PageContainer Grid.Column="1" />
            <StackPanel>
                <shell:NavigationButton Page="ExamplePage" ToolTip="Example Page">
                    <ctrl:SymbolIcon Symbol="MapPin"/>
                </shell:NavigationButton>
                <shell:NavigationButton Page="AnotherPage" ToolTip="Another Page">
                    <ctrl:SymbolIcon Symbol="Airplane"/>
                </shell:NavigationButton>
            </StackPanel>
        </Grid>
    </shell:DialogContainer>
</Window>

Pages and dialogs

The BasePage class, which should be inherited by every page, defines the following properties:

  • SubmitCommand — a command which is executed when the Enter key is pressed
  • LoadedCommand — a command which is executed immediately after the page is displayed

If a page inherits the template version of the BasePage class, the parameter of the template specifies the type of the view model which is automatically created and assigned as the data context of the page. For example:

public class ExamplePage : BasePage<ExamplePageViewModel>
{
    public ExamplePage()
    {
        InitializeComponent();
    }
}

The corresponding XAML file should also specify the base class and its type argument, for example:

<shell:BasePage
    x:Class="App.Desktop.Views.ExamplePage"
    x:TypeArguments="vm:ExamplePageViewModel"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:shell="clr-namespace:Bulletcode.Desktop.Shell;assembly=Bulletcode.Desktop.Wpf"
    xmlns:vm="clr-namespace:App.Desktop.ViewModels"
    >
    <!-- TODO -->
</shell:BasePage>

The BaseDialog class, which should be inherited by every dialog, defines the following properties:

  • Title — the title displayed in the dialog’s header
  • ShowCloseButton — specifies if a close button is displayed in the dialog’s header (the default value is true)
  • AutoHeight — when true, the height of the dialog is automatically adjusted between AutoMinHeight and the height of the main window. This is useful when the dialog contains a control with dynamic height, such as a grid view.
  • AutoMinHeight — specifies the minimum height of the dialog when AutoHeight is set to true
  • AutoFixedHeight — when true, the height of the dialog is automatically adjusted to take the full height of the main window.
  • SubmitCommand — a command which is executed when the Enter key is pressed
  • LoadedCommand — a command which is executed immediately after the dialog is displayed

If a dialog inherits the template version of BaseDialog, the parameter of the template specifies the type of the view model used for the dialog.

The pages, dialogs, and their associated view models are created using the service provider. It’s recommended that the dependencies are added to view models, and not directly to the page or dialog classes. The view models are created within the same scope as the corresponding page or dialog, and they are automatically destroyed when the page or dialog is destroyed.

See View models for more information about implementing view models.