© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
S. HoeflingGetting Started with the Uno Platform and WinUI 3https://doi.org/10.1007/978-1-4842-8248-9_6

6. Master-Detail Menu and Dashboard

Skye Hoefling1  
(1)
Rochester, NY, USA
 

Our application is going to have a main dashboard where all the operations of the authenticated user occur. You may also refer to this type of page as the main page or an application shell. In our application we will call it the dashboard, which is going to be our first deep dive into layout and menus. In Uno Platform you have several different ways to build your view stack and menus. In this chapter we are going to highlight some of the standard techniques and then build a flyout menu–style dashboard.

Navigation Types

When building any application, you need to decide what type of navigation you want to implement. There are several options to choose from, and each of them has its own advantages:
  • Frame-based navigation (view stack)

  • Tabbed menu

  • Master-detail menu

These standard techniques are not mutually exclusive and can be combined to create the right user experience.

Frame-Based Navigation (View Stack)

Frame-based navigation , also known as view stack navigation , is when you display a new page on top of an existing page. Consider your page hierarchy as a stack data structure that is First-In-Last-Out, which means the newest item is the first to be removed. When we use the term view stack, nothing changes, and the data structure works just as defined. This means as you push new pages onto your view stack, you go back to the previous page by popping the most recent page off the stack. See diagram of View Stack in Figure 6-1.

The stack diagram depicts a frame based navigator for a new page as well as existing pages.

View stack navigation is typically used with other navigation techniques. Consider you are building a shopping app and a user selects an item they want to purchase and see more details of. You could use a view stack and push a new page onto the screen that displays all the details. When they click the back button, the page is popped off the view stack, and they are back to the list of items.

Tabbed Menu

A tabbed menu is when you have several menu options always available as quick icons in a tab bar. When the user selects the tab, the main display area updates to the new page. By design all major pages are one tap away for the user, which makes it very easy to navigate to the various pages in your application.

You are not limited to where you display the tabs. You can place your tabbed menu horizontally on the top or bottom of the page. You can also place your tabbed menu vertically on the left or right of the page.

A challenge with tabbed menus is you can only display so many tabs on the screen at once. In smaller devices such as the typical Android or iOS phone, you can only have four or five menu options.

Master-Detail Menu

A master-detail menu is popular on small form factor devices such as mobile phones where there is an icon with three horizontal lines commonly called the “hamburger” icon, which opens the menu. This menu can open from the left or the right portion of the screen depending on application design. The master-detail menu is popular because it is an easy way to store several options that can’t be displayed in a tabbed menu or another navigation structure.

The master-detail menu is not just for small form factor devices but is very useful on tablets as well as desktop or laptop computers. In the larger form factors, the menu is typically always opened with the content area in the middle of the application. The menu can then be made responsive for the desktop or laptop scenario. As the screen size shrinks, it will fall back to a traditional flyout menu that is activated by the “hamburger” icon.

Combination

When building your application, you are not limited to just one navigation structure . It is common to mix and match these techniques where it makes the most sense. You may end up using all these techniques or even some that aren’t listed.

A common pairing is tabbed menus and flyout menus, where the popular options are listed in the tabbed menu. Then there is a “More” or “Settings” icon that opens a master-detail menu, which has the less common options.

Create a Dashboard Layout

The first thing we need to do is build our dashboard core layout. Once we have this built, we can start adding menu options and event handlers to properly implement the master-detail design pattern .

In Uno Platform there are many ways to implement your master-detail menu:
  • Master-detail control

  • NavigationView

  • Visual state manager

We are going to use the NavigationView as it provides everything we need to create a simple master-detail menu and it allows us to customize the menu completely. The NavigationView control is a powerful control that provides a master-detail implementation that we can customize. On larger displays we can have a left pane or top pane that is always visible. On smaller displays we can have it collapsed or hidden and only display it when we want to perform an operation in the pane menu. You can learn more about the NavigationView from the official WinUI documentation:

To get started we need to create a new Views folder in the shared project. This folder will contain all of our user interface pages, as well as any helper class to support those pages.

Note

The typical convention in XAML-based applications is to have a Views folder or a Pages folder. It stores the various pages and sometimes more. This is entirely convention based and is up to you and your team. We are going to use a Views folder for this book.

We are going to be creating some new views in this chapter. Once you have the new folder created, move the existing LoginPage into this folder and then add four new blank Uno Platform pages called Dashboard, MyFilesPage , RecentFilesPage , and SharedFilesPage . See Figure 6-2 for a screenshot of the Visual Studio Solution Explorer.

Note

It is best to create your XAML files using the Visual Studio extension, which will generate the XAML file and code behind correctly. If you have trouble, the complete code snippets included with this chapter will help you out.

A screenshot depicts the solution explorer dashboard for creating new blank pages, namely the dashboard dot Xaml, myFliespage dot Xaml, RecentFiles dot Xaml, and SharedFilespage dot Xaml.

Figure 6-2

Visual Studio Solution Explorer – Views folder

In the Dashboard.xaml update your XAML to have the basic structure for the NavigationView . Inside the control add the following elements: PaneCustomContent, MenuItems, Header, and a standard Frame. See code snippet in Listing 6-1.
<Page>
  <!-- root container for the dashboard -->
  <NavigationView x:Name="menu">
    <!-- flyout menu header content -->
    <NavigationView.PaneCustomContent>
    </NavigationView.PaneCustomContent>
    <!-- flyout menu items -->
    <NavigationView.MenuItems>
    </NavigationView.MenuItems>
    <!-- main content area header -->
    <NavigationView.Header>
    </NavigationView.Header>
    <!-- main content area -->
    <Frame x:Name="contentFrame" />
  </NavigationView>
</Page>
Listing 6-1

Dashboard.xaml – basic layout with NavigationView. The xmlns have been omitted

PaneCustomContent

This allows you to place any content that will render above the menu items in the flyout menu. This will be used to display information about the user such as name and their profile photo.

MenuItems

This defines all the menu options that the user can navigate to from the flyout menu. In this container you will specify a list of NavigationViewItems, and each one will define an icon and text to keep a consistent menu experience.

Header

This specifies a standard content object where you can control what your page header looks like. The page header is the content that is rendered just above the main content area to the right of the flyout menu.

Frame

The content frame is where the current page or main content is rendered.

Now that we have the basic structure, we can start implementing various pieces to the dashboard page. Let’s start by overriding some of the default values and configuring the platform targets.

Next, start adding properties to the NavigationView element. Update the control to match the code snippet in Listing 6-2.
<NavigationView x:Name="menu"
  IsBackButtonVisible="Collapsed"
  IsPaneOpen="False"
  IsSettingsVisible="False"
  ItemInvoked="MenuItemSelected">
  <!-- omitted code -->
</NavigationView>
Listing 6-2

Dashboard.xaml – NavigationView properties

These controls will configure the look and feel of the NavigationView to best match a standard flyout menu for the Windows target. The last property ItemInvoked configures an event listener that we will be using later. You will need to go into the code behind and create a method stub; otherwise, your application won’t compile. See the method stub for the code behind in Listing 6-3.
Void MenuItemSelected(
  NavigationView sender,
  NavigationViewItemInvokedEventArgs args)
{
  // todo – add implementation
}
Listing 6-3

Dashboard.xaml.cs – MenuItemSelected method stub for the event in NavigationView

In the <Page> declaration at the top of the XAML file , add the following xmlns definitions to allow us to add specific rules for the various target platforms. See xmlns definitions in Listing 6-4.
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:android="http://uno.ui/android"
xmlns:ios="http://uno.ui/ios"
xmlns:macos="http://uno.ui/macos"
xmlns:wasm="http://uno.ui/wasm"
xmlns:skia="http://uno.ui/skia"
Listing 6-4

Dashboard.xaml – XAML xmlns definitions

When adding platform-specific xmlns to the root element, you need to make sure they are added to the Ignorable attribute; otherwise, you will get compilation errors. See ignorables in Listing 6-5.
mc:Ignorable="d android ios macos wasm skia"
Listing 6-5

Dashboard.xaml – XAML xmlns ignorables

Let’s add special configuration rules in for those so we can have the NavigationView render correctly across the platforms. Update your NavigationView to match the code snippet in Listing 6-6.
<NavigationView
  x:Name="menu"
  IsBackButtonVisible="Collapsed"
  IsPaneOpen="False"
  win:PaneDisplayMode="Left"
  wasm:PaneDisplayMode="Left"
  skia:PaneDisplayMode="Left"
  android:PaneDisplayMode="LeftMinimal"
  ios:PaneDisplayMode="LeftMinimal"
  macos:PaneDisplayMode="Left"
  win:IsPaneToggleButtonVisible="False"
  wasm:IsPaneToggleButtonVisible="False"
  skia:IsPaneToggleButtonVisible="False"
  macos:IsPaneToggleButtonVisible="False"
  IsSettingsVisible="False"
  ItemInvoked="MenuItemSelected">
  <!-- omitted code -->
</NavigationView>
Listing 6-6

Dashboard.xaml – NavigationView updated pane display rules for the various target platforms

This may appear like a lot of code, but we are only modifying IsPaneToggleButtonVisible (hamburger menu icon visibility) and PaneDisplayMode (always open or flyout menu style). Since we need to customize two properties for many platforms, the code ends up being quite verbose.

Next, let’s define the PaneCustomContent area , which is a header that renders above the MenuItems. In our application we are going to use this to display the user’s name and email address. We stubbed this out earlier when we built the basic structure. This control is a simple UIElement , which makes it easy for us to add any type of UIElement control such as a page or in our case a StackPanel to it. Update the PaneCustomContent to match the code snippet in Listing 6-7.
<NavigationView.PaneCustomContent>
  <StackPanel Margin="5, 0, 5, 0" Spacing="10">
    <TextBlock
      Text="John Smith"
      FontSize="20"
      HorizontalTextAlignment="Center" />
    <TextBlock
      Text="[email protected]"
      FontSize="18"
      HorizontalTextAlignment="Center" />
    <Border
      Height="1"
      Background="Black"
      Margin="10, 0" />
  </StackPanel>
</NavigationView.PaneCustomContent>
Listing 6-7

Dashboard.xaml – NavigationView PaneCustomContent implementation that renders at the top of the pane

The code in Listing 6-7 displays the user’s name and email and a horizontal line to separate the header section from the MenuItems section. Currently we are hard-coding the name and email address, but in future chapters when we connect to the Microsoft Graph , we will be using the logged-in user’s information.

Next, we are going to implement the MenuItems, which control all the available options the user can select in the flyout menu. This container accepts a special menu item control called NavigationViewItem . This control defines an icon and content or text to display. Let’s update our stubbed-out section to match the snippet in Listing 6-8.
<NavigationView.MenuItems>
  <NavigationViewItem
    x:Name="myFiles"
    Icon="Play"
    Content="My Files"
    IsSelected="True" />
  <NavigationViewItem
    x:Name="recentFiles"
    Icon="Save"
    Content="Recent" />
  <NavigationViewItem
    x:Name="sharedFiles"
    Icon="Refresh"
    Content="Shared" />
  <NavigationViewItem
    x:Name="signOut"
    Icon="ClosePane"
    Content="Sign Out" />
</NavigationView.MenuItems>
Listing 6-8

Dashboard.xaml – NavigationViewItems

The Icon property uses the standard Windows Segoe MDL2 font. It is easiest to use the official Microsoft documentation to see what the icons look like:

Depending on the version of Uno Platform, you may need to download the font file and add it to your various platform targets. Custom fonts are covered in Chapter 7 .

Notice that each NavigationViewItem has a special x:Name property to define a name. This is intentional, and when we get to the code behind portion of this chapter, we will be using the names to configure the event handler that opens the various pages.

Next, we will be adding the page header. Let’s add a simple TextBlock control that displays the message “My Files ”. We will expand upon this later, but for now we are going to keep this section simple. See the code snippet in Listing 6-9.
<NavigationView.Header>
  <Border>
    <TextBlock
      Text="My Files"
      HorizontalAlignment="Left"
      FontSize="22"
      FontWeight="Bold" />
  </Border>
</NavigationView.Header>
Listing 6-9

Dashboard.xaml – NavigationView header

The final element of our NavigationView object is the main content area. You will add a <Frame> right before the closing </NavigationView> tag. The NavigationView has an implied container that allows you to specify the content anywhere within the NavigationView. If you choose you can use the explicit name and insert it into the NavigationView.Content. See the code snippet in Listing 6-10.
<NavigationView>
  <!-- omitted code -->
  <Frame x:Name="contentFrame" Padding="15, 10" />
</NavigationView>
Listing 6-10

Dashboard.xaml – NavigationView frame content using the implicit approach

If you want to be explicit about your declaration and more verbose, you can use the following XAML. Both are correct. See the code snippet in Listing 6-11.
<NavigationView>
  <!-- omitted code -->
  <NavigationView.Content>
    <Frame x:Name="contentFrame" Padding="15, 10" />
  </NavigationView.Content>
</NavigationView>
Listing 6-11

Dashboard.xaml – NavigationView frame content using the explicit approach

The choice is yours on how you want to use the control. We are going to use the implicit approach from Listing 6-10 as it generates less verbose XAML.

The XAML of our dashboard is complete. See completed XAML in Listing 6-12.
<Page>
  <NavigationView
    x:Name="menu"
    IsBackButtonVisible="Collapsed"
    IsPaneOpen="False"
    win:PaneDisplayMode="Left"
    wasm:PaneDisplayMode="Left"
    skia:PaneDisplayMode="Left"
    android:PaneDisplayMode="LeftMinimal"
    ios:PaneDisplayMode="LeftMinimal"
    macos:PaneDisplayMode="Left"
    win:IsPaneToggleButtonVisible="False"
    wasm:IsPaneToggleButtonVisible="False"
    skia:IsPaneToggleButtonVisible="False"
    macos:IsPaneToggleButtonVisible="False"
    IsSettingsVisible="False"
    ItemInvoked="MenuItemSelected">
    <NavigationView.PaneCustomContent>
      <StackPanel Margin="5, 0, 5, 0" Spacing="10">
        <TextBlock
          Text="John Smith"
          FontSize="20"
          HorizontalTextAlignment="Center" />
        <TextBlock
          Text="[email protected]"
          FontSize="18"
          HorizontalTextAlignment="Center" />
        <Frame
          Height="1"
          Background="Black"
          Margin="10, 0" />
      </StackPanel>
    </NavigationView.PaneCustomContent>
    <NavigationView.MenuItems>
      <NavigationViewItem
        x:Name="myFiles"
        Icon="Play"
        Content="My Files"
        IsSelected="True" />
      <NavigationViewItem
        x:Name="recentFiles"
        Icon="Save"
        Content="Recent" />
      <NavigationViewItem
        x:Name="sharedFiles"
        Icon="Refresh"
        Content="Shared" />
      <NavigationViewItem
        x:Name="signOut"
        Icon="ClosePane"
        Content="Sign Out" />
    </NavigationView.MenuItems>
    <NavigationView.Header>
      <Frame>
        <TextBlock Text="My Files" />
      </Frame>
    </NavigationView.Header>
    <Frame x:Name="contentFrame" Padding="15, 10" />
  </NavigationView>
</Page>
Listing 6-12

Dashboard.xaml completed XAML

The application should compile and run at this point, but before we go and test it, let’s configure our default page to load. Make the My Files page be your default page by adding the code in Listing 6-13 to your constructor in the code behind in Dashboard.xaml.cs.
public Dashboard()
{
  InitializeComponent();
  contentFrame.Navigate(
    typeof(MyFilesPage),
    null,
    new SuppressNavigationTransitionInfo());
}
Listing 6-13

Dashboard.xaml.cs – constructor initialization

Note

In the navigation code from Listing 6-13, we explicitly pass the SuppressNavigationTransitionInfo, which tells the navigation system to not render any animation behavior. There are several different options to provide in the Navigate() method to render rich animations.

Now we can run the application across the various platforms and figure out if we need to make any platform-specific code changes . See the running application in Figure 6-3 for Windows, Figure 6-4 for WASM, Figure 6-5 for Android, and Figure 6-6 for iOS.

Windows

A screenshot depicts the John Smiths Win U I desktop with the email john dot smith at the rate myemail dot com and my files selected to upload the flies.

Figure 6-3

Windows application with the flyout menu

Note

The header object that reads “My Files” is pushed to the right, which is by design of the WinUI control we are using. This behavior will be consistent across the various platforms. As you get more content in the pages, it will look better. If you need to have the control left-aligned, you will need to add some custom styles to apply a negative margin or create your own control template.

WASM

A screenshot depicts the Uno drive dashboard of John Smiths with the email john dot smith at the rate myemail.com and my files selected to upload the flies.

Figure 6-4

WebAssembly application with the flyout menu

Android

In the following we have two screenshots , one for the flyout closed and one for it opened.

The two mobiles window represents John Smith's email menu with my files, recent, shared, recycled bin, and sign out, where the files menu is open.

Figure 6-5

Android application with the flyout menu

iOS

A mobile window represents my flies with my files page in i phone 13 pro max.

Figure 6-6

iOS application with the flyout menu in the top bar outside of the safe zone

Once you launch the application , you may notice that the flyout menu doesn’t work as the menu icon is in the top bar of the phone. This isn’t appealing for our design, and it is not functional, and the menu won’t open.

In this instance we will need to add an iOS-specific top margin to move the entire NavigationView down and into the safe zone of the phone.

Note

The safe zone is a mobile device term that was coined when iOS and Android devices started to introduce the top notch for the camera. Typically, you will want to not have any content rendered outside of the “safe zone” as you will not be able to guarantee that the user can interact with it. The “safe zone” is the area of the screen that the user can safely interact with using their touch events.

In your Dashboard.xaml file, add the following iOS-specific XAML for the NavigationView object to set a top margin. See the code snippet in Listing 6-14.
ios:Margin="0, 45, 0, 0"
Listing 6-14

Dashboard.xaml – iOS-specific margin to prevent text from rendering behind the notch

Your complete updated NavigationView XAML can be seen in Listing 6-15.
<NavigationView
  x:Name="menu"
  ios:Margin="0, 45, 0, 0"
  IsBackButtonVisible="Collapsed"
  IsPaneOpen="False"
  win:PaneDisplayMode="Left"
  wasm:PaneDisplayMode="Left"
  skia:PaneDisplayMode="Left"
  android:PaneDisplayMode="LeftMinimal"
  ios:PaneDisplayMode="LeftMinimal"
  macos:PaneDisplayMode="Left"
  win:IsPaneToggleButtonVisible="False"
  wasm:IsPaneToggleButtonVisible="False"
  skia:IsPaneToggleButtonVisible="False"
  macos:IsPaneToggleButtonVisible="False"
  IsSettingsVisible="False"
  ItemInvoked="MenuItemSelected">
Listing 6-15

Dashboard.xaml – complete NavigationView property declarations

You may want to adjust the margin to a different value, but we are going to use 45 as that gives us enough space between the top bar content and where we want our content to render. Launching the application again for iOS, you will now be able to interact with the flyout menu and open it up. See screenshot of iOS application in Figure 6-7.

The two mobiles window represents John Smith's email menu with my files selection, and the files menu is open in i phone 13 pro max phones.

Figure 6-7

iOS application with the added top margin and opened flyout menu

macOS

The macOS platform uses a very similar UI toolkit as iOS, which means behaviors on the iPhone will work on macOS. There is no notch on macOS, so we don’t need to handle the safe zone. We get all the screen space as we would with any other desktop app. See screenshot of macOS application in Figure 6-8.

A screenshot represents the John Smith email dashboard with the selection of my files in the macOS application.

Figure 6-8

macOS application with the flyout menu

Linux

In Linux the page renders almost exactly how we want it. The only problem is the default fonts are not loading correctly. We aren’t going to worry about this too much right now and will fix it in the next chapter, which will be a deep dive into custom fonts. See screenshot of Linux application in Figure 6-9.

A screenshot represents the John Smith email minimized dashboard with the selection of my files in the Linux application.

Figure 6-9

Linux application with the flyout menu

WPF

The WPF platform uses the same Skia engine as the Linux target. Everything renders as we expect, and there are no additional changes needed. See screenshot of WPF application in Figure 6-10.

A screenshot represents the John Smith email main window with the selection of my files in the WPF application.

Figure 6-10

WPF application with the flyout menu

Menu Navigation

At this point you should have your application rendering correctly for the left menu and flyout menu depending on the platform you are running it on. Let’s start adding some code in the code behind in Dashboard.xaml.cs that properly updates the main content area when selecting a menu option on the left.

In the last section, we defined the MenuItems , and each item in that collection had a unique name:
  • My Files: myFiles

  • Recent: recentFiles

  • Shared: sharedFiles

  • Recycle Bin: recycleBin

We will need to create an event handler that is triggered when a menu item is selected. This is built into the NavigationView using the ItemInvoked event . Earlier in the chapter, we stubbed out the method MenuItemSelected and added it to the NavigationView as an event listener. Since all the configuration is done, let’s add our first navigation. See the code snippet in Listing 6-16.
void MenuItemSelected(
  NavigationView sender,
  NavigationViewItemInvokedEventArgs args)
{
  Type pageType = default;
  if (myFiles == args.InvokedItemContainer)
    pageType = typeof(MyFilesPage);
  contentFrame.Navigate(
    pageType, null, new CommonNavigationTransitionInfo());
}
Listing 6-16

Dashboard.xaml.cs – initial implementation of the MenuItemSelected method

We can check the arguments of the event listener and if they match a well-known menu item in the application and navigate. The well-known menu item is the x:Name property that we defined earlier in the XAML. This makes it easier to check in the event listener for navigation. Once we have identified the page type we want to navigate to, we can tell the contentFrame to navigate to the new page.

Implement the rest of the navigation items as seen in Listing 6-17.
void MenuItemSelected(
  NavigationView sender,
  NavigationViewItemInvokedEventArgs args)
{
  Type pageType = default;
  if (myFiles == args.InvokedItemContainer)
    pageType = typeof(MyFilesPage);
  else if (recentFiles == args.InvokedItemContainer)
    pageType = typeof(RecentFilesPage);
  else if (sharedFiles == args.InvokedItemContainer)
    pageType = typeof(SharedFilesPage);
  else if (recycleBin == args.InvokedItemContainer)
    pageType = typeof(RecycleBinPage);
  contentFrame.Navigate(
    pageType, null, new CommonNavigationTransitionInfo());
}
Listing 6-17

Dashboard.xaml.cs – implementation of MenuItemSelected

This will finish implementing the menu items that open a page, but it won’t complete all menu items. We need to add a special implementation for “Sign Out” as that will log the user out of OneDrive. Since we haven’t connected to the Microsoft Graph yet, this case will just return and do nothing.

Add the special “Sign Out ” rules before all the other rules we implemented so far. See the code snippet in Listing 6-18.
  // Signout is not implemented
  if (signOut == args.InvokedItemContainer)
    return;
Listing 6-18

Dashboard.xaml.cs – MenuItemSelected snippet for sign-out logic

See completed code for MenuItemSelected in Listing 6-19.
void MenuItemSelected(
  NavigationView sender,
  NavigationViewItemInvokedEventArgs args)
{
  // Signout is not implemented
  if (signOut == args.InvokedItemContainer)
    return;
  Type pageType = default;
  if (myFiles == args.InvokedItemContainer)
    pageType = typeof(MyFilesPage);
  else if (recentFiles == args.InvokedItemContainer)
    pageType = typeof(RecentFilesPage);
  else if (sharedFiles == args.InvokedItemContainer)
    pageType = typeof(SharedFilesPage);
  else if (recycleBin == args.InvokedItemContainer)
    pageType = typeof(RecycleBinPage);
  else
    return;
  contentFrame.Navigate(
    pageType, null, new CommonNavigationTransitionInfo());
}
Listing 6-19

Dashboard.xaml.cs – complete implementation of MenuItemSelected

Now if you run the application, you will be able to click the menu items, and the pages will update. There was no need to add any special platform-specific code in this section as we only configured navigation rules.

Page Header

The page header as implemented is a simple Border control and is included in the main Dashboard page. As the application complexity expands, we are going to want to add additional rules to the page header such as adding new files. Let’s convert this to use a custom UserControl and decouple the XAML and code from the Dashboard page.

It is a good idea to keep your code organized. Let’s create a new top-level folder in the shared project called “Controls .” In this folder we will put our new page header UserControl . See Visual Studio Solution Explorer screenshot with new “Controls” folder in Figure 6-11.

A screenshot depicts the solution explorer dashboard, where the control folder is selected.

Figure 6-11

Visual Studio Solution Explorer with the new Controls folder

Add a new UserControl named HeaderControl to the “Controls” folder so we can begin the basic design. Let’s start with a horizontal Grid that will have a left column and a right column. The header title will be left-aligned, and the right column will contain buttons that are right-aligned. For now, let’s just add the title. See the code snippet in Listing 6-20.
<UserControl>
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock
      Grid.Column="0 "
      HorizontalAlignment="Left"
      Text="My Files"
      FontWeight="Bold" />
  </Grid>
</UserControl>
Listing 6-20

UserControl for the NavigationView header

We can implement the right column a little bit later. Let’s focus on the TextBlock and get the style correct for the various platforms. We know that sizing is going to be different, and we have some values for font size and padding to start with. Update the TextBlock to match our snippet in Listing 6-21.
<TextBlock
  Grid.Column="0"
  HorizontalAlignment="Left"
  Text="My Files"
  FontWeight="Bold"
  win:FontSize="22"
  skia:FontSize="22"
  wasm:FontSize="22"
  android:FontSize="16"
  macos:FontSize="22"
  ios:FontSize="20"
  android:Padding="0, 4" />
Listing 6-21

UserControl for NavigationView – TextBlock with platform-specific XAML

We have not added any platform-specific customization for iOS, macOS, or Skia, so we may need to add additional properties for those platforms when we look at the running application. We have our basic UserControl implemented, so we can edit the Dashboard.xaml to include it.

We have been using the NavigationView.Header , but the control pushes our header content to a position that we don’t want. Our goal is to make the header line up with the menu icon on mobile and be top left on desktop.

Let’s remove the NavigationView.Header block and merge it with our Frame content. We will do this by creating a Grid and adding the new HeaderControl as the first item and the existing Frame as the second item.

If you haven’t done so already, add a new xmlns definition that maps to the new control namespace. See the xmlns snippet in Listing 6-22.
xmlns:c="using:UnoDrive.Controls"
Listing 6-22

xmlns definition for UnoDrive.Controls

Then you can add our new HeaderControl to the Grid in the Dashboard.xaml. See the code snippet in Listing 6-23.
<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*"
  </Grid.RowDefinitions>
  <c:HeaderControl Grid.Row="0" />
  <Frame Grid.Row="1" x:Name="contentFrame" />
</Grid>
Listing 6-23

Dashboard.xaml – NavigationView content Grid that wraps the HeaderControl and the Frame

This new XAML will place all the content starting at the top leftmost point. We need to add a little bit of padding and spacing to get our look and feel just right.

Update the Grid as seen in Listing 6-24.
<Grid Padding="15, 10" RowSpacing="10">
Listing 6-24

Dashboard.xaml – NavigationView content Grid Padding and RowSpacing

This change will cause the HeaderControl to overlap the menu icon in smaller displays for Android and iOS. When we review the screenshots, we will make changes to push the text to the right slightly.

Let’s run the application and test all the platforms to make sure everything looks correct for the TextBlock . See screenshots of running application in Figure 6-12 for Windows, and Figure 6-13 for WASM, Figure 6-14 and Figure 6-15 for Android, Figure 6-16 and Figure 6-17 for iOS, Figure 6-18 for macOS, Figure 6-19 for Linux, and Figure 6-20 for WPF.

Windows

A screenshot depicts the John Smith email Win U I and my file selection to upload files with header control.

Figure 6-12

Windows application with the new HeaderControl

WASM

A screenshot depicts the Uno drive dashboard of John Smiths email and my file selection to upload files with the Web Assembly page header.

Figure 6-13

The WebAssembly application with the new HeaderControl

Android

A phone menu represents my files in the Android application with the overlapping of my menu.

Figure 6-14

Android application with HeaderControl overlapping the menu icon

The new design for the HeaderControl overlaps the menu icon, which renders an undesirable result. To solve this, we will add a special padding to push the entire header to the right. See the code snippet in Listing 6-25.
<c:HeaderControl android:Padding="30, 0, 0, 0" />
Listing 6-25

HeaderControl padding for Android

The completed XAML for this section can be seen in Listing 6-26.
<Grid Padding="15, 10" Spacing="10">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*"
  </Grid.RowDefinitions>
  <c:HeaderControl
    Grid.Row="0"
    android:Padding="30, 0, 0, 0" />
  <Frame Grid.Row="1" x:Name="contentFrame" />
</Grid>
Listing 6-26

Completed header main content code

Now if you run the Android application again, you will see the header content is pushed to the right, leaving enough space for the menu icon.

A phone menu represents my files in the Android application with the left padding.

Figure 6-15

Android application with left padding

iOS

A phone menu represents my files in the iOS application, where the My header is overlapped by menu icon.

Figure 6-16

iOS application with header overlapping the menu icon

The iOS application has the same problem as Android where the header text now overlaps the menu icon. Since the padding we added for Android was specific to that platform, we will apply an iOS-specific one. See the code snippet in Listing 6-27.
<c:HeaderControl
  android:Padding="30, 0, 0, 0"
  ios:Padding="30, 0, 0, 0" />
See the completed NavigationView main content Grid in Listing 6-27.
<Grid Padding="15, 10" Spacing="10">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*"
  </Grid.RowDefinitions>
  <c:HeaderControl
    Grid.Row="0"
    android:Padding="30, 0, 0, 0" />
    ios:Padding="30, 0, 0, 0" />
  <Frame Grid.Row="1" x:Name="contentFrame" />
</Grid>
Listing 6-27

Dashboard.xaml – NavigationView main content Grid complete code

Run the application again, and you should see the header text pushed to the right of the menu icon.

A phone menu represents my files in the iOS application.

Figure 6-17

iOS application with header text pushed to the right by left padding

macOS

A screenshot represents the John Smith email dashboard with the selection of my files on the macOS page with new header control.

Figure 6-18

macOS application with new HeaderControl

Linux

A screenshot represents the John Smith email minimized dashboard with the selection of my files on the Linux page with new header control.

Figure 6-19

Linux application with the new HeaderControl

WPF

A screenshot depicts the John Smith email main window and my files selected in W P F application.

Figure 6-20

WPF application with the new HeaderControl

Everything looks good across all the platforms, so we can move on. We want to add a right column, which will display buttons to perform actions. Let’s add a horizontal StackPanel that is aligned to the right of the page that wraps all content inside the PaneCustomContent. This will be our button group container to provide primary and secondary actions. For now, we are going to hard-code two buttons, New and Upload. See the code snippet in Listing 6-28.
<StackPanel
  Grid.Column="1"
  Orientation="Horizontal"
  HorizontalAlignment="Right"
  Spacing="10">
  <Button
    Background="#0078D4"
    Foreground="#FFFFFF"
    Content="New" />
  <Button Content="Upload" />
</StackPanel>
Listing 6-28

Dashboard.xaml – NavigationView PaneCustomContent two buttons

This will render the basic concept, but we want to use icons to help our user quickly identify what actions the buttons perform. In Uno Platform you can include powerful view containers inside the Button control ’s Content property. Let’s update both buttons to display an icon prior to the text. See code for the first button in Listing 6-29 and code for the second button in Listing 6-30.
<Button
  Background="#0078D4"
  Foreground="#FFFFFF">
  <Button.Content>
    <StackPanel
      Orientation="Horizontal"
      Spacing="10"
      Margin="5, 2">
      <FontIcon
        FontFamily="Segoe MDL2 Assets"
        Glpyh="&#xE109;"
        win:FontSize="14"
        skia:FontSize="14"
        wasm:FontSize="14"
        android:FontSize="18"
        macos:FontSize="14"
        ios:FontSize="18" />
      <TextBlock
        Text="New"
        win:FontSize="16"
        skia:FontSize="16"
        wasm:FontSize="16"
        android:Fontsize="12"
        macos:FontSize="16"
        ios:FontSize="16" />
    </StackPanel>
  <Button.Content>
</Button>
Listing 6-29

Dashboard.xaml – NavigationView PaneCustomContent first button (New) content

<Button>
  <Button.Content>
    <StackPanel
      Orientation="Horizontal"
      Spacing="10"
      Margin="5, 2">
      <FontIcon
        FontFamily="Segoe MDL2 Assets"
        Glpyh="&#xE11C;"
        win:FontSize="14"
        skia:FontSize="14"
        wasm:FontSize="14"
        android:FontSize="18"
        macos:FontSize="14"
        ios:FontSize="18" />
      <TextBlock
        Text="Upload"
        win:FontSize="16"
        skia:FontSize="16"
        wasm:FontSize="16"
        android:Fontsize="12"
        macos:FontSize="16"
        ios:FontSize="16" />
    </StackPanel>
  <Button.Content>
</Button>
Listing 6-30

Dashboard.xaml – NavigationView PaneCustomContent second button (Upload) content

Now let’s run the application and see how the right column looks and if there are any changes we need to make to any of the platforms. See Figure 6-21 for Windows, Figure 6-22 for WASM, Figure 6-23 for Android, Figure 6-24 and Figure 6-25 for iOS, Figure 6-26 for macOS, Figure 6-27 for Linux, and Figure 6-28 for WPF.

Windows

A Win U I desktop screenshot depicts the name, email, and options. My file option is selected with header buttons, new files, and upload.

Figure 6-21

Windows application with header buttons

WASM

A UnoDrive webpage screenshot depicts the name, email, and options. My file option is selected with header buttons, new files, and upload.

Figure 6-22

WebAssembly application with header buttons

Android

A mobile phone screenshot depicts my files in the Android application with the header buttons, new and upload.

Figure 6-23

Android application with header buttons

Note

The assets were not loaded correctly for Android. This is expected as the Segoe MDL2 Assets are only available on Windows by default. We will need to explicitly add these for Android.

We will be fixing this in the next chapter.

iOS

A mobile phone screenshot depicts my files in the i O S application with the header buttons new and upload indicated with emojis.

Figure 6-24

iOS application with header buttons off the screen

The iOS application draws the buttons as expected, but they are drawn outside the bounds of the screen, making it difficult to use the “Upload” button. In our Grid design we defined each column to take exactly 50% of screen width as seen in the code snippet in Listing 6-31.
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
Listing 6-31

HeaderControl.xaml – grid column definitions

Looking at our screen, we can see the header text or the first column doesn’t need exactly 50% of screen width and it can use up less. Let’s update the first column to use the keyword Star that uses up the remaining space and the second column to use Auto. This will give the necessary space for both buttons:
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
When we run the application again on iOS, both buttons will be in the bounds of the screen.

A mobile screenshot depicts my files in the i O S application with the header buttons new, and upload is indicated with emojis.

Figure 6-25

iOS application with header buttons

Note

The iOS application does not have the font icons loaded correctly as the Segoe MDL2 Assets are only included in Windows by default. We will need to add the assets to the iOS project for them to show up correctly.

This will be explained in detail in the next chapter.

macOS

A screenshot depicts the John Smith email dashboard with the selection of my files on the mac O S page with the header buttons, new and upload.

Figure 6-26

macOS application with header buttons

The Segoe MDL2 Assets are not included by default for macOS. We will need to add them to the project.

This will be explained in the next chapter.

Linux

A screenshot depicts the John Smith email minimized dashboard with the selection of my files on the Linux page with header control buttons, new and upload.

Figure 6-27

Linux application with header buttons

Note

Just like iOS and macOS, the Linux application does not have the Segoe MDL2 Assets. For this platform it is considered a custom font and needs to be added to the project.

This will be explained in the next chapter.

WPF

A screenshot of Mainwindow depicts the John Smith page with email and my files selected. It has my files page with header buttons new and upload.

Figure 6-28

WPF application with header buttons

Even though the WPF project uses the same Skia rendering engine as Linux, since it is on Windows, it has the Segoe MDL2 Assets. There’s no need to explicitly add them to the WPF project.

We are going to conclude with the fonts not working on all platforms. In the next chapter, we are going to fix this by adding custom fonts.

Conclusion

In this chapter we covered the fundamental navigation strategy of a dashboard page using a left menu or flyout menu depending on the target platform. We also added navigation for the various stubbed pages we added.

In the next chapter we are going to pick up where we left off by adding custom fonts to all the projects, so the icons look identical between the platforms. If you had any trouble following the code in this chapter, you can download the code for Chapter 6 at https://github.com/SkyeHoefling/UnoDrive/tree/main/Chapter%206 .

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.5.86