Implementing ViewModel-based Navigation in your Universal App

Since the introduction XAML applications (WFP, Silverlight) and continuing with Windows 8 Metro-style Apps, a Universal Windows Platform App (UWP) takes advantage of the Model-View-ViewModel (MVVM) pattern to distribute the responsibilities of an app to different classes or layers.

In this post, I will walk you through implementing view model-based navigation in a UWP App, instead of the more intuitive Page-based navigation.
In order to make the explanation more complete, I will not make use of any framework for this post. In a later post, I will refactor the code in order to take advantage of one of many frameworks that include MVVM helpers.

For instance, starting from the Hamburger Menu application we’ve built in this previous post, we can introduce view model classes to refactor the navigation between pages and synchronize which option is selected when landing on a particular page. But first, let’s start with an initial situation:

  • First, follow the steps in the previous post to build a Hamburger Menu application.
  • Create a couple of additional pages, MainPage.xaml and OtherPage.xaml.

Remember that the Hamburger Menu is implemented using RadioButton controls.
In the Shell.cs file, add the following code to navigate to the pages when a RadioButton is selected:

public Frame AppFrame { get { return Content; } }

private void Option1Button_Checked(object sender, RoutedEventArgs e)
{
    AppFrame.NavigateTo(typeof (MainPage));
}

private void Option2Button_Checked(object sender, RoutedEventArgs e)
{
    AppFrame.NavigateTo(typeof (OtherPage));
}

...

Notice that the application currently uses the traditional Page-based navigation technique, reacting to event handler in the code-behind.

Introducing view model in the UWP App

One key benefit of view models is that they can help remove most of the code-behind a page. Using two-way databinding instead of wiring events in the code-behind usually lends to a better design.

Create the MainPageViewModel.cs and OtherPageViewModel.cs classes, that correspond to their respective pages. Create also the ApplicationViewModel.cs class that represents the view model for the entire application. A view model class looks like this:

public class MainPageViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly INavigationService navigationService_;

    /// <summary>
    /// Initialize a new instance of the <see cref="MainPageViewModel"/> class with required dependencies.
    /// </summary>
    /// <param name="navigationService"></param>
    public MainPageViewModel(INavigationService navigationService)
    {
    	navigationService_ = navigationService;
    }

    ...

    private void RaisePropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
...

In a real-world program, a view model inherits from a common base class that provides helpers for property notifications. Please, note that, for this post, I’m not using any framework so that everything is made clear at the cost of a slightly longer post :-).

Notice that the MainPageViewModel class requires a dependency to the INavigationService interface. Here is what this is all about:

Creating a simple NavigationService

A good practice to follow when building a UWP application is to abstract away most features behind an interface to some kind of “service”.
So, we will create a simple INavigationService and its corresponding NavigationService implementation.

Because we want to abstract as many infrastructure details as possible, we will use simple string identifiers to represent the pages of our application.
The INavigationService interface can be as simple as :

/// <summary>
/// Provides a mechanism to navigate between pages.
/// </summary>
public interface INavigationService
{
    /// <summary>
    /// Gets the name of the currently displayed page.
    /// </summary>
    string CurrentPage { get; }

    /// <summary>
    /// Navigates to the specified page.
    /// </summary>
    /// <param name="page"></param>
    void NavigateTo(string page);

    /// <summary>
    /// Navigates to the specified page and
    /// supply additional page-specific parameters.
    /// </summary>
    /// <param name="page"></param>
    /// <param name="parameter"></param>
    void NavigateTo(string page, object parameter);

    /// <summary>
    /// Navigates to the previous page in the navigation history.
    /// </summary>
    void GoBack();
}

The implementation is straightforward:

/// <summary>
/// Provides a mechanism to navigate between pages.
/// </summary>
public sealed class NavigationService : INavigationService
{
    private readonly IDictionary<string, Type> pages_
        = new Dictionary<string, Type>();

    /// <summary>
    /// The name of the virtual "root" page at the top of the navigation history.
    /// </summary>
    public const string RootPage = "(Root)";

    /// <summary>
    /// A moniker for an "unknown" page when navigation happens without
    /// using the <see cref="NavigationService"/>.
    /// </summary>
    public const string UnknownPage = "(Unknown)";

    private static Frame AppFrame => ((Window.Current.Content as Frame)?.Content as Shell)?.AppFrame;

    public void Configure(string page, Type type)
    {
        lock (pages_)
        {
            if (pages_.ContainsKey(page))
                throw new ArgumentException("The specified page is already registered.");

            if (pages_.Values.Any(v => v == type))
                throw new ArgumentException("The specified view has already been registered under another name.");

            pages_.Add(page, type);
        }
    }

    #region INavigationService Implementation

    /// <summary>
    /// Gets the name of the currently displayed page.
    /// </summary>
    public string CurrentPage
    {
        get
        {
            var frame = AppFrame;
            if (frame.BackStackDepth == 0)
                return RootPage;

            if (frame.Content == null)
                return UnknownPage;

            var type = frame.Content.GetType();

            lock (pages_)
            {
                if (pages_.Values.All(v => v != type))
                    return UnknownPage;

                var item = pages_.Single(i => i.Value == type);

                return item.Key;
            }
        }
    }

    /// <summary>
    /// Navigates to the specified page.
    /// </summary>
    /// <param name="page"></param>
    public void NavigateTo(string page)
    {
        NavigateTo(page, null);
    }

    /// <summary>
    /// Navigates to the specified page and
    /// supply additional page-specific parameters.
    /// </summary>
    /// <param name="page"></param>
    /// <param name="parameter"></param>
    public void NavigateTo(string page, object parameter)
    {
        lock (pages_)
        {
            if (!pages_.ContainsKey(page))
                throw new ArgumentException("Unable to find a page registered with the specified name.");

            System.Diagnostics.Debug.Assert(AppFrame != null);
            AppFrame.Navigate(pages_[page], parameter);
        }
    }

    /// <summary>
    /// Navigates to the previous page in the navigation history.
    /// </summary>
    public void GoBack()
    {
        System.Diagnostics.Debug.Assert(AppFrame != null);
        if (AppFrame.CanGoBack)
            AppFrame.GoBack();
    }

    #endregion
}

The NavigationService class includes the Configure method that is used to associate a simple string identifier to a data type. This method will be called when registering dependencies and implementing the view modelLocator further in this post.

You’ll notice that this implementation is using the Page-based navigation that is dependent from the infrastructure on which this application runs. Putting this service behind the INavigationService interface allows to decouple the implementation from the application code. This will make it possible to provide alternate implementations for other platforms, such as iOS or Android using Xamarin, for instance.

Users of MVVMLight will recognize part of the implementation of its Navigation Service. One key difference, however, is the use of the following line:

private static Frame AppFrame => ((Window.Current.Content as Frame)?.Content as Shell)?.AppFrame;

This represents the top-level frame that is hosted in the Shell’s SplitView pane. This allows to keep the Hamburger Menu on all pages and still navigate between different views.

As per this Stack Overflow Question it seems that the default MVVMLigth implementation of the Navigation Service does not work well with Hamburger Menu-based applications such as the one I’m showing here. Fortunately, as we’ve seen, implementing a Navigation Service from scratch is straightforward.

More Scaffolding Required

Because we’re using services and would like to code to their interfaces, we will really benefit from using some form of dependency injection. Here, any IoC container or framework will do but, again, for the purpose of this post, let’s use a simple implementation like this one:

/// <summary>
///  A singleton dependency container.
/// </summary>
public sealed class DependencyContainer : IDependencyContainer, IDependencyResolver
{
    private readonly IDictionary<Type, Type> registrations_
        = new Dictionary<Type, Type>();

    private readonly IDictionary<Type, Object> instances_
        = new Dictionary<Type, Object>();

    private static readonly object[] NoArguments = new object[0];

    private static readonly object SyncLock = new object();

    private DependencyContainer()
    {
    }

    /// <summary>
    /// Gets the default instance of the <see cref="DependencyContainer"/> class.
    /// </summary>
    public static DependencyContainer Default { get; } = new DependencyContainer();

    /// <summary>
    /// Registers a new mapping between the specified types.
    /// </summary>
    /// <param name="type"></param>
    /// <param name="mappedTo"></param>
    public void RegisterType(Type type, Type mappedTo)
    {
        if (instances_.ContainsKey(type))
            throw new Exception("A registration for this type already exists.");
        if (registrations_.ContainsKey(type))
            throw new ArgumentException("The specified interface type has already been registered.");

        registrations_.Add(type, mappedTo);
    }

    /// <summary>
    /// Registers a new mapping between the specified type and the specified instance.
    /// </summary>
    /// <param name="type"></param>
    /// <param name="instance"></param>
    public void RegisterInstance(Type type, object instance)
    {
        if (instances_.ContainsKey(type))
            throw new Exception("A registration for this type already exists.");
        if (registrations_.ContainsKey(type))
            throw new ArgumentException("The specified interface type has already been registered.");

        instances_.Add(type, instance);
    }

    /// <summary>
    /// Returns a registered instance corresponding to the specified type.
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public object Resolve(Type type)
    {
        lock (SyncLock)
        {
            if (!instances_.ContainsKey(type))
            {
                if (!registrations_.ContainsKey(type))
                    throw new Exception($"Unable to resolve an instance of the specified type '{type.FullName}'. Are you missing a type registration?");

                var mappedTo = registrations_[type] ?? type;
                var constructors = mappedTo.GetConstructors();
                if (constructors.Length > 1)
                    throw new Exception($"Unable to resolve an type that has more than one constructor.");

                var constructor = constructors[0];
                var parameters = constructor.GetParameters();
                if (parameters.Length == 0)
                    instances_[type] = constructor.Invoke(NoArguments);
                else
                {
                    var args = new object[parameters.Length];
                    foreach (var parameter in parameters)
                        args[parameter.Position] = Resolve(parameter.ParameterType);

                    instances_[type] = constructor.Invoke(args);
                }
            }

            return instances_[type];
        }
    }
}

This code is inspired freely from a sample found here. The bulk of the code is in the Resolve method that uses reflection to dynamically invoke the constructor on types you want to resolve.

Basically, there are two kind of registrations :

  • Mapping between types (usually, from an instance type to an implementing type).
  • Mappings between types and concrete instances.

If an instance is already registered for a given type, it is returned directly.

If not, the method attempts to find the (instance) type mapped to the specified (interface) type. Using reflection, the method looks up a single constructor and identifies the list of its parameters. If no parameters are required, the method instantiates an object of this type and adds a mapping for subsequent use. If more parameters are required, the method is called recursively to resolve an instance for each required parameter, building a list that is supplied in a call to the constructor.

Please, note that this code does not support resolving types with more than one constructor. For a more complete implementation, refer to the aforementioned MVVMLight framework, or use an alternate implementation such as Unity, etc.

For syntactic sugar, I like to include additional methods via an extension class:

public static class DependencyContainerExtensions
{
    /// <summary>
    /// Registers a concrete instance of a particular type to the dependency container.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="container"></param>
    /// <param name="instance"></param>
    public static void RegisterInstance<T>(this IDependencyContainer container, object instance)
    {
        container.RegisterInstance(typeof (T), instance);
    }

    /// <summary>
    /// Registers a mapping from a specified type to itself to the dependency container.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="container"></param>
    public static void RegisterType<T>(this IDependencyContainer container)
    {
        container.RegisterType(typeof (T), typeof (T));
    }

    /// <summary>
    /// Registers a mapping between two specified types to the dependency container.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TU"></typeparam>
    /// <param name="container"></param>
    public static void RegisterType<T, TU>(this IDependencyContainer container)
    {
        container.RegisterType(typeof(T), typeof(TU));
    }

    /// <summary>
    /// Resolves an instance mapped from the specified type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="resolver"></param>
    /// <returns></returns>
    public static T Resolve<T>(this IDependencyResolver resolver)
    {
        return (T) resolver.Resolve(typeof (T));
    }
}

One final piece of code that will help us is to implement a view modelLocator object in our app. This class is responsible for returning instances of a given view model and usually takes advantage of the dependency container shown above. Here is a simple implementation of a view modelLocator for this particular application:

/// <summary>
/// Provides access to the view model classes used by this application.
/// </summary>
public sealed class view modelLocator
{
    /// <summary>
    /// Initialize a new instance of the <see cref="ViewModelLocator"/> class.
    /// </summary>
    public view modelLocator()
    {
        var container = DependencyContainer.Default;

        // dependencies

        container.RegisterType<ISettingsService, SettingsService>();

        // register view models

        container.RegisterType<ApplicationViewModel>();
        container.RegisterType<MainPageViewModel>();
        container.RegisterType<OtherPageViewModel>();

        // navigation service

        var navigationService = new NavigationService();
        navigationService.Configure("MainPage", typeof (MainPage));
        navigationService.Configure("OtherPage", typeof (OtherPage));

        container.RegisterInstance<INavigationService>(navigationService);
    }

    /// <summary>
    /// Gets the <see cref="ApplicationViewModel"/> associated with the <see cref="Shell"/> page.
    /// </summary>
    public ApplicationViewModel Application => DependencyContainer.Default.Resolve<ApplicationViewModel>();

    /// <summary>
    /// Gets the <see cref="MainPageViewModel"/> associated with the <see cref="MainPage"/> page.
    /// </summary>
    public MainPageViewModel MainPage => DependencyContainer.Default.Resolve<MainPageViewModel>();

    /// <summary>
    /// Gets the <see cref="OtherPageViewModel"/> associated with the <see cref="OtherPage"/> page.
    /// </summary>
    public OtherPageViewModel OtherPage => DependencyContainer.Default.Resolve<OtherPageViewModel>();
}

The primary purpose of this class is to provide access to all view model objects used in this application. Specifically, the three property getters are used via databinding to associated a particular view model objects to its respective page in the XAML DataContext.

For the purpose of this post, I have also centralized the registration of all required dependencies in the constructor of the ViewModelLocator class. When an instance of a particular view model object is resolved, the dependency container will resolve its INavigationService dependency.

Finally, this class also uses the NavigationService directly to configure a string identifier for each page of the application.

Putting it all together

First, we need to register a single instance of the ViewModelLocator in the application. This can be done in the application’s resource dictionary. In the App.xaml file, include the following code:

<Application.Resources>
  <ResourceDictionary>
    ...
    <vm:ViewModelLocator xmlns:vm="using:App.ViewModels" x:Key="ViewModelLocator" />
  </ResourceDictionary>
</Application.Resources>

In order to associate the corresponding view model object for each page in the DataContext, add a declaration in the XAML file. For instance, here is the association of the ApplicationViewModel view model in the Shell.xaml page:

<Page
    x:Class="App.Shell"
    ...
    DataContext="{Binding Path=Application, Source={StaticResource ViewModelLocator}}"
    >

 

Here Application refers to the name of the property in the ViewModelLocator that returns an instance of the appropriate ApplicationViewModel object.

In order to substitute event handlers in the code-behind with properties from the view model bound to some controls, we need to add the following properties to the ApplicationViewModel:

/// <summary>
/// Represents the ViewModel for the entire application.
/// </summary>
public sealed class ApplicationViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly INavigationService navigationService_;

    /// <summary>
    /// Initialize a new instance of the <see cref="ApplicationViewModel"/> class.
    /// </summary>
    /// <param name="navigationService"></param>
    public ApplicationViewModel(INavigationService navigationService)
    {
        navigationService_ = navigationService;
    }

    /// <summary>
    /// Gets a value indicating whether the <see cref="MainPage"/> is currently displayed.
    /// </summary>
    public Boolean MainPageSelected
    {
        get { return navigationService_.CurrentPage == "MainPage"; }
        set
        {
            if (value)
                navigationService_.NavigateTo("MainPage");
        }
    }

    /// <summary>
    /// Gets a value indicating whether the <see cref="OtherPage"/> is currently displayed.
    /// </summary>
    public Boolean OtherPageSelected
    {
        get { return navigationService_.CurrentPage == "OtherPage"; }
        set
        {
            if (value)
                navigationService_.NavigateTo("OtherPage");                
        }
    }

    public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The MainPageSelected and OtherPageSelected properties are defined as simple booleans to make it easy to databind them to the IsChecked state of the RadioButton controls used in the Hamburger Menu for the navigation.

The setters for these properties are used to trigger navigation when the corresponding RadioButton control is selected from the UI.

In the Shell.xaml file, remove the event handlers and use the following databindings instead:

This makes it easy to trigger navigation when a RadioButton is selected.
Conversely, when navigation changes the currently displayed page, the state for the corresponding RadioButton controls is updated.

Finally, in the Shell.cs file, remove the now unused event handlers.

Conclusion

I hope this post makes it easier for beginners to see what’s behind a simple navigation service in a Universal Windows Platform app. In the next posts, I will show how to refactor this code around one of many popular frameworks. I will also show how to record the currently displayed page on exit in order to navigate to the previously displayed page when the application starts.

Posted in UWP | Tagged , | Leave a comment

Opening a PowerShell Prompt at the TargetPath of the Selected Project

When developing an application, I usually spend a lot of time in the command-line. For this, I use PowerShell, that gives me access to all my scripts that I have built over time. One special need that I have is to perform some commands in the target path of the selected Project in Solution Explorer.

For instance, in the product we’re building, that has many dependencies, we are controlling exactly which binding redirects need to be present in our executables’ App.config file.

We have built a simple script that processes the contents of a folder and determines from there the exact list of binding redirects that need to be written in the project’s App.config file, along with appropriate comments specifying which dependencies do need these redirects.

Therefore, when developing with Visual Studio, I constantly have to drop to PowerShell and change the location of the current workfing folder in order to run my scripts. For this, I use the “Open Folder in Explorer” context-menu command to open Explorer. I then copy the full path to the project and paste that into PowerShell to issue a Set-Location command.

Over time, this is cumbersome.

So I set out to build a simple Visual Studio Extension (VSIX) that does exactly that. It allows to open a PowerShell command prompt initialized at the location of the selected Project’s TargetPath.

A simple Visual Studio Extension

In order to help me build my first Visual Studio Extension, I followed the talk about Visual Studio Extensibility, that Mads Kristensen has given during //Build 2016. This talk walks you through building a Visual Studio Extension, publishing it on GitHub and setting up a continuous integration build on AppVeyor so that new versions can be published in a minimum of time to the official Visual Studio Gallery where extensions live.

Of course, this being my first Visual Studio Extension, I still had to figure out how to determine the selected project in Solution Explorer, then how to find its TargetPath and, finally, how to launch a PowerShell prompt initialized with a specific folder.

So, here are the three pieces of the puzzle:

How to find the selected Project in Solution Explorer

This has been the hardest part for me to solve. Indeed, Visual Studio has many extensibility points and it is not easy to find the correct way to achieve a particular result. Fortunately, many developers have similar needs when building their extensions, so I was able to coble together a working solution from multiple sources.

The following code shows how to do this:

public sealed class SolutionExplorer
{
    public static Project GetSelectedProject()
    {
        return GetSelectedItem() as Project;
    }

    private static object GetSelectedItem()
    {
        var monitorSelection = (IVsMonitorSelection)Package.GetGlobalService(typeof(SVsShellMonitorSelection));

        IntPtr hierarchyPointer;
        IntPtr selectionContainerPointer;
        IVsMultiItemSelect multiItemSelect;
        uint projectItemId;

        monitorSelection.GetCurrentSelection(out hierarchyPointer,
            out projectItemId,
            out multiItemSelect,
            out selectionContainerPointer);

        using (var container = TypedObjectForIUnknown<ISelectionContainer>.Attach(selectionContainerPointer))
        using (var hierarchy = TypedObjectForIUnknown<IVsHierarchy>.Attach(hierarchyPointer))
        {
            object selection = null;
            ErrorHandler.ThrowOnFailure(
                hierarchy.Instance.GetProperty(
                    projectItemId,
                    (int)__VSHPROPID.VSHPROPID_ExtObject,
                    out selection)
                );

            return selection;
        }
    }
}

This code uses the IVsMonitorSelection extensibility interface that is used to obtain some informations about the project hierarchy.

This code also takes advantage of the TypedObjectForIUnknown helper class that I have written in order to make it easier to consume and dispose of extensibility interfaces around the using statement.

How to find a project’s Target Path

Once the full path to the project’s .csproj file has been determined, I figured I could leverage MSBuild to give me its TargetPath, since this information is specified in the project as part of a PropertyGroup.

It turns out this is quite easy to do, as the following code demonstrates:

  public static string GetTargetPath(string csproj)
  {
      const string propertyName = "TargetPath";
      return GetProperty(csproj, propertyName);
  }

  public static string GetProperty(string csproj, string propertyName)
  {
      var properties = new Dictionary<string, string>();

      // load MSBuild project and evaluate its TargetPath
      // based upon the selected configuration

      using (var collection = new ProjectCollection())
      {
          var project = new Project(
              csproj
              , properties
              , null
              , collection
              , ProjectLoadSettings.Default
              );

          var property = project.Properties
              .Where(p => p.Name == propertyName)
              .Select(p => p.EvaluatedValue)
              .SingleOrDefault()
              ;

          return property;
      }
  }

How to launch PowerShell at a specified location

This is the easiest part of building this extension! For this, it is sufficient to run PowerShell with an initial command that performs a Set-Location to the specified folder. However, path can have spaces in them and it is somewhat hard to issue commands with the proper escaping characters for the different layers of processing.

In order to make it easier on me, I chose to use the EncodedCommand command-line switch of PowerShell, that allows to specified a command as a Base64 encoded UTF-16 string.

The code looks like this:


// run PowerShell prompt at the target location

const string program = @"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe";
const string arguments = "/nologo /noexit /encodedCommand {0}";

var command = $"Set-Location \"{targetFolder}\"";
var encodedCommand = Convert.ToBase64String(Encoding.Unicode.GetBytes(command));

var prog = Environment.ExpandEnvironmentVariables(program);
var options = String.Format(arguments, encodedCommand);

var process = new System.Diagnostics.Process();
process.StartInfo.FileName = prog;
process.StartInfo.Arguments = options;
process.StartInfo.WorkingDirectory = targetFolder;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.Start();
Posted in Visual Studio (VSIX) | Tagged , | Leave a comment

Taking advantage of Continuum with Adaptive UI

With Windows 10, Microsoft introduces Continuum, a feature that allows one to use the phone as the central hub of a PC-like experience with a big screen, a keyboard and, optionally, a mouse.

A Continuum-enabled UWP App should provide a seamless experience when used in both environments. For instance, all the built in applications that ship with Windows 10 are Continuum-enabled Universal Window Platform Apps. Mail, News, Finance, Weather, etc. These are all applications that are usable from the touch-based Phone screen and lighten up with more features and more controls when used from an external display with a mouse and keyboard.

In order to achieve that, a developer can use several new controls that ship with Windows 10. For instance, the SplitView control provides the popular Hamburger-based navigation menu. Also, the RelativePanel is a container that helps create complex layout that can be modified to accommodate more screen real estate by specifying spatial-contraints on its child controls.

Additionally, a developer can take advantage of Adaptive Coding which refers to a set of techniques that help create a single application targeting multiple devices, from small phones to tablets to desktops and more.

There are two sides to Adaptive Coding:

  1. The first one deals with providing the best screen layout and navigation based upon the screen real estate, the current device orientation and the available features.
  2. The second one deals with using features specific to a particular device by detecting whether a particular type or API Contract is present.

In this post, I will highlight how to tackle the first part, thanks to the VisualStateManager class.

The Visual State Manager in Windows 10 takes advantage of Visual State Triggers which are a set of conditions on various states of the application to modify the appearance and organization of one or more controls.

For instance, most examples on this subject show how one can change the color of the background depending upon the current window Width. Several other examples show how one can take advantage of the device orientation to achieve similar results.

In my example, I will show how one can create a custom StateTrigger, i.e. a class that derives from the StateTriggerBase class to detect the current UserInteractionMode. The User Interaction Mode is a value that specifies whether the app is currently being used by using Touch gestures or by using a Mouse and keyboard.

When an App transitions from the Phone screen to an external display thanks to Continuum, the UserInteractionMode changes from Touch to Mouse. That is one of many ways that a developer can take advantage of this feature.

Creating a custom UserInteractionMode state trigger

The idea behind this example is to create a custom class that can be used as a Visual State Trigger in the XAML page. The condition being whether the particular user interaction mode is the one associated with a phone screen – i.e. touch – or the one associated with an external display monitor – i.e. mouse.

This allows to build apps that look like this:

Basic Text Editor App when used on the Phone

Basic Text Editor App when used on the Phone

In your application, create a new file, named UserInteractionModeTrigger and paste the following code:

using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;

public class UserInteractionModeTrigger : StateTriggerBase
{
    ///

<summary>
    /// Initialize a new instance of the <see cref="UserInteractionModeTrigger" /> class.
    /// </summary>


    public UserInteractionModeTrigger()
    {
        Initialize();
    }

    ///

<summary>
    /// Get or set the current UserInteractionMode, i.e. Touch or Mouse.
    /// </summary>


    public string CurrentUserInteractionMode
    {
        get { return (string)GetValue(UIModeProperty); }
        set { SetValue(UIModeProperty, value); }
    }

    public static readonly DependencyProperty UIModeProperty =
        DependencyProperty.Register("UIMode"
            , typeof(string)
            , typeof(UserInteractionModeTrigger),
            new PropertyMetadata("")
            );

    private void Initialize()
    {
        if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
        {
            WindowActivatedEventHandler windowactivated = null;
            windowactivated = (s, e) =>
            {
                Window.Current.Activated -= windowactivated;
                Current_SizeChanged(this, null);
            };

            Window.Current.Activated += windowactivated;
            Window.Current.SizeChanged += Current_SizeChanged;
        }
    }

    private void Current_SizeChanged(object sender, WindowSizeChangedEventArgs e)
    {
        var currentUIMode = UIViewSettings.GetForCurrentView().UserInteractionMode.ToString();
        SetActive(currentUIMode == CurrentUserInteractionMode);
    }
}

This is a simple custom state trigger. Notice that when Initialized, the trigger registers itself on the Activated and SizeChanged events in order to raise a trigger the first time the application is launched. Otherwise, the trigger would only happen during a transition from the Phone screen to the external display.

You can use this call in your app, for instance, like so:

<Page ...
      xmlns:trigg="using:App.Triggers"
...
<Grid>
 
    <!-- The VisualState Groups must be enclosed -- in the first top-level container on a Page. -->

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="UserInteractionModeStates">
            <VisualStateGroup.States>
                <VisualState x:Name="MouseState">
                    <VisualState.StateTriggers>
                        <trigg:UserInteractionModeTrigger CurrentUserInteractionMode="Mouse" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BottomCommandBar.Visibility" Value="Collapsed" />
                        <Setter Target="Header.Visibility" Value="Collapsed" />
                        <Setter Target="SplitView.(Grid.Row)" Value="0" />
                        <Setter Target="SplitView.(Grid.RowSpan)" Value="2" />
                        <Setter Target="TopCommandBar.Visibility" Value="Visible" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState x:Name="NarrowState">
                    <VisualState.StateTriggers>
                        <trigg:UserInteractionModeTrigger CurrentUserInteractionMode="Touch" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BottomCommandBar.Visibility" Value="Visible" />
                        <Setter Target="Header.Visibility" Value="Visible" />
                        <Setter Target="SplitView.(Grid.Row)" Value="1" />
                        <Setter Target="SplitView.(Grid.RowSpan)" Value="1" />
                        <Setter Target="TopCommandBar.Visibility" Value="Collapsed" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup.States>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    
</Grid>

The following article has been used as a reference for this post:

Posted in UWP | Tagged , , | Leave a comment

Displaying a Yes, No, Cancel Message Dialog in a Universal Windows Platform (UWP) App

In one of the applications I’m developing, I needed to display a simple Yes, No, Cancel message dialog. Something like this:

In a Universal App, the MessageDialog class is used to display an alert dialog with a set of choices for the user to make. According to the documentation, this class supports up to three commands.

Here is how one could come up with the dialog shown above with the following code:

var title = "Pending changes"
var content = "There are pending changes.\r\nDo you want to save the modifications?";

var yesCommand = new UICommand("Yes", cmd => { ... });
var noCommand = new UICommand("No", cmd => { ... });
var cancelCommand = new UICommand("Cancel", cmd => { ... });

...

var dialog = new MessageDialog(content, title);
dialog.Options = MessageDialogOptions.None;
dialog.Commands.Add(yesCommand);

dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 0;

if (noCommand != null)
{
    dialog.Commands.Add(noCommand);
    dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

if (cancelCommand != null)
{
    dialog.Commands.Add(cancelCommand);
    dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

var command = await dialog.ShowAsync();

if (command == yesCommand)
{
	// handle yes command
}
else if (command == noCommand)
{
	// handle no command
}
else
{
	// handle cancel command
}

This all works fine… except on an actual Windows Phone device!

In fact, this code crashes on a phone for no apparent reason. I have debugged and could not find a good reason why this code failed. However, I found out that if I only keep two commands for the dialog, the code works all right.

So, my conclusion is that the MessageDialog class only supports two commands on a Windows Phone device, as opposed to a Desktop or a Tablet.

Thinking about it, it makes sense to use the physical hardware back button on a Windows Phone to simulate Cancel. After all, that would be the most natural choice. So I decided to implement this with the modified code below:

...

var dialog = new MessageDialog(content, title);
dialog.Options = MessageDialogOptions.None;
dialog.Commands.Add(yesCommand);

dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 0;

if (noCommand != null)
{
    dialog.Commands.Add(noCommand);
    dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
}

if (cancelCommand != null)
{
    // Devices with a hardware back button
    // use the hardware button for Cancel.
    // for other devices, show a third option

	var t_hardwareBackButton = "Windows.Phone.UI.Input.HardwareButtons";

    if (ApiInformation.IsTypePresent(t_hardwareBackButton))
    {
        // disable the default Cancel command index
        // so that dialog.ShowAsync() returns null
        // in that case

        dialog.CancelCommandIndex = UInt32.MaxValue;
    }
    else
    {
        dialog.Commands.Add(cancelCommand);
        dialog.CancelCommandIndex = (uint)dialog.Commands.Count - 1;
    }
}

var command = await dialog.ShowAsync();

if (command == null && cancelCommand != null)
{
    // back button was pressed
    // invoke the UICommand

    cancelCommand.Invoked(cancelCommand);
}

if (command == yesCommand)
{
	// handle yes command
}
else if (command == noCommand)
{
	// handle no command
}
else
{
	// handle cancel command
}

The code first builds the dialog box normally, adding commands. Each time a new command is added, it sets the CancelCommandIndex property to the last one. For a Yes, No dialog, the default Cancel behavior would result in the No command being triggered.

When both a Yes and a Cancel command are present, the code needs to make a choice. If no hardware button is present – for instance, like on a Desktop – the code proceeds normally. In order to detect a feature, the code uses the ApiInformation.IsTypePresent method with “Windows.Phone.UI.Input.HardwareButtons”, which, as its name suggests, returns whether the device has physical hardware buttons.

If a physical hardware back button is present, however, the code does not add the Cancel command. Instead, it sets the CancelCommandIndex property to (uint) -1. This invalid number will make the MessageDialog.ShowAsync() method return null in that case.

After the MessageDialog.ShowAsync() method returns, the code checks to see whether the Cancel command was choosen. Since this command was not actually handled by the MessageDialog class itself, the code invokes it directly for consistency purposes.

Then it proceeds normally to handle the appropriate branch depending upon the user choice.

This code works fine and allows me to build a single App for Desktop and Mobile with the same behavior. However, this code does not produce the absolutely correct behavior when the App is used on a phone but displayed on an external display, such as when taking advantage of the Continuum feature. In that case, I would like the three buttons to be displayed on the dialog box. However, I did not find a solution for this small problem.

Please, let me know in the comments if you know or find out how to achieve this.

Posted in UWP | Tagged , , | Leave a comment

Improving the Hamburger Menu with mouse hover feedback and proper keyboard handling

Last time, we saw how to build a simple Hamburger Menu for navigation in a Universal Windows Platform App starting with Windows 10.

In this post, I will highlight a few modifications that we need to make in order to enhance the appearance of the menu and in order to properly handle keyboard navigation.

Keyboard navigation

A Universal Windows Platform (UWP) App runs on a potentially a large number of devices, including high-end desktop computers. Therefore, it is important to allow the use of the application through proper handling of the keyboard.

Many features come built in the controls available in XAML. For instance, using the Space key allows one to press a button. So it works all right with the Hamburger button and the Radio Buttons.

In my example, however, I would like the Enter key to be used to select a particular option. And I would like the Arrow keys to be used to select amongst the available options.

Open up the MainPage.xaml and add a handler for the KeyDown event:

<Page ... xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" KeyDown="Shell_KeyDown">

    <SplitView x:Name="NavigationPane">
    ...
</Page>

Then, in MainPage.xaml.cs and type in the following code:

///
<summary>
/// Default keyboard focus movement for any unhandled keyboarding
/// </summary>

/// <param name="sender"></param>
/// <param name="e"></param>
private void Shell_KeyDown(object sender, KeyRoutedEventArgs e)
{
    FocusNavigationDirection direction = FocusNavigationDirection.None;

    switch (e.Key)
    {
        // both Space and Enter will trigger navigation

        case Windows.System.VirtualKey.Space:
        case Windows.System.VirtualKey.Enter:
            {
                var control = FocusManager.GetFocusedElement() as Control;
                var option = control as RadioButton;
                if (option != null)
                {
                    var automation = new RadioButtonAutomationPeer(option);
                    automation.Select();
                }
            }
            return;

        // otherwise, find next focusable element in the appropriate direction

        case Windows.System.VirtualKey.Left:
        case Windows.System.VirtualKey.GamepadDPadLeft:
        case Windows.System.VirtualKey.GamepadLeftThumbstickLeft:
        case Windows.System.VirtualKey.NavigationLeft:
            direction = FocusNavigationDirection.Left;
            break;
        case Windows.System.VirtualKey.Right:
        case Windows.System.VirtualKey.GamepadDPadRight:
        case Windows.System.VirtualKey.GamepadLeftThumbstickRight:
        case Windows.System.VirtualKey.NavigationRight:
            direction = FocusNavigationDirection.Right;
            break;

        case Windows.System.VirtualKey.Up:
        case Windows.System.VirtualKey.GamepadDPadUp:
        case Windows.System.VirtualKey.GamepadLeftThumbstickUp:
        case Windows.System.VirtualKey.NavigationUp:
            direction = FocusNavigationDirection.Up;
            break;

        case Windows.System.VirtualKey.Down:
        case Windows.System.VirtualKey.GamepadDPadDown:
        case Windows.System.VirtualKey.GamepadLeftThumbstickDown:
        case Windows.System.VirtualKey.NavigationDown:
            direction = FocusNavigationDirection.Down;
            break;
    }

    if (direction != FocusNavigationDirection.None)
    {
        var control = FocusManager.FindNextFocusableElement(direction) as Control;
        if (control != null)
        {
            control.Focus(FocusState.Programmatic);
            e.Handled = true;
        }
    }
}

The code is divided into two parts.

The first part handles the Space and Enter key so that it selects the currently selected button. In order to do that, one has to use the RadioButtonAutomationPeer class to programmatically select the associated RadioButton. That way, when you press Enter, the RadioButton is selected and this, in turn, triggers navigation to the appropriate page.

The second part of the code handles the Arrow keys and equivalent keys coming from controllers and game pads. This searches for and set the focus to the next focusable element available in the given direction. For instance, if the currently selected option is the Option 1 RadioButton, a press on the Down key will find the Option 2 RadioButton. That way, selecting a given control using the Arrow keys is very simple.

The last modification we need to make is to fix the incorrect display of the focus rectangle when the SplitView is in Compact DisplayMode. Because the Compact display mode clips the content of the navigation pane, the result is somewhat unexpected:

In MainPage.xaml.cs modify the implementation of the HamburgerButton_Click event handler and add a call to a method that resizes the controls hosted on the SplitView pane:

private void HamburgerButton_Click(object sender, RoutedEventArgs e)
{
    NavigationPane.IsPaneOpen = !NavigationPane.IsPaneOpen;

    ResizeOptions();
}

///
<summary>
/// Fix the clipped focus rectangle when SplitView DisplayMode is Compact
/// </summary>

private void ResizeOptions()
{
    // calculate the actual width of the navigation pane

    var width = NavigationPane.CompactPaneLength;
    if (NavigationPane.IsPaneOpen)
        width = NavigationPane.OpenPaneLength;

    // change the width of all control in the navigation pane

    HamburgerButton.Width = width;

    foreach (var option in NavigationMenu.Children)
    {
        var radioButton = (option as RadioButton);
        if (radioButton != null)
            radioButton.Width = width;
    }
}

Mouse Hover feedback

In to improve the feedback to the user as to whether a particular control can be selected or pressed, one has to change the color of the control when the mouse hovers above it. Because we already have some styles for the HamburgerButton and the navigation RadioButton controls, it is very easy to add this feature.

A quick look into the default generic.xaml file that ships with the SDK alllows one to copy and paste the default Template for a given control. This file is located in the following folder:

For instance, the default Button template defines the following Common States such as Normal, PointOver Pressed and Disabled. Likewize, the default RadioButton Template defines the same Common States (because it is also a Button) as well as the following CheckedStates such as Checked, Unchecked and Indeterminate.

We are only interested in the PointerOver and Pressed states for the HamburgerButton and the RadioButton controls.

So, open up Themes\Generic.xaml and add the following states in both styles MenuItemButtonStyle and NavigationButtonStyle:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="Common">
        <VisualState x:Name="Normal" />
        <VisualState x:Name="PointerOver">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames
                    Storyboard.TargetName="RootGrid"
                    Storyboard.TargetProperty="Background">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListLowBrush}" />
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
        <VisualState x:Name="Pressed">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames
                    Storyboard.TargetName="RootGrid"
                    Storyboard.TargetProperty="Background">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListMediumBrush}" />
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
        <VisualState x:Name="Disabled" />
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Posted in UWP | Tagged , , | 1 Comment

Building a Hamburger Menu for your Universal App

One of the first thing to do when building a Universal Windows Platform (UWP) Application is to choose how the application will present its features to the user. The Hamburger Menu is one of the most popular designs for implementing navigation in a universal app. Its use is pervasive across all Microsoft apps and a lot of third-party apps as well. Evidently, the SafetyBox App will also feature navigation based upon a Hamburger Menu.

I have found a lot of information about building such a feature in my app. Specifically, the following articles or samples were used as inspiration for this article:

However, in this article, I will try to present a consolidated approach that makes sense and present the concepts in the order I stumbled upon when trying to implement this menu in my own app.

Creating the “Chrome” of the App in a Shell.xaml Page

In order to power a Hamburger-based navigation menu, one has to use the SplitView control, designed to separate the contents of a page into two parts:

  • The left part, called the navigation Pane, consisting in the hamburger menu itself.
  • The right part, designed to host a Frame, consisting in any page that the user wants to navigate to.

The navigation pane can be invisible, or displayed in Compact or Open mode. The Compact mode typically displays only the hamburger button itself and the navigation options in the form of icons. The Open mode diplays additional text for the navigation options, as well as a richer set of interactive options if the application requires it.

A default project for building universal applications create an App.xaml application object as well as a MainPage.xaml page. One of the first task when implementing the Hamburger menu is to change the MainPage.xaml to act as a Shell that provides navigations to other pages.

There are several options for laying out the Hamburger menu.

One popular option is the one used by most builtin MSN/Live Apps, such as News, Finances, Weather, etc. This is done with the following layout, where you can notice that the Hamburger button is actually not part of the SplitView pane.

A snapshot of the News MSN App where the hamburger button is not part of the SplitView pane.

Another option is to include the Hamburger button inside the SplitView pane. This approach is used by the Groove App, for instance.

A snapshot of the Groove Music App where the hamburger button is part of the SplitView pane.

In this article, I will present a solution for implementing the latter option, similar to the Groove Music App.

Another choice to make is how to implement the SplitView pane menu items.

I have personally chosen to use RadioButton controls for several reasons. First, conceptually, the options in a SplitView pane can be thought of as a group of mutually exclusive options. Second, it is very easy to apply custom styling to a RadioButton via a redefined control template. Since styling is easy, it is also easy to include support for changing the colors on mouse/pointer hover.

Another option would have been to use a ListView control with single selection enabled, for instance. A ListView is a bit more cumbersome to use, however, because have to use a custom class that represent ListView items and use a DataTemplate to apply styling and custom behavior. Also, one has to include special code to support navigating the list with the keyboard without triggering item selection.

With that said, let’s dive into the code!

Starting from a blank project for a universal application, make the following changes:

  1. First, you may optionally want to rename MainPage.xaml to Shell.xaml.
  2. Open MainPage.xaml (or Shell.xaml), and replace the empty Grid control with the following code:
<SplitView x:Name="NavigationPane" DisplayMode="CompactInline">
  <SplitView.Pane>

    <Grid>

      <Grid.RowDefinitions>
        <RowDefinition Height="44" />
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>

      <Button x:Name="HamburgerButton" Grid.Row="0" Style="{StaticResource MenuItemButtonStyle}" Tag="& #xE700;" Click="HamburgerButton_Click" />

      <StackPanel x:Name="NavigationMenu" Orientation="Vertical" Grid.Row="1">
        <RadioButton x:Name="Option1"
           GroupName="Group1"
           Style="{StaticResource NavigationButtonStyle}"
           Tag="& #xE76E;"
           Checked="Option1Button_Checked"
           Content="Option 1"
           />
        <RadioButton x:Name="Option2"
           GroupName="Group1"
           Style="{StaticResource NavigationButtonStyle}"
           Tag="& #xE76E;"
           Checked="Option2Button_Checked"
           Content="Option 2"
           />
      </StackPanel>

    </Grid>

  </SplitView.Pane>
  <SplitView.Content>

    <Frame x:Name="Content">
      <Frame.ContentTransitions>
        <TransitionCollection>
          <NavigationThemeTransition>
            <NavigationThemeTransition.DefaultNavigationTransitionInfo>
              <EntranceNavigationTransitionInfo/>
            </NavigationThemeTransition.DefaultNavigationTransitionInfo>
          </NavigationThemeTransition>
        </TransitionCollection>
      </Frame.ContentTransitions>
    </Frame>
  </SplitView.Content>
</SplitView>

This code uses the otherwise unused Tag property of XAML controls to store the icon associated with a particular option. This icon is designated with a code that maps to the character code using the Windows 10 font named Segoe MDL Assets 2.

  1. Open MainPage.xaml.cs (or Shell.xaml.cs) and paste the following code:
public Frame AppFrame { get { return Content; } }

private void Option1Button_Checked(object sender, RoutedEventArgs e)
{

}

private void Option2Button_Checked(object sender, RoutedEventArgs e)
{

}

private void HamburgerButton_Click(object sender, RoutedEventArgs e)
{
    NavigationPane.IsPaneOpen = !NavigationPane.IsPaneOpen;
}

The layout code makes use of static resources to style the hamburger button as well as the radio buttons that act as navigation options:

  1. Open App.xaml and past the following code to enable access to additional resources:
<Application.Resources>
  <ResourceDictionary>

    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="Themes/Generic.xaml"/>
    </ResourceDictionary.MergedDictionaries>

  </ResourceDictionary>
</Application.Resources>


  1. Create a file named Themes\Generic.xaml to host the resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


<Style TargetType="Button" x:Name="MenuItemButtonStyle" x:Key="MenuItemButtonStyle">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
    <Setter Property="MinWidth" Value="{ThemeResource SplitViewCompactPaneThemeLength}" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">

          <Grid x:Name="RootGrid">

            <Grid.RowDefinitions>
              <RowDefinition Height="44" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="48" />
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
        
            <!-- use extra left margin to align the icon with NavigationButtonStyle'd controls -->
            <FontIcon Grid.Column="0"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                Margin="12,8,8,8"
                FontFamily="{ThemeResource SymbolThemeFontFamily}"
                Glyph="{TemplateBinding Tag}"
                />
            <TextBlock Grid.Column="1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Margin="8,8,8,8"
                Text="{TemplateBinding Content}"
                />
          </Grid>

        </ControlTemplate>
      </Setter.Value>
    </Setter>

  </Style>

<Style TargetType="RadioButton" x:Name="NavigationButtonStyle" x:Key="NavigationButtonStyle">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
    <Setter Property="MinWidth" Value="{ThemeResource SplitViewCompactPaneThemeLength}" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="RadioButton">

          <Grid x:Name="RootGrid">

            <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CheckedStates">
                <VisualState x:Name="Checked">
                  <Storyboard>
                    <ColorAnimation Storyboard.TargetName="Brush" Storyboard.TargetProperty="Color" From="{Binding Path=Background}" To="Blue" Duration="0:0:0" FillBehavior="HoldEnd" />
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="Unchecked" />
                <VisualState x:Name="Indeterminate" />
              </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>

            <Grid.RowDefinitions>
              <RowDefinition Height="44" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="4" />
              <ColumnDefinition Width="44" />
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Rectangle Grid.Column="0">
              <Rectangle.Fill>
                <SolidColorBrush x:Name="Brush" Color="{Binding Path=Background}" />
              </Rectangle.Fill>
            </Rectangle>

            <FontIcon Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="8,8,8,8" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="{TemplateBinding Tag}" />
            <TextBlock Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="8,8,8,8" Text="{TemplateBinding Content}" />

          </Grid>

        </ControlTemplate>
      </Setter.Value>
    </Setter>

  </Style>

</ResourceDictionary>

Two styles are defined in the Generic.xaml file.

The second style, named NavigationButtonStyle, is targeting RadioButton controls in the Hamburger menu. This style defines a layout with three columns. From left to right, one finds a) a selection-rectangle that highlights when the particular option is selected, b) an icon and, c) a text, displayed when the SplitView pane is open. This style includes visual triggers using a ColorAnimation to highlight the selection rectangle in blue when the particular option is selected.

The first style, named MenuItemButtonStyle, is targeting regular Button controls. In particular, this is the style used by the Hamburger button itself. You will notice that is uses a two-columns layout, with an icon and a text. Notice that the icon’s left margin is adjusted so as to be aligned with icons displayed by the first style.

At this stage, you should have a basic working application:

A basic universal app with a popular Hamburger navigation menu

In the next post, I will walk you through adding proper mouse/pointer hover feedback and handling navigation using the keyboard.

Posted in UWP | Tagged , | 3 Comments

Building Universal Windows Platform (UWP) App for Windows 10

You may have noticed it’s been a long time since I posted on this blog.
As a matter of fact, I have been really busy working on expanding our Middleware as a Service Platform at Moskitos.

At the same time, since the unveiling of Windows 10, I have been really excited about Universal Windows apps and wanted to contribute some ideas on this.
Although my day to day job is still very much grounded in the fields of Integration and Azure, I would like to primarily focus my interest around building Apps for the Windows 10 Universal Windows Platform on my spare time.

Announcing the Safety Box App

To this end, I’m setting up an open-source project on GitHub.
The purpose of the SafetyBox App is to be a Universal client for KeePass .kdbx database files.

I have been a long time user of the iPhone where one of the best password manager app is AgileBits’ 1Password.
Since I recently switched to using a Windows Phone, I figured I’d use a similar app from the store. However, I found the offering to be very poor. I found that the 1Password app only works in read-only mode and is lacking compared to its iOS counterpart. So I switched to using KeePass at the same time. Again, there are a couple of applications in the Store for KeePass, but they are either old or not as feature-rich as I may like them to be. Additionally, I think the user experience for these apps can be improved.

That’s why I decided to share my contributions as an open-source project.

Since I’m new to building XAML apps – I must have lived on another planet during the Silverlight and Windows 8 eras – I have a a lot to learn!
Anyway, I have reserved a name in the Windows Store and this blog will record my attempt at publishing the application to the Windows Store in due course…

Thanks for reading.

Posted in UWP | Leave a comment