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”.
In the next screen, click in the 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:
Changing the app's name
Start by changing the name to BackOffice, in the App Title field at the top.
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.
Click in the arrow in the menu created, to expand it.
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:
Collection using default columns
Define the name of the collection and click in the option “Add default columns”.
That will 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.
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:
It will create a row as in the following:
For each field insert the respective information: field's name, type and if it is multiline.
At the end we will have something like:
Note: Each field name should start with upper case.
After all collections are defined, we will have the following definition:
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:
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:
And for the detail page:
Editing default data
We will now edit the default data, by clicking in the “Data” separator and then in “Edit Data”.
Click in “Create new” and fill in the item with dummy values like the following:
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.
The following screen will be shown:
Click in the “Generate” button to generate the source code.
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.
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:
- 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!).
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.
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 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 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):
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.
With the cursor under the string “Beverages1DetailDetail” press F12 and the BeveragesViews.xaml will be opened, with focus in:
- <DataTemplate x:Key="Beverages1DetailDetail">
Change this template to something like:
- <DataTemplate x:Key="Beverages1DetailDetail">
- <StackPanel Margin="120,0,0,0">
- <TextBlock Text="Title:"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Margin="0,20,0,0" Grid.Column="1"
- Text="{Binding Title, Mode=TwoWay}" Width="400" HorizontalAlignment="Left"/>
- <TextBlock Text="Subtitle:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Grid.Column="1"
- Text="{Binding Subtitle, Mode=TwoWay}" Margin="0,20,0,0" Width="400" HorizontalAlignment="Left"/>
- <TextBlock Text="Image:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Grid.Column="1" Margin="0,20,0,0"
- Text="{Binding Image, Mode=TwoWay}" Width="400" HorizontalAlignment="Left"/>
- <Image Grid.RowSpan="2"
- Source="{Binding Image}" Margin="0,20,0,0"
- Width="200" Height="200"
- HorizontalAlignment="Left" Stretch="UniformToFill" />
- <TextBlock Text="Description:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="1" Grid.Column="1"
- Margin="0,20,0,0" ScrollViewer.HorizontalScrollBarVisibility="Auto"
- ScrollViewer.VerticalScrollBarVisibility="Auto"
- AcceptsReturn="true" HorizontalAlignment="Left"
- Height="200" MaxHeight="200" Width="800" MaxWidth="800"
- Text="{Binding Description, Mode=TwoWay}" />
- </StackPanel>
- </DataTemplate>
It will result in something like:
Do the same changes for Starters, Mains and Desserts. For Special Offers change the data template for something like:
- <DataTemplate x:Key="SpecialOffers1DetailDetail">
- <StackPanel Margin="120,0,0,0">
- <TextBlock Text="Title:"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Margin="0,20,0,0" Grid.Column="1"
- Text="{Binding Title, Mode=TwoWay}" Width="400" HorizontalAlignment="Left"/>
-
- <TextBlock Text="Subtitle:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Grid.Column="1"
- Text="{Binding Subtitle, Mode=TwoWay}" Margin="0,20,0,0" Width="400" HorizontalAlignment="Left"/>
-
-
- <TextBlock Text="Starter1:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Grid.Column="1"
- Text="{Binding Starter1, Mode=TwoWay}" Margin="0,20,0,0" Width="400" HorizontalAlignment="Left"/>
-
- <TextBlock Text="Main1:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Grid.Column="1"
- Text="{Binding Main1, Mode=TwoWay}" Margin="0,20,0,0" Width="400" HorizontalAlignment="Left"/>
- <TextBlock Text="Dessert1:" Margin="0,20,0,0"
- Style="{StaticResource ItemHeaderTextSnapped}" />
- <TextBox Grid.Row="0" Grid.Column="1"
- Text="{Binding Dessert1, Mode=TwoWay}" Margin="0,20,0,0" Width="400" HorizontalAlignment="Left"/>
- </StackPanel>
- </DataTemplate>
The result will be something like:
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:
- <Page.BottomAppBar>
- <CommandBar Background="{StaticResource AppBarBackground}">
- <AppBarButton DataContext="{Binding BeveragesModel}"
- x:Uid="SaveButton"
- Command="{Binding SaveCommand}">
- <AppBarButton.Icon>
- <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Save.png"/>
- </AppBarButton.Icon>
- </AppBarButton>
- <AppBarButton DataContext="{Binding BeveragesModel}"
- x:Uid="DeleteButton"
- Command="{Binding DeleteCommand}">
- <AppBarButton.Icon>
- <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Delete.png"/>
- </AppBarButton.Icon>
- </AppBarButton>
- </CommandBar>
- </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:
- <Page.BottomAppBar>
- <CommandBar Background="{StaticResource AppBarBackground}">
- <AppBarButton x:Uid="AddButton"
- Command="{Binding AddCommand}"
- DataContext="{Binding BeveragesModel}">
- <AppBarButton.Icon>
- <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Add.png"/>
- </AppBarButton.Icon>
- </AppBarButton>
- <AppBarButton x:Uid="RefreshButton" DataContext="{Binding BeveragesModel}" Visibility="{Binding RefreshVisibility}"
- Command="{Binding RefreshCommand}">
- <AppBarButton.Icon>
- <BitmapIcon UriSource="ms-appx:///Assets/AppBar/Refresh.png"/>
- </AppBarButton.Icon>
- </AppBarButton>
- </CommandBar>
- </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.
Scroll to the ViewModelBase<T> class and add the following commands:
- public ICommand AddCommand
- {
- get { return new DelegateCommand(AddItem); }
- }
-
- public ICommand DeleteCommand
- {
- get { return new DelegateCommand(DeleteItem); }
- }
-
- public ICommand SaveCommand
- {
- get { return new DelegateCommand(SaveItem); }
- }
After it, add the missed methods:
- public abstract bool CanSave();
-
- public abstract void AddItemAsync();
-
- private async void DeleteItemAsync ()
- {
-
- }
-
- private async void SaveItemAsync ()
- {
-
- }
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:
- private async void DeleteItemAsync()
- {
- ProgressBarVisibility = Visibility.Visible;
- var currentItem = GetCurrentItem();
-
- var messageDialog = new MessageDialog(currentItem.DefaultTitle, "Are you sure you want to delete this item?");
- messageDialog.Commands.Add(new UICommand("Yes"));
- messageDialog.Commands.Add(new UICommand("No"));
- var result = await messageDialog.ShowAsync();
- if (result.Label == "Yes")
- {
- await DataSource.DeleteAsync(currentItem);
- Items.Remove(currentItem);
- OnPropertyChanged("PreviewItems");
- OnPropertyChanged("Items");
- OnPropertyChanged("HasMoreItems");
- }
-
- ProgressBarVisibility = Visibility.Collapsed;
- }
This will require changes in the DataSourceBase<T> class as in the following:
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:
- public override async Task DeleteAsync(BeveragesSchema currentItem)
- {
- await _mobileService.Table.DeleteAsync(currentItem);
- await UpdateCacheAsync();
- }
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.
- private async void SaveItem()
- {
- if (!CanSave())
- {
- var cannotSaveMessageDialog = new MessageDialog("You must fill all data.", "Attention!");
- cannotSaveMessageDialog.Commands.Add(new UICommand("Ok"));
- await cannotSaveMessageDialog.ShowAsync();
- return;
- }
- ProgressBarVisibility = Visibility.Visible;
- var currentItem = GetCurrentItem();
- var messageDialog = new MessageDialog(currentItem.DefaultTitle, "The item was saved!");
- messageDialog.Commands.Add(new UICommand("Ok"));
- await DataSource.SaveAsync(currentItem);
- await messageDialog.ShowAsync();
- OnPropertyChanged("Items");
- OnPropertyChanged("PreviewItems");
- OnPropertyChanged("HasMoreItems");
- ProgressBarVisibility = Visibility.Collapsed;
- }
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.
- public async Task<DateTime> LoadDataAsync(ObservableCollection<T> viewItems, bool forceRefresh)
- {
- DateTime timeStamp = DateTime.Now;
-
- if (HasStaticData)
- {
- viewItems.AddRangeUnique(await LoadDataAsync());
- }
- else
- {
- var dataInCache = await AppCache.GetItemsAsync<T>(CacheKey);
- if (dataInCache != null)
- {
- timeStamp = dataInCache.TimeStamp;
-
- viewItems.AddRangeUnique(dataInCache.Items);
- }
-
- if (NetworkInterface.GetIsNetworkAvailable() && DataNeedToBeUpdated(forceRefresh, dataInCache))
- {
- var freshData = await UpdateCacheAsync();
-
- viewItems.AddRangeUnique(freshData.Items);
- timeStamp = freshData.TimeStamp;
- }
- }
- return timeStamp;
- }
And then we will have:
- nternal async Task<DataSourceContent<T>> UpdateCacheAsync()
- {
- var freshData = new DataSourceContent<T>()
- {
- TimeStamp = DateTime.Now,
- Items = await LoadDataAsync()
- };
-
- await AppCache.AddItemsAsync(CacheKey, freshData);
- return freshData;
- }
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:
- public override void AddItemAsync()
- {
- ProgressBarVisibility = Visibility.Visible;
-
- ProgressBarVisibility = Visibility.Visible;
- var newItem = new BeveragesSchema();
- newItem.IsNew = true;
- Items.Add(newItem);
- NavigationServices.NavigateToPage("BeveragesDetail", newItem);
- OnPropertyChanged("PreviewItems");
- OnPropertyChanged("HasMoreItems");
-
- ProgressBarVisibility = Visibility.Collapsed;
- ProgressBarVisibility = Visibility.Collapsed;
- }
And in BeveragesDetailPage.xaml.cs in OnNavigateTo change the code to receive the parameter sent in the AddItemAsync method as in the following:
- protected async override void OnNavigatedTo(NavigationEventArgs e)
- {
- _dataTransferManager = DataTransferManager.GetForCurrentView();
- _dataTransferManager.DataRequested += OnDataRequested;
-
- _navigationHelper.OnNavigatedTo(e);
-
- await BeveragesModel.LoadItemsAsync();
- if (e.Parameter is BeveragesSchema)
- {
- BeveragesModel.Items.Add(e.Parameter as BeveragesSchema);
- }
- BeveragesModel.SelectItem(e.Parameter);
-
- if (BeveragesModel != null)
- {
- BeveragesModel.ViewType = ViewTypes.Detail;
- }
- DataContext = this;
- }
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:
- public override bool CanSave()
- {
- return !string.IsNullOrEmpty(SelectedItem.Title) &&
- !string.IsNullOrEmpty(SelectedItem.Subtitle) &&
- !string.IsNullOrEmpty(SelectedItem.Image) &&
- !string.IsNullOrEmpty(SelectedItem.Description);
- }
The Special Offers will be something like:
- public override bool CanSave()
- {
- return !string.IsNullOrEmpty(SelectedItem.Title) &&
- !string.IsNullOrEmpty(SelectedItem.Subtitle) &&
- !string.IsNullOrEmpty(SelectedItem.Starter1) &&
- !string.IsNullOrEmpty(SelectedItem.Main1) &&
- !string.IsNullOrEmpty(SelectedItem.Dessert1);
- }
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.
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:
Running the AppNow that the BackOffice supports editing the data and has a new look, let's see how it looks.
The main viewThe main view will be the start page; in this case, it will be the MainPage.xaml.
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
The item viewThe 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:
For a 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:
- BottomAppBar.IsOpen = true;
- 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.