INTRODUCTION: Over a two part series I introduced how to use an MVC pattern with a Universal Windows App.  That example simply gave a list of Game of Thrones characters for display.  I’ve extended the example to also allow editing those characters in a details page.  I’ve also added a singleton to control application state and to perform navigation.

Let’s first take a look at the Singleton, which I’ve named ApplicationSingleton.  I’ve moved the instantiation of the Controller to this class as well as removed code in App.xaml.cs that used the Frame.  Instead of a navigation frame, our Singleton will assign instances of MainPage and DetailsPage to the Window Context as the user navigates the app.

EXAMPLE: App.xaml.cs Changes

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {

#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = false;
            }
#endif

            Window.Current.Content = ApplicationSingleton.Instance.MainPage;
            Window.Current.Activate();
        }

Now that we have the App setting it’s initial page with the Singleton, we’ll take a look at the Singleton itself.

EXAMPLE: ApplicationSingleton Code

using System.Collections.Generic;
using Windows.UI.Xaml;
using SharpNinja.Controllers.People;
using SharpNinja.Models.BlogEntities;
using SharpNinja.Utilities.MvcBasics;

namespace SharpNinja.UI.Windows10Sample2.Singletons
{
    internal class ApplicationSingleton
    {
        private ApplicationSingleton()
        {
            _personsController.Initialize(new List<IView> { _listView, _detailsView });
        }

        private readonly static ApplicationSingleton _instance = 
            new ApplicationSingleton();

        public static ApplicationSingleton Instance => _instance;

        private readonly PersonsController _personsController = 
            new PersonsController();

        private MainPage _listView = new MainPage();
        public MainPage MainPage => _listView;

        private DetailsPage _detailsView = new DetailsPage();
        public DetailsPage DetailsView => _detailsView;

        private AppState _state = AppState.List;

        public void Navigate(AppState state, params object[] values)
        {
            switch (state)
            {
                case AppState.List:
                    Window.Current.Content = _listView;
                    _state = AppState.List;
                    break;

                case AppState.Details:
                    Window.Current.Content = _detailsView;
                    var person = values[0] as Person;
                    _detailsView.InvokeLoadPersonModel(person.Name);
                   _state = AppState.Details;
                    break;
            }
        }
    }

    internal enum AppState { List, Details }
}

The Singleton is used to not only manage the state of the application, but to instantiate and register the pages of the application with the PersonsController (which has not changed at all to accomodate the changes in the User Interface).

The MainPage had to be updated a little to handle the SelectedItemChanged event so that users can click a character and open their details.

EXAMPLE: MainPage.xaml Changes

        <ListView Name="lstResults" Grid.Row="1" HorizontalAlignment="Stretch"
                  SelectionChanged="Selector_OnSelectionChanged">

EXAMPLE: MainPage.xaml.cs Changes

        private void Selector_OnSelectionChanged(object sender, 
            SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 1)
            {
                var person = e.AddedItems[0] as Person;

                ApplicationSingleton.Instance.Navigate(AppState.Details, person);
            }
        }

We now have our navigation and MainPage changes.  We can now look at the XAML for our DetailsPage.

EXAMPLE: DetailsPage.xaml Code

<Page
    x:Class="SharpNinja.UI.Windows10Sample2.DetailsPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SharpNinja.UI.Windows10Sample2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" 
    >
    <Page.TopAppBar>
        <AppBar Height="50" Background="Black" IsOpen="True">
            <AppBarButton Height="50" Name="btnBack" Click="ButtonBase_OnClick">
                <TextBlock Margin="5 0 5 0">Back</TextBlock>
            </AppBarButton>
        </AppBar>
    </Page.TopAppBar>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Border Grid.Row="0" BorderThickness="5" CornerRadius="7" 
                Background="Blue" BorderBrush="Yellow" 
                Margin="10 5 10 7" Padding="8" Name="nameBorder">
            <TextBlock Text="{Binding Name, Mode=OneWay}"/>
        </Border>

        <Grid Grid.Row="1" Margin="10 5 10 7">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5 0 5 0">First Name:</TextBlock>
            <TextBox Grid.Row="0" VerticalAlignment="Center" Grid.Column="1" Text="{Binding FirstName, Mode=TwoWay}"/>
            <TextBlock Grid.Row="1" VerticalAlignment="Center" Grid.Column="0" HorizontalAlignment="Left" Margin="5 0 5 0">Last Name:</TextBlock>
            <TextBox Grid.Row="1" VerticalAlignment="Center" Grid.Column="1" Text="{Binding LastName, Mode=TwoWay}"/>
            <TextBlock Grid.Row="2" VerticalAlignment="Center" Grid.Column="0" HorizontalAlignment="Left" Margin="5 0 5 0">Date of Birth:</TextBlock>
            <DatePicker Grid.Row="2" VerticalAlignment="Center" Grid.Column="1" Date="{Binding DateOfBirth, Mode=TwoWay}"/>
            <TextBlock Grid.Row="3" VerticalAlignment="Center" Grid.Column="0" HorizontalAlignment="Left" Margin="5 0 5 0">Age:</TextBlock>
            <TextBlock Grid.Row="3" VerticalAlignment="Center" Grid.Column="1" Text="{Binding Age, Mode=OneWay}"/>
        </Grid>
        <Border Grid.Row="2" BorderThickness="5" CornerRadius="7" 
                Background="Blue" BorderBrush="Yellow" 
                Margin="10 7 10 7" Padding="8" Name="statusBorder">
            <TextBlock Name="lblStatus"/>
        </Border>

    </Grid>
</Page>

EXAMPLE: DetailsPage.xaml.cs Code

using System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using SharpNinja.Controllers.People.Views;
using SharpNinja.Models.BlogEntities;
using SharpNinja.UI.Windows10Sample2.Singletons;

namespace SharpNinja.UI.Windows10Sample2
{
    public sealed partial class DetailsPage : Page, IPersonDetailsView
    {
        public DetailsPage()
        {
            this.InitializeComponent();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            ApplicationSingleton.Instance.Navigate(AppState.List);
        }

        // From IView 
        public void SetStatus(string message, bool isError)
        {
            lblStatus.Text = message;
            statusBorder.Background =
                new SolidColorBrush(isError ? Colors.Red : Colors.Blue);
        }

        public Person Model { get; set; }
        public event EventHandler<string> LoadPersonModel;

        internal void InvokeLoadPersonModel(string name)
        {
            LoadPersonModel?.Invoke(this, name);
            DataContext = Model;
        }

    }
}

The DetailsPage uses two way binding on the Model property (an instance of the Person object defined in the IPersonDetailsView interface).  This allows the UI and Model to be updated in real time.  Because the List page uses the ObservableCollection to hold all of the instances of the Person object, even the MainPage gets updated so when you go back to it, you see the changes immediately without having to refresh the search!

CONCLUSION: Using the MVC pattern along with a Singleton is a very, very powerful method of engineering any application.  Here we see it working beautifully with a Universal Windows Application, but it could just as well be WinForms or ASP.Net.  MVVM is not the end-all-be-all of XAML-based development.  Whether you are using Silverlight, WPF, Windows Store App, or Universal Windows App, you can still use the tried and true MVC, and you get a more portable framework to boot.

INTRODUCTION: Singleton is the design pattern that is the easiest to abuse and hardest to nail down. You must correctly ascertain when they are appropriate and to what scope. But first, what is a Singleton? Singletons are classes that should only be instantiated once per application. These classes typically represent a set of functionality that is global to the application, yet deserves to be encapsulated into a single set of functionality.

The Singleton should expose itself as a static property from the class definition, making available public instance methods only from the referenced static property. Singletons should have a single purpose. Some examples are a Window manager for an Application, or a Report generator. I’ve even used a Singleton to manage the state of a WinForms ribbon. The main thing to remember is that the Singleton only gets instantiated at a global scope and should have a narrow focus.

For an example, let’s make a Singleton that performs acts as a state manager for a fictional application. This application will represent a wizard that must move between three steps.

EXAMPLE:

public class AppSingleton
{
  // Private static reference to the long instance of this
  // Singleton.  
  private static readonly AppSingleton _instance = new AppSingleton();

  // Current state of the application.
  private State _state = State.Start;

  public State State => _state;

  // Private constructor ensures that only the Singleton
  // can create new instances.
  private AppSingleton() { }

  public AppSingleton Instance => _instance;

  // Instance method to change the state of the application,
  // returns the resulting app state.
  public State MoveNext()
  { 
    switch(_state)
    {
      case State.Start:
        _state = Middle;
        break;

      case State.Middle:
        _state = Finish;
        break;

      case State.Finish:
        _state = Completed;
        break;
    }
    return _state;
  }

  // Instance method to change the state of the application,
  // returns the resulting app state;
  public State MovePrevious()
  {
    switch(_state)
    {
      case State.Middle:
        _state = First;
        break;

      case State.Finish:
        _state = Middle;
        break;
    }
  }

  // Here we can tell the singleton to perform some work.
  // You do not need this method signature, you can have
  // as many "worker" methods as is necessary.  This is 
  // just an example!
  public void DoSomething(params object[] args)
  {
    // DO some work!
  }
}

public enum State
{
  Start, Middle, Finish, Completed
}

public class Program
{
  public static void Main(string[] args)
  {
    AppSingleton.Instance.DoSomething(1);
    var state = AppSingleton.Instance.MoveNext();

    if(state != State.Start) 
    {
      // We moved state!  Do something else!
      AppSingleton.Instance.DoSomething(2);

      // Let's go back and try again!
      state = AppSingleton.Instance.MovePrevious();

      // Etc.
    }
    else
    {
      // Uh-oh, the singleton says it cannot move.
    }
  }
}

Here our application state is managed by the AppSingleton. The main program simply attempts to manipulate that state by providing data to the Singleton to process. In a real app, there would be user interaction that would drive the wizard forwards and backwards.

CONCLUSION: Singletons are a very powerful arrow in your C# quiver. Single-user applications are great usages for them. If you are not sure if you should use a singleton, ask yourself these questions:

  • Does the Singleton represent a single state within the application?
  • Does the Singleton have logic that should be encapsulated?
  • Do you need to control the object lifecycle for the Singleton?

If your answer is yes to these questions, a Singleton is right for you!