jeudi 30 novembre 2017

Building your Shell for your UWP app, quick tutorial on how to get started [XAML,C#]

In this tutorial we will see how to setup your shell in your UWP application using the UWP Community Toolkit HamburgerMenu control.  I really liked the shell that was done for the sample app of the UWP Community Toolkit and thus we will try to do the same thing!

The HamburgerMenu control in the UWP Community Toolkit sample has an ineteresting way or using narrow and wide states.  It allow us to have a hamburger menu when the app is in narrow mode or have the navigation at the top when we are in the wide mode, this is what we are going to reproduce.

This is when you are in wide mode:



This is when you are in Narrow mode:



And that is what we are going to setup in this tutorial!

Lets start, first we need create a page called Shell.xaml/.cs this will be our shell of our application. 

Next we will create a HamburgerMenu control that we will named x:Name="HamburgerMenu" inside the HamburgerMenu tag we will add a VisualStateManager which will allow us to change between the Narrow and Wide States of the app.  Depending on which state we are in we will need to change the style and the ItemTample of the HamburgerMenu.

 <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="WindowStates">
                <VisualState x:Name="NarrowState">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="HamburgerMenu.Style" Value="{StaticResource VerticalHamburgerMenu}" />
                        <Setter Target="HamburgerMenu.ItemTemplate" Value="{StaticResource VerticalItemTemplate}" />

                        <Setter Target="HamburgerMenu.IsPaneOpen" Value="False" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="WideState">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="700" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="HamburgerMenu.Style" Value="{StaticResource HorizontalHamburgerMenu}" />
                        <Setter Target="HamburgerMenu.ItemTemplate" Value="{StaticResource HorizontalItemTemplate}" />

                        <Setter Target="HamburgerMenu.PaneBackground" Value="White" />
                        <Setter Target="HamburgerMenu.PaneForeground" Value="Black" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>


For this page we will add 2 DataTemplates :

 <Page.Resources>
        <DataTemplate x:Key="HorizontalItemTemplate" x:DataType="local:SampleCategory">
            <Grid>
                <TextBlock
                    Grid.Column="1"
                    VerticalAlignment="Center"
                    FontFamily="Segoe UI"
                    FontSize="15px"
                    FontWeight="Normal"
                    Foreground="Black"
                    Text="{x:Bind Name}" />
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="VerticalItemTemplate" x:DataType="local:SampleCategory">
            <Grid>
                <TextBlock
                    Padding="30,14"
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Top"
                    FontFamily="Segoe UI"
                    FontSize="20px"
                    FontWeight="SemiBold"
                    Foreground="White"
                    Opacity="0.8"
                    Text="{x:Bind Name}" />
            </Grid>
        </DataTemplate>
    </Page.Resources>

Next we will need to set the different styles for the HamburgerMenu which you can find here, most of these style were taken from the sample UWP app of the toolkit and reworked to fit our needs you can check it out here.


In our Shell.xaml.cs file we will add 3 properties:

 public static Shell Current { get; private set; }
So that we can acces the shell from anywhere in the application.

private NavViewModel _currentSample;
So that we know under which tab we arer navigating

private List<NavViewModel> NavigationViews { get; set; }
To load the different view that should be accessible in the nav bar.


Next we need to work on the OnNavigatedTo method, here we are going to want to populate our NavigationViews property and bind it to our HamburgerMenu as follows:

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            NavigationFrame.Navigating += NavigationFrame_Navigating;

            NavigationViews = new List<NavViewModel>();

            NavigationViews.Add(new NavViewModel() { Name = "Red view", PageView = "RedView" });
            NavigationViews.Add(new NavViewModel() { Name = "Blue view", PageView = "BlueView" });
            NavigationViews.Add(new NavViewModel() { Name = "Orange view", PageView = "OrangeView" });

            HamburgerMenu.ItemsSource = NavigationViews;

            NavigationFrame.Navigate(typeof(BlueView));
        }


You should have something like this in the end:

        public static Shell Current { get; private set; }
        private NavViewModel _currentSample;
        private List<NavViewModel> NavigationViews { get; set; }


        public Shell()
        {
            this.InitializeComponent();
            Current = this;
        }

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            NavigationFrame.Navigating += NavigationFrame_Navigating;

            NavigationViews = new List<NavViewModel>();

            NavigationViews.Add(new NavViewModel() { Name = "Red view", PageView = "RedView" });
            NavigationViews.Add(new NavViewModel() { Name = "Blue view", PageView = "BlueView" });
            NavigationViews.Add(new NavViewModel() { Name = "Orange view", PageView = "OrangeView" });

            HamburgerMenu.ItemsSource = NavigationViews;

            NavigationFrame.Navigate(typeof(BlueView));
        }


Next we will what to handle the NavigationFrame_Navigating this will allow us to make sure that the correct tab is selected deepending on which tab we click, tap, etc

        private async void NavigationFrame_Navigating(object sender, NavigatingCancelEventArgs navigationEventArgs)
        {
            if (navigationEventArgs.Parameter != null && NavigationViews != null)
            {
                _currentSample = NavigationViews.Where(a => a.Name == navigationEventArgs.Parameter).FirstOrDefault();
            }
            else
            {
                _currentSample = null;
            }

            await SetHamburgerMenuSelection();
        }



        private async Task SetHamburgerMenuSelection()
        {
            if (_currentSample != null)
            {
                if (HamburgerMenu.Items.Contains(_currentSample))
                {
                    HamburgerMenu.SelectedItem = _currentSample;
                    HamburgerMenu.SelectedOptionsItem = null;
                }
            }
            else
            {
                try
                {
                    HamburgerMenu.SelectedItem = null;
                    HamburgerMenu.SelectedOptionsIndex = 0;
                }
                catch (Exception e)
                {
                    //fail in silence
                }
            }
        }

and there you have it =), have fun paying around with the demo app.

Happy coding!

Full Source code of the app here.