Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / UWP

Programming Windows 10 Desktop: UWP Focus (6 of N)

5.00/5 (7 votes)
28 Nov 2017CPOL11 min read 13.8K   318  
Get Started in UWP (moving away from WinForm) Chapter 6 Programmatically Adding Tabs (Pivot) for Multiple entries in DailyJournal (includes CommandBar and Menu concepts)

Introduction

This is my continuing attempt to tell the story of Desktop development under the UWP paradigm.

You can read the beginning of the saga in the following articles.

Programming Windows 10: UWP Focus (1 of N)[^]

Programming Windows 10: UWP Focus (2 of N)[^]

Programming Windows 10: UWP Focus (3 of N)[^]

Programming Windows 10 Desktop: UWP Focus (4 of N)[^]

Programming Windows 10 Desktop: UWP Focus (5 of N)[^]

Background

This series is growing by the hour.  I don't know what is going on but it has captured my attention greatly and I'm actually becoming quite interested in using XAML.   However, it is possible I've simply come down with a case of hypergraphia (Hypergraphia - Wikipedia[^]). Smile | :)

Menu Concept: Gone After 30 Years

We need to provide the user with a way to tell the app that she wants to create a new Journal Entry.  In the past, Microsoft helped to create the idea of a top menu which contained specific items such as File, Edit, View, Help (those top level menu items all still exist in Visual Studio.

Menus Aren’t Optimal For Touch

I kind of browsed around the Internet and determined that menus are basically gone under UWP.  You may want to read that sentence again.  It’s fairly significant since we’ve all dealt with menus for a long time.

Getting rid of menus makes sense if you’re thinking about apps which run on devices (Pads (Surface), Phones, etc.)

Menus aren’t exactly easy to use when you are using touch screens.  

It doesn’t really make sense on the Windows Desktop though. Menus should still be valid when using a pointing device like a mouse.  Oh well.  This is the new world.  

CommandBar : The New Menu

After a bit of searching around I discovered that there is a thing called a CommandBar.

I actually found it because I was looking to see if I could add a quick accelerator key combination which would allow the user to create a new entry.  It looks like we can add keyboard accelerators (Ctrl-Shift-S or whatever) to CommandBar sub-elements.

Here We Go Again

My idea is to add a CommandBar which will contain some buttons which will allow the user to activate functionality like creating a new entry.   

Start Here

That’s where we’ll start : with a CommandBar with one button which will allow the creation of a new entry.

I will add this CommandBar to the top of the Grid and have it span all the columns so it will become the main focus where the user can go to activate functionality.

Finding Sample Code

My CommandBar XAML came from a sample at: https://docs.microsoft.com/en-us/windows/uwp/design/input/keyboard-accelerators

Heavily Modified

However, beware, I have heavily modified it for our purposes.  The reason I mention that I started with a sample is to make you aware that developers do this all the time.  The important thing is to make sure you fully understand the samples that you are using.

Adding New Row To Grid

The first thing we need to do is update our XAML to add a new row to our Grid by adding a new RowDefinition.  We need to add the new row so we can place our CommandBar XAML in that row.  

Here’s how simple that is.  Just add the following line to the Grid.RowDefinition:

XML
<RowDefinition Height="Auto"/>

Image 2

 

This will define a total of three rows now.

CommandBar Is Now The First Row

Since the CommandBar will now be the first row in the Grid, we will need to update the other controls which were previously had attributes of Grid.Row=”0” to be Grid.Row=”1”.  That will insure they appear below the CommandBar.

I’ve done that work and if you look closely at the next image you’ll see that Grid.Row is updated in the appropriate places.

Image 3

XAML and Code Download

Here is the entire updated XAML as it now appears.  You can also download the altered code by getting the zip file at the top of this article named DailyJournal_v005.zip.

 

XML
<Page
   x:Class="DailyJournal.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="using:DailyJournal"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d" Loaded="Page_Loaded">

   <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
       <Grid.RowDefinitions >
           <RowDefinition Height="Auto"/>
           <RowDefinition Height="Auto" />
           <RowDefinition Height="*"/>
       </Grid.RowDefinitions>
       <Grid.ColumnDefinitions>
           <ColumnDefinition Width="Auto"/>
           <ColumnDefinition/>
       </Grid.ColumnDefinitions>
       
       <CalendarView HorizontalAlignment="Left" Grid.Row="1" Grid.Column="0" VerticalAlignment="Top"/>
       <ListView Name="EntriesListView" Grid.Row="2" Grid.Column="0"
           HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
       <Pivot x:Name="rootPivot" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" >
           <PivotItem Header="Entry1">
               <RichEditBox Name="MainRichEdit" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"
                    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
           </PivotItem>
           <PivotItem Header="Entry2">
               <RichEditBox Name="MainRichEdit2" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"
                    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
           </PivotItem>
       </Pivot>
   </Grid>
</Page>

 

Build and Run (CTRL-F5)

Go ahead and build and run the app and you’ll see that it still works perfectly.

Pleasantly Surprised By XAML & Layout

I think it is quite interesting that after those changes DailyJournal still runs perfectly fine and all items are still layed out properly.  I’m very glad that is true.  These kinds of things are why XAML is really cool.  Wait until you see how easily we can add the CommandBar and AppBarButtons.  You will be even more impressed.  I was pleasantly surprised by how well it worked and how easily I could add new XAML elements to our existing layout without destroying everything.

The Magic of Proportional Spacing

This is really all because we have been using the proportionally spaced items, which simply fill the provided area.  Since at the present there is no Row 0 -- or Row 0 takes up no layout space -- everything just squeezes it out of view and continues to look as if there are actually only two rows.

CommandBar and Associated Controls

A CommandBar is another container and it can contain one or more AppBarButton controls.  The AppBarButton is what the user actually clicks on (or touches) to activate functionality.

Right now, we only want to add one AppBarButton which allows the user to create a new journal entry.  This will allow us to introduce the smallest amount of XAML to get a CommandBar and associated AppBarButton working.  That’s how I like to work: add small pieces and get them working.

Pasting In CommandBar XAML.

At this point there is an empty line right after the ending Grid.ColumnDefinitions and before the CalendarView control.  We are going to copy the XAML for our CommandBar into that space and it is going to appear at the top of the Page.

 

You can see the location where we’re going to paste in the following image (right where the I-bar cursor is located):

Image 4

 

Here’s the new XAML for the CommandBar which you can paste into that spot.

XML
<CommandBar  Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">

           <AppBarButton

        x:Name="CreateNewEntryButton"

               Icon="NewWindow"

               Label="New"

               ToolTipService.ToolTip="New (Ctrl+N)">

           </AppBarButton>

       </CommandBar>

As soon as you add that, it’ll add the CommandBar at the top of the Page and you’ll see it in the DesignView.  I’ve clicked on the AppBarButton so it’ll get focus in the DesignView.

 

Image 5

 

Build and Run (CTRL-F5) and take check it out.

 

Image 6

 

 

It looks good and everything works as expected.  

Button Icons

I used the NewWindow icon for our New Entry AppBarButton. That icon comes from a list that the UWP SDK  (Software Development Kit) provides for you.  You can see a list of all the icons along with their names at : https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.symbol

That Ellipsis (...) item seems a little odd.  We didn’t add that, but we got it for free.  

 

Right now our button doesn’t do anything when we click it.  That’s easy to fix so let’s do it and see if we can programmatically create a new PivotItem also.

Add Functionality to Button

In DesignView, simply double-click the CreateNewEntryButton and it will generate a Click method for the button and move you to that method in MainPage.xaml.cs.

Image 7

Thinking, Trying, Intellisensing and Internet Searching

Now, we can try to figure out how to add a new PivotItem and RichEditBox to our Page.

After only a few minutes of code typing guided by years of experience and Visual Studio’s Intellisense (code help) I figured out 95% of what I needed to do.  

A Bit Stuck : StackOverflow To The Rescue

The one thing I became stuck on was how to add the RichEditBox to the PivotItem.  However, one quick search on that brought up a StackOverflow entry which guided me to the solution.

Here’s the code you can add to the CreateNewEntryButton_Click method to make it work.

C#
PivotItem pi = new PivotItem();

          var entryText = String.Format("Entry{0}",rootPivot.Items.Count+1);

          pi.Header = entryText;

          RichEditBox reb = new RichEditBox();

          reb.HorizontalAlignment = HorizontalAlignment.Stretch;

          reb.VerticalAlignment = VerticalAlignment.Stretch;

          pi.Content = reb;

          rootPivot.Items.Add(pi);

          rootPivot.SelectedIndex = rootPivot.Items.Count - 1;

I’ve added numbers to the following code screenshot so we can talk about what each line does.

 

Image 8

 

NOTE: The numbers in the following text relate to the numbers in the previous code listing snapshot.

 

  1. The first thing we need to do is programmatically create new PivotItem.  It’s absolutely fantastic that the XAML elements are named exactly the same as the objects that we deal with in the code because it makes it all so much easier.  This is a big win by the UWP / XAML Architects.  This first line simply creates a new PivotItem object.

    1. I totally guessed at that line of code and Intellisense (Visual Studio’s built-in code helper) got me there.  Very easy.

  2. I used the keyword var here which allows the compiler to decide which type the target variable will be.  It will be a string, of course, because I am calling the static String.Format() method so I can create the text which will be used for the PivotItem’s header.  String.Format() allows us to easily create a string which contains various values. Format Specifiers are made up of curly brackets and a number (beginning at 0 and incrementing each time you add a format specifier to the string.  The format specifier will be replaced by the target value which is the value which is also in the method call.  In our case our value is obtained from our rootPivot object’s count of items.  In other words, we have a reference to our rootPivot object (which is defined in the XAML) and inside that rootPivot object we find a collection named Items which contains all the objects that the rootPivot object contains (nested elements).  We ask it for the current Count of those items which is the current number of PivotItems (tabs) that it contains.  Our app starts out with 2 but this will increase as the user clicks the button.  We add one to the Count since we are adding one right now.  That Count will be an integer value of course and it will be appended to the word “Entry” so that the first time the button is clicked the string value will be “Entry3”.  That will be the header for our new PivotItem the first time through. Each time will increment the counter and the string will be formed that way.

  3. We named the new PivotItem pi and now we are ready to set its Header text to the value we generated and that is easily done with this line.

  4. For each PivotItem we create, we want to create a new RichEditBox and that’s what this line does.  

  5. These two lines are similar and simple so they just get one number.  Here we are setting the RichEditBox to Stretch itself into the available area (vertically and horizontally) that is created by the PivotItem.  This way the RichEditBox will be as large as it can possibly be on each tab.

  6. We need the RichEditBox to be added to the PivotItem so the PivotItem can contain the RichEditBox.  This is just the same thing we’ve done when we created the XAML.  However, to do that programmatically we have to set pi.Content to the new RichEditBox we just created.  Now the reb is part of the pi content and it will manage it and display it.

  7. Finally, we need to add the new PivotItem (which contains the RichEditBox) to our outer rootPivot.  We do that by simply adding our new PivotItem to the rootPivot’s collection of items named appropriately, Items.  To add the item we call the Add() method and pass in the pi (PivotItem).  It’s all really very simple.

  8. Once the new PivotItem and RichEditBox has been added we want the app to automatically select it and we can do that by simply setting the currently selected index to the last item added -- the one we just added.

 

That’s it.  Now you can run the app and click the Create New button and it will add a new Journal Entry with an auto-generated unique header every time.

Get Code Download

If you’ve been following along you can build and run it now (CTRL-F5) or download the DailyJournal_v006.zip from the top of this article and try it out.  You can add tons of PivotItems (tabs).  If you add too many to fit the app will automatically handle that by adding “arrows” which allow you to move left/right through them.

Fire it up and start adding PivotItems.  It’s cool and this thing is starting to feel like a real app.

Image 9

 

Functional Needs Arise When Other Functionality Is Added

Next Article Will Pick Up Here

Now that we can easily add entries, we instantly recognize our need for a way to:

  • Delete entries

  • Save the text related to an entry

 

We will add that functionality next time.  I hope you’ve enjoyed this chapter and learned something. 

History

2017-11-28 : First publication of the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)