Microsoft’s Windows AppStudio: Creating a BackOffice App For Menu App

Scope

The purpose of this article is to create a BackOffice App for the Menu App created by Windows AppStudio in the article:

Microsoft's Windows App Studio Beta: Connecting a Menu App to Azure Mobile Service

How to create a BackOffice App

The MyMenuApp Azure Mobile Service is defined and the Menu App consumes the data from the service, now we will create the BackOffice App to change that data.

For help in creating the BackOffice App, we will use the Empty Template from AppStudio to create the starting point of this app, that will contain:

  • A Start menu with the following choices: Starters, Mains, Desserts, Beverages and Special offers
  • Clicking in each menu item will show a page with list of the items
  • Clicking in one item in the list will show a page to edit the item

Creating the BackOffice App using Empty Template

Go to the AppStudio, do the login and then click in “Start new project”.

empty Template

In the next screen, click in the Empty App Template.

Empty App Template

Like the name indicates, this app is empty app and we will define all the structure for the BackOffice.

Here is the starting point:

structure for the BackOffice

Changing the app's name

Start by changing the name to BackOffice, in the App Title field at the top.

main section

Ceating the menu

The first page of the app will have a menu; for that, click in the Menu option and then fill in the name of the menu.

add menu section

Click in the arrow in the menu created, to expand it.

menu created

With this, we will have more options available, more specific the menu action. To create the menu we want, we will use the collection option, because we want to show a list of items.

Adding collections

Click in the collection menu to create the first menu item as in the following:

advance section

Collection using default columns

Define the name of the collection and click in the option “Add default columns”.

Add default columns

That will create the following structure:

create the following structure

Here we can define if we want dynamic or static data, that it is not important in our case, because we will use the data from the Azure Mobile Service, as we did in the Menu App.

After creating each collection, it is recommended to save the definitions, using the button “Save” in the right.

save the definitions

Do the same process for Mains, Desserts and Beverages.

Collection using specific columns

For Special Offers we need to add the columns manually, because it has a structure different from the default, for rhat click in “Create new” as in the following:

Create new

It will create a row as in the following:

create a row

For each field insert the respective information: field's name, type and if it is multiline.

At the end we will have something like:

insert the respective information

Note: Each field name should start with upper case.

After all collections are defined, we will have the following definition:

all collection

These collection can be ordered, deleted and edited but they cannot be more than six. In the code it is possible to add more or create another menu and add the missed collections. In source code change it to have only one section.

Editing a collection

It is possible to edit the Starters by clicking in “Edit Item”, that will show the following screen:

edit item

For each collection we need to do the following:

  • Define the Layout and Bindings for the windows with the list of items
  • Define the Layout and Bindings and Page Extras for the details of the item
  • Edit the data to add one dummy starter

Editing Layout and Bindings

We can choose what we want, for the sample we will define the following:

Editing Layout

And for the detail page:

page details

Editing default data

We will now edit the default data, by clicking in the “Data” separator and then in “Edit Data”.

edit data option

Click in “Create new” and fill in the item with dummy values like the following:

create new option

Here, it is not important what we will fill in, it only allows us to see data until we change the BackOffice to consume the Azure Mobile Service. Insert a similar dummy data for the others collection.

At this moment, we have a starting point for the BackOffice App, it is possible to define the theme, the tiles, the published info, but this article will not cover it. To see in more depth how to define each one, see the article Creating Windows Phone and Window 8.1 applications using Microsoft App Studio.

Generating the BackOffice app

Now we will click in the “Finish” button to generate the BackOffice App.

                                    finish

The following screen will be shown:

Generate button

Click in the “Generate” button to generate the source code.

generate new app

To generate the app for Windows 8.1 and Windows Phone 8.1 we should click in that option, that will be a Universal App, like the Menu App.

Click in the link to download the source code.

download source code

Code

See the source code for the BackOffice App, here:

Step 6: Add default BackOffice App generated by AppStudio

Changing the source code generated

Now we have a starting point for the BackOffice, we will change the code to support editing the data consumed by the Azure Mobile Service.

To add the MyMenuApp Azure Mobile Service to the BackOffice App, see the topic How to consume the Azure Mobile Services in Menu App . The changes in BackOffice are the same.

Note:

  1. The default columns added to the “ImageUrl” in each collection and each dto from the service uses the name “Image”. There are two options to fix this difference: rename the property “ImageUrl” for each collection and in the UI or using JSON attributes for the property “ImageUrl” that will define the name as “Image”.

In the Azure Mobile Service we added the static data from the Menu App, for this reason when we run the BackOffice App using the Azure Mobile Service it will not show the images, because the BackOffice App doesn´t have these images. To solve it for now, do a copy and paste of these images (it will be temporary!).

Azure Mobile Service

Running the app

Running the app we will get the following screens.

The main view

The main view is the start page when the app launches. In this case, it will be MainPage.xaml.

MainPage xaml

The collection view

The collection view will be the page that shows a list of objects. In this case, it will be BeveragesPage.xaml, DessertsPage.xaml, Main1Page.xaml, SpecialOffersPage.xaml and StartersPage.xaml.

the collection view

The item view

The item view will be the page that shows a specific item, in this case, it will be BeveragesDetailPage.xaml, DessertsDetailPage.xaml, Main1DetailPage.xaml, SpecialOffersDetailPage.xaml and StartersDetailPage.xaml.

the item view

The others collection has similar views.

For now the BackOffice app is only in read mode similar to the Menu App.

Add support for edit data

Here are the main changes we need to do:

  • Change the item view to edit the data
  • Add an AppBarButton to “Add” a new item in the collection view
  • Add two AppBarButton to “Save” and “Delete” the current item in the item view

Note: We will only focus on Windows 8.1 App, for the Windows Phone 8.1 the changes are similar and in some cases the changes are shared.

Changing the UI (XAML)

The following describes changing the data templates.

To add the support to edit the data, we need to change the item view. We should change the following views (that can be found in the DataTemplate folder):

data templates

Each one contains the DataTemplates used in each page.

To find which template should be changed, open the BeveragesDetailPage.xaml, the page for seeing a selected Beverage. We will find the FlipView that contains the a ItemTemplate defined, called Beverages1DetailDetail.

file view

With the cursor under the string “Beverages1DetailDetail” press F12 and the BeveragesViews.xaml will be opened, with focus in:

  1. <DataTemplate x:Key="Beverages1DetailDetail">  
Change this template to something like:
  1. <DataTemplate x:Key="Beverages1DetailDetail">  
  2.         <StackPanel Margin="120,0,0,0">  
  3.             <TextBlock Text="Title:"   
  4.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  5.             <TextBox Grid.Row="0" Margin="0,20,0,0"  Grid.Column="1"   
  6.                      Text="{Binding Title, Mode=TwoWay}" Width="400" HorizontalAlignment="Left"/>  
  7.             <TextBlock Text="Subtitle:" Margin="0,20,0,0"   
  8.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  9.             <TextBox Grid.Row="0" Grid.Column="1"   
  10.                      Text="{Binding Subtitle, Mode=TwoWay}" Margin="0,20,0,0"  Width="400" HorizontalAlignment="Left"/>  
  11.             <TextBlock Text="Image:" Margin="0,20,0,0"   
  12.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  13.             <TextBox Grid.Row="0" Grid.Column="1" Margin="0,20,0,0"    
  14.                      Text="{Binding Image, Mode=TwoWay}" Width="400" HorizontalAlignment="Left"/>  
  15.             <Image Grid.RowSpan="2"   
  16.                    Source="{Binding Image}" Margin="0,20,0,0"   
  17.                    Width="200" Height="200"  
  18.                    HorizontalAlignment="Left" Stretch="UniformToFill" />  
  19.             <TextBlock Text="Description:" Margin="0,20,0,0"   
  20.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  21.             <TextBox Grid.Row="1" Grid.Column="1"   
  22.                      Margin="0,20,0,0" ScrollViewer.HorizontalScrollBarVisibility="Auto"  
  23.                      ScrollViewer.VerticalScrollBarVisibility="Auto"  
  24.                      AcceptsReturn="true" HorizontalAlignment="Left"  
  25.                      Height="200" MaxHeight="200" Width="800" MaxWidth="800"  
  26.                      Text="{Binding Description, Mode=TwoWay}" />  
  27.         </StackPanel>  
  28.     </DataTemplate>  
It will result in something like:

output of code

Do the same changes for Starters, Mains and Desserts. For Special Offers change the data template for something like:
  1. <DataTemplate x:Key="SpecialOffers1DetailDetail">  
  2.         <StackPanel Margin="120,0,0,0">  
  3.             <TextBlock Text="Title:"   
  4.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  5.             <TextBox Grid.Row="0" Margin="0,20,0,0"  Grid.Column="1"   
  6.                      Text="{Binding Title, Mode=TwoWay}" Width="400" HorizontalAlignment="Left"/>  
  7.              
  8.             <TextBlock Text="Subtitle:" Margin="0,20,0,0"   
  9.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  10.             <TextBox Grid.Row="0" Grid.Column="1"   
  11.                      Text="{Binding Subtitle, Mode=TwoWay}" Margin="0,20,0,0"  Width="400" HorizontalAlignment="Left"/>  
  12.    
  13.    
  14.             <TextBlock Text="Starter1:" Margin="0,20,0,0"   
  15.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  16.             <TextBox Grid.Row="0" Grid.Column="1"   
  17.                      Text="{Binding Starter1, Mode=TwoWay}" Margin="0,20,0,0"  Width="400" HorizontalAlignment="Left"/>  
  18.    
  19.             <TextBlock Text="Main1:" Margin="0,20,0,0"   
  20.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  21.             <TextBox Grid.Row="0" Grid.Column="1"   
  22.                      Text="{Binding Main1, Mode=TwoWay}" Margin="0,20,0,0"  Width="400" HorizontalAlignment="Left"/>  
  23.              <TextBlock Text="Dessert1:" Margin="0,20,0,0"   
  24.                      Style="{StaticResource ItemHeaderTextSnapped}" />  
  25.             <TextBox Grid.Row="0" Grid.Column="1"   
  26.                      Text="{Binding Dessert1, Mode=TwoWay}" Margin="0,20,0,0"  Width="400" HorizontalAlignment="Left"/>  
  27.         </StackPanel>  
  28.     </DataTemplate>  
The result will be something like:

output result

At this moment, all the data can be edited. Now we need to add options to persist it.

Adding the AppBarButtons

To allow doing the operations “Add”, “Save” and “Delete” it is necessary to add an AppBarButton for each operation. In the BeveragesDetailPage.xaml add the following app bar:
  1. <Page.BottomAppBar>  
  2.     <CommandBar Background="{StaticResource AppBarBackground}">  
  3.       <AppBarButton DataContext="{Binding BeveragesModel}"   
  4.                     x:Uid="SaveButton"   
  5.                     Command="{Binding SaveCommand}">  
  6.         <AppBarButton.Icon>  
  7.           <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Save.png"/>  
  8.         </AppBarButton.Icon>  
  9.       </AppBarButton>  
  10.             <AppBarButton DataContext="{Binding BeveragesModel}"   
  11.                     x:Uid="DeleteButton"   
  12.                     Command="{Binding DeleteCommand}">  
  13.                 <AppBarButton.Icon>  
  14.                     <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Delete.png"/>  
  15.                 </AppBarButton.Icon>  
  16.             </AppBarButton>  
  17.         </CommandBar>  
  18.   </Page.BottomAppBar>  
That will add the “Save” and “Delete” AppBarButton. This XAML can be added to the others object's pages.

To add the “Add” AppBarButton, open the BeveragesPage.xaml and added the following XAML:
  1. <Page.BottomAppBar>  
  2.     <CommandBar Background="{StaticResource AppBarBackground}">  
  3.             <AppBarButton x:Uid="AddButton"   
  4.                          Command="{Binding AddCommand}"  
  5.                         DataContext="{Binding BeveragesModel}">  
  6.                 <AppBarButton.Icon>  
  7.                     <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Add.png"/>  
  8.                 </AppBarButton.Icon>  
  9.             </AppBarButton>  
  10.             <AppBarButton x:Uid="RefreshButton" DataContext="{Binding BeveragesModel}" Visibility="{Binding RefreshVisibility}"  
  11.                   Command="{Binding RefreshCommand}">  
  12.         <AppBarButton.Icon>  
  13.           <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Refresh.png"/>  
  14.         </AppBarButton.Icon>  
  15.       </AppBarButton>  
  16.     </CommandBar>  
  17.   </Page.BottomAppBar>  
Do the same for the others object's pages.

For now it is missing the commands for each AppBarButton: AddCommand, SaveCommand and DeleteCommand.

Notes:

Changing the code behind (C#)

Go to the ViewModelBase.cs file in the Shared project.

ViewModelBase cs file

Scroll to the ViewModelBase<T> class and add the following commands:

  1. public ICommand AddCommand  
  2. {  
  3.     get { return new DelegateCommand(AddItem); }  
  4. }  
  5.    
  6. public ICommand DeleteCommand  
  7. {  
  8.     get { return new DelegateCommand(DeleteItem); }  
  9. }  
  10.    
  11. public ICommand SaveCommand  
  12. {  
  13.     get { return new DelegateCommand(SaveItem); }  
  14. }  
After it, add the missed methods:
  1. public abstract bool CanSave();  
  2.   
  3. public abstract void AddItemAsync();  
  4.   
  5. private async void DeleteItemAsync ()  
  6. {  
  7.       // todo  
  8. }   
  9.   
  10. private async void SaveItemAsync ()  
  11. {  
  12.       // todo  
  13. }  
The methods CanSave and AddItemAsync will be implemented in the view model for each object, more specifically the view models BeveragesViewModel, DessertsViewModel, Main1ViewModel, SpecialOffersViewModel and StartersViewModel. Because it needs to know each object will be added.

The DeleteItemAsync and SaveItemAsync can be implemented in the ViewModelBase<T> class because they can be generic.

The DeleteItemAsync method

For the DeleteItemAsync method, we need to show a message to the user to confirm the operation, then the item will be deleted and then the item will be removed from the Items list and will raise notifications to update the UI.

The implementation will be something like:
  1. private async void DeleteItemAsync()  
  2. {  
  3.      ProgressBarVisibility = Visibility.Visible;  
  4.      var currentItem = GetCurrentItem();  
  5.    
  6.      var messageDialog = new MessageDialog(currentItem.DefaultTitle, "Are you sure you want to delete this item?");  
  7.      messageDialog.Commands.Add(new UICommand("Yes"));  
  8.      messageDialog.Commands.Add(new UICommand("No"));  
  9.      var result = await messageDialog.ShowAsync();  
  10.      if (result.Label == "Yes")  
  11.      {  
  12.            await DataSource.DeleteAsync(currentItem);  
  13.            Items.Remove(currentItem);  
  14.            OnPropertyChanged("PreviewItems");  
  15.            OnPropertyChanged("Items");  
  16.            OnPropertyChanged("HasMoreItems");  
  17.      }  
  18.           
  19.            ProgressBarVisibility = Visibility.Collapsed;  
  20.  }  
This will require changes in the DataSourceBase<T> class as in the following:

dataSourceBase

Where we will add the abstract methods for DeleteAsync and SaveAsync. These methods will be implemented in all specific data sources, for example in BeveragesDataSource we will do something like:
  1. public override async Task DeleteAsync(BeveragesSchema currentItem)  
  2. {  
  3.     await _mobileService.Table.DeleteAsync(currentItem);  
  4.     await UpdateCacheAsync();  
  5. }  
This code applies to the other data sources: DessertsDataSource, MainDataSource, SpecialOffersDataSource and StartersDataSource.

The SaveItemAsync method

For the SaveItemAsync method, we will update the data and then will show a message to the user to confirm the operation was done and then the UI will be updated.
  1. private async void SaveItem()  
  2.  {  
  3.        if (!CanSave())  
  4.        {  
  5.             var cannotSaveMessageDialog = new MessageDialog("You must fill all data.""Attention!");  
  6.             cannotSaveMessageDialog.Commands.Add(new UICommand("Ok"));  
  7.             await cannotSaveMessageDialog.ShowAsync();  
  8.             return;  
  9.        }  
  10.             ProgressBarVisibility = Visibility.Visible;  
  11.             var currentItem = GetCurrentItem();  
  12.             var messageDialog = new MessageDialog(currentItem.DefaultTitle, "The item was saved!");  
  13.             messageDialog.Commands.Add(new UICommand("Ok"));  
  14.             await DataSource.SaveAsync(currentItem);  
  15.             await messageDialog.ShowAsync();  
  16.             OnPropertyChanged("Items");  
  17.             OnPropertyChanged("PreviewItems");  
  18.             OnPropertyChanged("HasMoreItems");  
  19.             ProgressBarVisibility = Visibility.Collapsed;  
  20. }   
This code applies to the others data sources: DessertsDataSource, MainDataSource, SpecialOffersDataSource and StartersDataSource.

Note: When an item is saved, the item can be new or can be an existing item, for this reason a IsNew property was created for each object that allows understanding if we will do an Insert or an Update.

The UpdateCacheAsync method

The UpdateCacheAsync is a method used to update the cache for each collection, this code was provided by AppStudio, when we generated the app, only was applied a refactoring in the LoadDataAsync method for reuse of the code.
  1. public async Task<DateTime> LoadDataAsync(ObservableCollection<T> viewItems, bool forceRefresh)  
  2. {  
  3.       DateTime timeStamp = DateTime.Now;  
  4.   
  5.       if (HasStaticData)  
  6.       {  
  7.             viewItems.AddRangeUnique(await LoadDataAsync());  
  8.       }  
  9.       else  
  10.       {  
  11.             var dataInCache = await AppCache.GetItemsAsync<T>(CacheKey);  
  12.             if (dataInCache != null)  
  13.             {  
  14.                   timeStamp = dataInCache.TimeStamp;  
  15.   
  16.                   viewItems.AddRangeUnique(dataInCache.Items);  
  17.             }  
  18.      
  19.             if (NetworkInterface.GetIsNetworkAvailable() && DataNeedToBeUpdated(forceRefresh, dataInCache))  
  20.             {  
  21.                   var freshData = await UpdateCacheAsync();  
  22.      
  23.                   viewItems.AddRangeUnique(freshData.Items);  
  24.                   timeStamp = freshData.TimeStamp;  
  25.             }  
  26.       }  
  27.       return timeStamp;  
  28. }  
And then we will have: 
  1. nternal async Task<DataSourceContent<T>> UpdateCacheAsync()  
  2. {  
  3.     var freshData = new DataSourceContent<T>()  
  4.     {  
  5.         TimeStamp = DateTime.Now,  
  6.         Items = await LoadDataAsync()  
  7.     };  
  8.    
  9.     await AppCache.AddItemsAsync(CacheKey, freshData);  
  10.     return freshData;  
  11. }  
Note: Each data source must not be static, for it use the property HasStaticData.

The AddItemAsync method

For the AddItemAsync method, the implementation must be done in BeveragesViewModel, DessertsViewModel, Main1ViewModel, SpecialOffersViewModel and StartersViewModel.

For example, in BeveragesViewModel we will create a BeveragesSchema that is new and it will be added to the Items list, then the app will navigate to the item view to allow to edit this new object.

The implementation will be something like:
  1. public override void AddItemAsync()  
  2. {  
  3.     ProgressBarVisibility = Visibility.Visible;  
  4.    
  5.     ProgressBarVisibility = Visibility.Visible;  
  6.     var newItem = new BeveragesSchema();  
  7.     newItem.IsNew = true;  
  8.     Items.Add(newItem);  
  9.     NavigationServices.NavigateToPage("BeveragesDetail", newItem);  
  10.     OnPropertyChanged("PreviewItems");  
  11.     OnPropertyChanged("HasMoreItems");  
  12.    
  13.     ProgressBarVisibility = Visibility.Collapsed;  
  14.     ProgressBarVisibility = Visibility.Collapsed;  
  15. }  
And in BeveragesDetailPage.xaml.cs in OnNavigateTo change the code to receive the parameter sent in the AddItemAsync method as in the following:
  1. protected async override void OnNavigatedTo(NavigationEventArgs e)  
  2. {  
  3.     _dataTransferManager = DataTransferManager.GetForCurrentView();  
  4.     _dataTransferManager.DataRequested += OnDataRequested;  
  5.   
  6.     _navigationHelper.OnNavigatedTo(e);  
  7.    
  8.      await BeveragesModel.LoadItemsAsync();  
  9.     if (e.Parameter is BeveragesSchema)  
  10.         {  
  11.             BeveragesModel.Items.Add(e.Parameter as BeveragesSchema);  
  12.         }  
  13.         BeveragesModel.SelectItem(e.Parameter);  
  14.    
  15.         if (BeveragesModel != null)  
  16.         {  
  17.             BeveragesModel.ViewType = ViewTypes.Detail;  
  18.         }  
  19.         DataContext = this;  
  20.     }  
For the other objects the process is similar.

The CanSave method

The CanSave method will determine if an item can be saved or not, since it uses some validations.

The implementation, for Beverages, Starters and Mains, will be something like:
  1. public override bool CanSave()  
  2. {  
  3.       return !string.IsNullOrEmpty(SelectedItem.Title) &&  
  4.       !string.IsNullOrEmpty(SelectedItem.Subtitle) &&  
  5.       !string.IsNullOrEmpty(SelectedItem.Image) &&  
  6.       !string.IsNullOrEmpty(SelectedItem.Description);  
  7. }  
The Special Offers will be something like:
  1. public override bool CanSave()  
  2. {  
  3.       return !string.IsNullOrEmpty(SelectedItem.Title) &&  
  4.       !string.IsNullOrEmpty(SelectedItem.Subtitle) &&  
  5.       !string.IsNullOrEmpty(SelectedItem.Starter1) &&  
  6.       !string.IsNullOrEmpty(SelectedItem.Main1) &&  
  7.       !string.IsNullOrEmpty(SelectedItem.Dessert1);  
  8. }  
At this moment, the BackOffice App has support for editing the data received from the Azure Mobile Service and can persist it.

Changing the theme and assets

In the AppStudio we could change the theme and the assets for the BackOffice app, but is possible do the same in the code. For the BackOffice App we will add the same theme and assets from the Menu App. For it, go to the Assets folder for Windows 8.1 app and copy and paste the images related with logos.

Changing the theme and assets

To change the theme, go to the Style folder in the Windows 8.1 app, open the file AppStyles.xaml and replace the following styles:

Apps style

Running the App

Now that the BackOffice supports editing the data and has a new look, let's see how it looks.

The main view

The main view will be the start page; in this case, it will be the MainPage.xaml.

The main view

The collection view

The collection view will be the pages that show a list of objects; in this case, it will be:

BeveragesPage.xaml, DessertsPage.xaml, Main1Page.xaml, SpecialOffersPage.xaml and StartersPage.xaml

collection view

The item view

The item view the pages that show a specific item; in this case, it will be:

BeveragesDetailPage.xaml, DessertsDetailPage.xaml, Main1DetailPage.xaml, SpecialOffersDetailPage.xaml and StartersDetailPage.xaml

For an existing item:

item view

For a new item:

for new item

To have the app bar opened each time for each navigation to the collection view or the item view we should add the following definition to the OnNavigationTo for each page:
  1. BottomAppBar.IsOpen = true;  
  2. BottomAppBar.IsSticky = true;  
Code

See the source code for the BackOffice App, here:

Step 7: Changes the BackOffice app: add support for edit data from Azure Mobile Service and add new theme and assets.