Programowanie aplikacji internetowych 2015/2016 Instrukcja laboratoryjna cz.4 Aplikacje na Windows 8.x Store (C# i XAML) Prowadzący: Tomasz Goluch Wersja: 1.1
I. Wprowadzenie 1 Cel: Przekazanie podstawowych informacje o laboratorium. Laboratorium odbywa się na maszynach fizycznych które posiadają zainstalowany system Windows 8.x i IDE Visual Studio 2012 (tylko aplikacje Windows 8.0) lub wyższe (również aplikacje Windows 8.1). II. Tworzenie aplikacji Windows Store (C# i XAML) Cel: Utworzenie aplikacji Windows Store, zamiana wyświetlania domyślnych przykładowych z szablonu rzeczywistymi oraz dostosowanie wyglądu interfejsu użytkownika. Podczas uruchamiania programu lub próby użycia debuggera może pojawić się monit o odnowienie licencji developera systemu Windows 8.1. Zgoda wymaga uprawnień administratora (login i hasło podane przez prowadzącego) oraz aktywnego konta email w domenie Windows (hotmail.com albo outlook.com). Nazwa i hasło do konta również zostaną podane przez prowadzącego. Do wyrejestrowania licencji deweloperskiej służy w PowerShell u polecenie: Unregister- WindowsDeveloperLicense. Proszę uruchomić PowerShell a w trybie administratora. 1 Instrukcja przygotowana na podstawie laboratorium Hands-On lab: Building your first App for Windows 8.1 and publishing it to the Windows Store. 1
1. Utwórz nowy: Store Apps Windows Apps BlankApp(Visual c#) projekt. 2. Do folderu Assets dodaj pliki zasobów. Należy je ściągnąć ze strony przedmiotu (pliki: Logo.scale-100.png, SmallLogo.scale-100.png, SplashScreen.scale- 100.png, StoreLogo.scale-100.png i YouTube_Channel_Logo.png). 3. W pliku MainPage.xaml wewnątrz elementu Grid umieść kod reprezentujący główne okno naszej aplikacji składające się kontrolki Hub zawierającej nagłówek i trzy sekcje. Pierwsza sekcja zawiera grafikę a kolejne kontrolki ProgressRing i ListView, które będą reprezentować kolejno: posortowane alfabetycznie i najwcześniej opublikowane filmy na naszym kanale. Wklej poniższy kod zaznaczony na szaro. <Grid Background="ThemeResource ApplicationPageBackgroundThemeBrush"> <Hub> <Hub.Header> <Grid> <TextBlock x:name="texttitle" Text="Title" Style="StaticResource HeaderTextBlockStyle" Grid.Column="1" VerticalAlignment="Top" IsHitTestVisible="false" TextWrapping="NoWrap" /> </Grid> </Hub.Header> <HubSection Width="600" Margin="0,0,80,0"> <HubSection.Background> <ImageBrush ImageSource="Assets/YouTube_Channel_Logo.png" Stretch="UniformToFill" /> </HubSection.Background> </HubSection> <HubSection Width="600" Header="Aktualne"> <DataTemplate> <Grid x:name="gridrecent"> <ProgressRing x:name="progressringrecent" IsActive="False" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Width="100" Height="100" Foreground="Red" /> <ListView x:name="listviewrecent" SelectionMode="None" IsItemClickEnabled="True" Margin="-10,0"/> </Grid> </DataTemplate> </HubSection> <HubSection Width="600" Header="Najnowsze"> <DataTemplate> <Grid x:name="gridrecent"> <ProgressRing x:name="progressringpopular" IsActive="False" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Width="100" Height="100" Foreground="Red" /> 2
</Hub> </Grid> <ListView x:name="listviewpopular" SelectionMode="None" IsItemClickEnabled="True" Margin="-10,0"/> </Grid> </DataTemplate> </HubSection> 4. Dodaj do projektu folder Classes i dodaj do niego plik BindableBase.cs (do ściągnięcia ze strony przedmiotu). 5. Dodaj do projektu folder DataModel i dodaj do niego nowy plik klasy YouTubeVideo.cs. 6. Dodaj poniższy kod klasy do nowego pliku. Pamiętaj o dodaniu dostępu do wymaganej przestrzeni nazw System.Xml.Linq i folderu Classes. public class YouTubeVideo : BindableBase private const string YoutubeWatchBaseUrl = "http://www.youtube.com/watch?v="; private string _title; /// <summary> /// Gets/Sets the title of the YouTube video. /// </summary> public string Title get return _title; set SetProperty(ref _title, value); private string _videourl; /// <summary> /// Gets/Sets the video URL of the YouTube video. /// </summary> public string VideoUrl get return _videourl; set SetProperty(ref _videourl, value); private string _videoimageurl; /// <summary> /// Gets/Sets the video image URL of the YouTube video. /// </summary> public string VideoImageUrl get return _videoimageurl; set SetProperty(ref _videoimageurl, value); private string _videoid; /// <summary> /// Gets/Sets the identifier of the YouTube video. /// </summary> public string VideoId 3
get if (!string.isnullorempty(videourl)) var parsed = VideoUrl.Split('/'); _videoid = parsed[parsed.length - 1]; return _videoid; set SetProperty(ref _videoid, value); public string ExternalUrl get return YoutubeWatchBaseUrl + VideoId; private string _summary; /// <summary> /// Gets/Sets the summary of the YouTube video. /// </summary> public string Summary get return _summary; set SetProperty(ref _summary, value); public YouTubeVideo() public YouTubeVideo(Google.Apis.YouTube.v3.Data.SearchResult ytvideo) Title = ytvideo.snippet.title; VideoId = ytvideo.id.videoid; VideoImageUrl = ytvideo.snippet.thumbnails.default.url; Summary = ytvideo.snippet.description; 7. Dodaj do folderu DataModel nowy plik klasy YouTubeChannel.cs. 8. Dodaj poniższy kod klasy do nowego pliku. Pamiętaj o dodaniu dostępu do wymaganej przestrzeni nazw System.Collections.ObjectModel. public class YouTubeChannel private string _q; private string _orderby; private const int maxresults = 50; public YouTubeChannel(string query, string orderby) _q = query; _orderby = orderby; public async Task<ObservableCollection<YouTubeVideo>> LoadData(int page = 0) 4
var youtubeservice = new YouTubeService(new BaseClientService.Initializer() ApiKey = "AIzaSyCZCr7d3QzRnHzzcWf5dAQ6GR7h1bH5zNg", ApplicationName = this.gettype().tostring() ); var searchlistrequest = youtubeservice.search.list("snippet"); searchlistrequest.q = _q; // Replace with your search term. searchlistrequest.maxresults = 50; // Call the search.list method to retrieve results matching the specified query term. var searchlistresponse = await searchlistrequest.executeasync(); var items = new List<YouTubeVideo>(); foreach (var searchresult in searchlistresponse.items) if (searchresult.id.kind == "youtube#video") items.add(new YouTubeVideo(searchResult)); switch (_orderby) case "title": items.sort(delegate(youtubevideo x, YouTubeVideo y) if (x.title == null && y.title == null) return 0; else if (x.title == null) return -1; else if (y.title == null) return 1; else return x.title.compareto(y.title); ); break; case "published": throw new NotImplementedException("zaimplementuj sortowanie malejąco po dacie opublikowania"); break; default: break; return items!= null? new ObservableCollection<YouTubeVideo>(items) : new ObservableCollection<YouTubeVideo>(); 9. Aby skorzystać z obiektu reprezentującego serwis YouTube należy dodać referencje do następujących bibliotek: Google.Apis, Google.Apis.Auth, Google.Apis.Auth.PlatformServices, Google.Apis.Core, Google.Apis.PlatformServices i Google.Apis.YouTube.v3. Najlepiej w tym celu wykorzystać managera pakietów: NuGet. 5
10. Następnie należy dodać wymagane przestrzenie nazw: Google.Apis.Auth.OAuth2, Google.Apis.Services, Google.Apis.Upload, Google.Apis.Util.Store, Google.Apis.YouTube.v3, Google.Apis.YouTube.v3.Data 11. Dodaj do folderu DataModel nowy plik klasy Settings.cs. 12. Dodaj poniższy kod klasy do nowego pliku i uzupełnij: słowo kluczowe np.: board game, nazwę kanału (ChannelName) oraz jego opis (Description). static class Settings public const string Query = "..."; public const string ChannelName = "... Channel"; public const string Description = "... Channel on YouTube."; 13. Dodaj do folderu DataModel nowy plik klasy YouTubeDataSource.cs. 14. Dodaj poniższy kod klasy do nowego pliku. Pamiętaj o dodaniu dostępu do wymaganej przestrzeni nazw System.Collections.ObjectModel i folderu Classes. public class YouTubeDataSource : BindableBase public ObservableCollection<YouTubeVideo> RecentItems get; set; public ObservableCollection<YouTubeVideo> SortedByTitleItems get; set; private string _channelname; public string ChannelName get return _channelname; set SetProperty(ref _channelname, value); private bool _isloadingdata; public bool IsLoadingData get return _isloadingdata; set SetProperty(ref _isloadingdata, value); private bool _isloadingerror; public bool IsLoadingError get return _isloadingerror; set SetProperty(ref _isloadingerror, value); private YouTubeChannel _popularchannel; 6
private YouTubeChannel _recentchannel; public YouTubeDataSource() IsLoadingError = false; RecentItems = new ObservableCollection<YouTubeVideo>(); SortedByTitleItems = new ObservableCollection<YouTubeVideo>(); _popularchannel = new YouTubeChannel(Settings.Query, "title"); _recentchannel = new YouTubeChannel(Settings.Query, "published"); public async Task LoadData() ChannelName = Settings.ChannelName; IsLoadingData = true; Task taskrecent = LoadRecent(); Task taskpopular = LoadPopular(); await taskrecent; await taskpopular; IsLoadingData = false; private async Task LoadRecent() for (int page = 0; page < 10; page++) var loadedrecentitems = await _recentchannel.loaddata(page); foreach (var item in loadedrecentitems) RecentItems.Add(item); private async Task LoadPopular() for (int page = 0; page < 10; page++) var loadedsortedbytitleitems = await _popularchannel.loaddata(page); foreach (var item in loadedsortedbytitleitems) SortedByTitleItems.Add(item); 15. Dodaj do pliku MainPage.xaml.cs poniższy kod. Pamiętaj o dodaniu dostępu do wymaganej przestrzeni nazw Windows.UI.ApplicationSettings i folderu DataModel. public sealed partial class MainPage : Page private static YouTubeDataSource _datasource; public MainPage() 7
this.initializecomponent(); if (_datasource == null) _datasource = new YouTubeDataSource(); DataContext = _datasource; protected override async void OnNavigatedTo(NavigationEventArgs e) Application.Current.Resuming += Current_Resuming; await LoadData(); private async void Current_Resuming(object sender, object e) await LoadData(); private bool _alreadyloading = false; private async Task LoadData() if (_alreadyloading) return; == 0) _alreadyloading = true; if (_datasource.sortedbytitleitems.count == 0 _datasource.recentitems.count try await _datasource.loaddata(); catch _datasource.isloadingdata = false; _datasource.isloadingerror = true; _datasource.sortedbytitleitems.clear(); _datasource.recentitems.clear(); _alreadyloading = false; 16. W pliku MainPage.xaml dodaj wiązanie (binding) atrybutu Text z właściwością (property) ChannelName zadeklarowaną w klasie YouTubeDataSource. Text="Binding ChannelName" 17. Podobnie ustaw wiązania atrybutu IsActive kontrolek ProgressRing z właściwością IsLoadingData również zadeklarowaną w klasie YouTubeDataSource. IsActive="Binding IsLoadingData 8
18. Do kontrolek ListView dodaj atrybuty ItemsSource i powiąż je z właściwościami RecentItems i SortedByTitleItems również zadeklarowanymi w klasie YouTubeDataSource. ItemsSource="Binding RecentItems" 19. Po kompilacji powinniśmy zobaczyć działające kontrolki ProgressRing (podczas ładowania danych) oraz wypełnione kontrolki ListViews, niestety będą one wyświetlały jedynie domyślną reprezentację w postaci stringu. 20. Dodaj sekcję Page.Resources w pliku MainPage.xaml. <Page x:class="bgytchannelapp.mainpage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:bgytchannelapp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:ignorable="d"> <Page.Resources> <DataTemplate x:key="videoitemtemplate"> <Grid Margin="10" Height="120"> <Grid.ColumnDefinitions> <ColumnDefinition Width="160"/> <ColumnDefinition Width="320"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> 9
<Rectangle Grid.RowSpan="2" Grid.ColumnSpan="2" /> <Image Source="Binding VideoImageUrl" Stretch="UniformToFill" Margin="0" Grid.RowSpan="2" Height="120"/> <TextBlock Text="Binding Title, Margin="20,0,-10,0" Grid.Column="1" Grid.Row="0" FontSize="18" TextWrapping="WrapWholeWords" /> <TextBlock Text="Binding Summary, Margin="20,10,0,0" Grid.Column="1" Grid.Row="1" FontSize="14" TextWrapping="WrapWholeWords" /> </Grid> </DataTemplate> </Page.Resources> <Grid Background="ThemeResource ApplicationPageBackgroundThemeBrush"> Dodaj w kontrolkach ListView poniższy atrybut. ItemTemplate="StaticResource VideoItemTemplate" III. Informacja o braku połączenia z Internetem. Cel: Zapoznanie z konwerterem wartości dla XAML. 1. W przypadku problemu z połączeniem internetowym należy poinformować o tym użytkownika. W tym celu dodaj na końcu pliku MainPage.xaml, za kontrolką Hub poniższy kod. </Hub> <Grid x:name="gridproblem" Visibility="Binding IsLoadingError, Converter=StaticResource Converter_Visibility"> <StackPanel Orientation="Vertical" Background="Orange" HorizontalAlignment="Stretch" VerticalAlignment="Center" > <TextBlock TextWrapping="Wrap" Foreground="Black" Text="Connection failed" FontSize="40" TextAlignment="Center" Padding="0,10,0,0"/> <TextBlock TextWrapping="Wrap" Foreground="Black" Text="Check your Internet connection" FontSize="20" TextAlignment="Center"/> <TextBlock TextWrapping="Wrap" Foreground="Black" Text="or try again later" FontSize="20" TextAlignment="Center" Padding="0,0,0,10"/> </StackPanel> </Grid> </Grid> 2. Ponieważ właściwość IsLoadingError przyjmuje wartości ze zbioru true, false należy je odpowiednio przekonwertować na napisy zrozumiałe dla atrybutu Visibility czyli Visible albo Collapsed. W tym celu dodaj do folderu Classes nowy plik klasy VisibilityConverter i dodaj do niego poniższy kod. Pamiętaj o dodaniu dostępu do wymaganej przestrzeni nazw Windows.UI.Xaml.Data. public sealed class VisibilityConverter : IValueConverter public object Convert(object value, Type targettype, object parameter, string language) if ((bool)value) return "Visible"; else return "Collapsed"; 10
public object ConvertBack(object value, Type targettype, object parameter, string language) throw new NotImplementedException(); 3. Dodaj konwerter Converter_Visibility do pliku MainPage.xaml. Pamiętaj o dodaniu przestrzeni nazw: xmlns:local="using:<nazwa_projektu>". <converters:visibilityconverter x:key="converter_visibility"/> 4. Rozłącz połączenie sieciowe i sprawdź czy aplikacja odpowiednio zareagowała. IV. Dodawanie przycisku odświeżającego zawartości list. Cel: Zapoznanie z paskiem poleceń oraz dodawaniem do niego przycisku wraz z jego obsługą. 1. Aby umożliwić użytkownikowi ręczne odświeżanie wyświetlanych list należy dodać odpowiedni przycisk. W tym celu wystarczy do pliku MainPage.xaml dodać poniższy kod. </Grid> <Page.BottomAppBar> <CommandBar> <CommandBar.PrimaryCommands> <!-- These commands appear on the right --> <AppBarButton Icon="Refresh" Label="Refresh" Tapped="AppBarButtonRefresh_Tapped"/> </CommandBar.PrimaryCommands> </CommandBar> </Page.BottomAppBar> 11
</Page> 2. W celu utworzenia pustej funkcji obsługi zdarzenia Tapped kliknij na przypisanej jej nazwie AppBarButtonRefresh_Tapped i naciśnij przycisk F12. 3. Dodaj do nowo powstałej funkcji obsługi zdarzenia następujący kod. _datasource.isloadingdata = true; _datasource.isloadingerror = false; _datasource.sortedbytitleitems.clear(); _datasource.recentitems.clear(); LoadData(); 4. Funkcja LoadData() jest funkcją nieblokującą należy zatem zaczekać na jej wykonanie wykorzystując słowo kluczowe await. Jego użycie wymaga poinformowania kompilatora że nowo powstała funkcja obsługi zdarzenia Tapped będzie zawierać asynchroniczne elementy kodu. Robimy to oznaczając ją słowem kluczowym async. 5. Przycisk odświeżania będzie widoczny na pasku poleceń po kliknięciu gdziekolwiek na ekranie prawym przyciskiem myszy. V. Dodanie strony odtwarzacza. Cel: Zapoznanie z procesem dodawania nowej strony do aplikacji nowej strony XAML oraz obsługą odtwarzacza Windows Player. 1. Do kontrolek ListView dodaj atrybuty ItemClick i przy pomocy przycisku F12 utwórz uchwyty do obsługi klinięcia. 12
ItemClick="ListView_ItemClick" 2. Dodaj do nowej metody następujący kod. var _video = (YouTubeVideo)e.ClickedItem; this.frame.navigate(typeof(playbackpage), _video); 3. Dodaj do projektu nowy element Blank Page o nazwie PlaybackPage. 4. Dodaj layout do pliku XAML nowo dodanej strony. <Grid Background="ThemeResource ApplicationPageBackgroundThemeBrush"> <Grid.RowDefinitions> <RowDefinition Height="120"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- Back button and page title --> <Grid x:name="gridtop"> <Grid.ColumnDefinitions> <ColumnDefinition Width="120"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Button x:name="buttonback" Style="StaticResource NavigationBackButtonNormalStyle" Margin="40,40,0,0" VerticalAlignment="Top" Click="ButtonBack_Click"/> <TextBlock x:name="texttitle" Margin="0,40,0,0" Text="Title" Style="StaticResource HeaderTextBlockStyle" Grid.Column="1" VerticalAlignment="Top" IsHitTestVisible="false" TextWrapping="NoWrap" /> </Grid> <Grid x:name="gridproblem" Grid.RowSpan="2" Visibility="Collapsed"> <StackPanel Orientation="Vertical" Background="Orange" HorizontalAlignment="Stretch" VerticalAlignment="Center" > <TextBlock TextWrapping="Wrap" Foreground="Black" Text="Connection failed" FontSize="40" TextAlignment="Center" Padding="0,10,0,0"/> <TextBlock TextWrapping="Wrap" Foreground="Black" Text="Check your Internet connection" FontSize="20" TextAlignment="Center"/> 13
</Grid> <TextBlock TextWrapping="Wrap" Foreground="Black" Text="or try again later" FontSize="20" TextAlignment="Center" Padding="0,0,0,10"/> </StackPanel> </Grid> <ProgressRing x:name="progressringloading" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Width="100" Height="100" Foreground="Red" IsActive="True"/> 5. Aby umożliwić powrót ze strony odtwarzacza na stronę główną dodaj implementację atrybutu Click="ButtonBack_Click" w elemancie buttonback. this.frame.goback(); 6. Pobierz i zainstaluj odtwarzacz Microsoft Player Framework (wymagane prawa administratora). 7. Dodaj referencję do Microsoft Player Framework SDK. 8. W pliku XAML odtwarzacza umieść, wewnątrz kontrolki Grid kontrolkę odtwarzacza MadiaPlayer. <mpf:mediaplayer x:name="mediaplayer" DoubleTapped="MediaPlayer_DoubleTapped" Grid.Row="1" IsFullScreenVisible="True" IsScrubbingEnabled="True" /> 9. Jego definicja znajduje się w przestrzeni nazw Microsoft.PlayerFramework. xmlns:mpf="using:microsoft.playerframework" 10. Dodaj do pliku zawierającego kod C# strony odtwarzacza prywatną zmienną typu YouTubeVideo przechowywującą film przekazany z głównej strony aplikacji. Dodaj także przeciążoną metodę OnNavigatedTo ładującą wspomniany film, aktualizującą nazwę strony oraz aktywującą na czas ładowania filmu kontrolkę ProgressRing. private static YouTubeVideo _video; protected override async void OnNavigatedTo(NavigationEventArgs e) if (e.parameter == null) _video = new YouTubeVideo(); else _video = ((YouTubeVideo)e.Parameter); 14
texttitle.text = _video.title; progressringloading.isactive = true; try YouTubeUri url = await YouTube.GetVideoUriAsync(_video.VideoId, YouTubeQuality.Quality1080P); mediaplayer.source = url.uri; catch (Exception) gridproblem.visibility = Visibility.Visible; mediaplayer.visibility = Visibility.Collapsed; progressringloading.isactive = false; 11. W celu obsługi nowo dodanych klas YouTube wymagane jest dodanie projektu MyToolkit.Extended, najlepiej przy pomocy menedżera pakietów NuGet. Pamiętaj o dodaniu dostępu do wymaganej przestrzeni nazw MyToolkit.Multimedia i folderu DataModel. 12. Dodaj implementację uchwytu MediaPlayer_DoubleTapped z pliku XAML odtwarzacza pozwalającą na przełączanie pomiędzy pełnym ekranem a oknem. mediaplayer.isfullscreen =!mediaplayer.isfullscreen; 13. Sama zmiana wartości IsFullScreen niestety nie wystarczy, musimy jaszcze zdefiniować i podpiąć odpowiednią metodę do zdarzenia IsFullScreenChanged obiektu mediaplayer. public PlaybackPage() this.initializecomponent(); mediaplayer.isfullscreenchanged += mediaplayer_isfullscreenchanged; private void mediaplayer_isfullscreenchanged(object sender, RoutedPropertyChangedEventArgs<bool> e) if (e.newvalue) Grid.SetRow(mediaPlayer, 0); Grid.SetRowSpan(mediaPlayer, 2); gridtop.visibility = Visibility.Collapsed; else Grid.SetRow(mediaPlayer, 1); Grid.SetRowSpan(mediaPlayer, 1); gridtop.visibility = Visibility.Visible; VI. Wyświetlanie polityki prywatności. 15
Cel: Zapoznanie ze sposobem automatycznego generowania strony dotyczącej polityki prywatności. 1. Stronę wyświetlającą politykę prywatności można wygenerować automatycznie korzystając z witryny: http://w8privacy.azurewebsites.net/. 2. Podaj Nazwę Wyświetlaną Wydawcy (znajdziesz ją w ustawieniach twojego konta developerskiego), adres email oraz nazwę właśnie tworzonej aplikacji. 3. Skopiuj wygenerowany link z pola adresu przeglądarki 4. Dodaj do projektu nowy element Settings Flyout. 5. Dodaj do nowego pliku XAML poniższy kod. <!-- Content Section 1--> <StackPanel Style="StaticResource SettingsFlyoutSectionStyle"> <!-- Section 1 header --> <TextBlock Style="StaticResource TitleTextBlockStyle" Text="About the use of personal data." /> <!-- Section 1 body --> <TextBlock Style="StaticResource BodyTextBlockStyle" Margin="0,20,0,0" TextWrapping="Wrap" FontFamily="Global User Interface"> <Run> This app uses your internet connection only to download and update content. </Run> <LineBreak/> <LineBreak/> <Run> This application does not: </Run> <LineBreak/> <LineBreak/> 16
<Run> - Collect and use any personal information. </Run> <LineBreak/> <Run FontFamily="Global User Interface"> - Store any personal data. </Run> <LineBreak/> <Run FontFamily="Global User Interface"> - Share any personal data to third parties. </Run> </TextBlock> <HyperlinkButton Margin="0,20,0,0" Content="Online Privacy Policy" NavigateUri="Your Privacy Policy URL here." /> </StackPanel> <!-- Define more Content Sections below as necessary --> 6. W miejscu Your Privacy Policy URL here. Podaj skopiowany wcześniej link URL. Należy zastąpić wszystkie znaki & na & ponieważ są niezgodne ze specyfikacją XML a w szczególności z XAML. W oknie designera powinna pojawić na się podobna strona. 7. W pliku MainPage.cs należy dodać poniższy kod do konstruktora, uczyni to naszą stronę dostępną. SettingsPane.GetForCurrentView().CommandsRequested += MainPage_CommandsRequested; 8. Oczywiście musimy jeszcze zaimplementować odpowiednią metodę. 17
SettingsCommand privacycommand = new SettingsCommand("privacy", "Privacy Policy", (handler) => PrivacyFlyout privacyflyout = new PrivacyFlyout(); privacyflyout.show(); ); args.request.applicationcommands.add(privacycommand); 9. Oraz przeciążyć metodę OnNavigatedFrom wyrejestrowującą nasz uchwyt. protected override void OnNavigatedFrom(NavigationEventArgs e) base.onnavigatedfrom(e); SettingsPane.GetForCurrentView().CommandsRequested -= MainPage_CommandsRequested; 10. Strona w akcji: VII. Finalizacja aplikacji. Cel: Zapoznanie z ostatnimi czynnościami wymaganymi przed opublikowaniem aplikacji. 1. Należy jeszcze zmienić jeszcze kilka ustawień w pliku manifestu. W sekcji Application dodaj wyświetlaną nazwę oraz opis aplikacji. 18
2. W zakładce Visual Assets wypełnij pola podobnie jak na poniższym rysunku. Ponownie użyj wyświetlanej nazwy aplikacji. 3. Na koniec w zakładce Packaging sprawdź wyświetlaną nazwę oraz wyświetlaną nazwę wydawcy. VIII. Utworzenie pliku aplikacji gotowego do umieszczenia w sklepie. Cel: Zapoznanie z kolejnymi krokami wymaganymi do utworzenia i wstępnej walidacji pliku aplikacji. 1. Aby utworzyć plik aplikacji należy zapisać zmiany w projekcie i przebudować projekt (Rebuild). 19
2. Wybrać PROJECT Store Create App Packages Yes Next (należy się zalogować do konta developerskiego w celu automatycznego podpisania aplikacji naszym certyfikatem developera). Następnie w poniższym oknie należy wybrać opcję Next. 3. W kolejnym oknie zostawiamy domyślne wartości i klikamy Create 20
4. Teraz możemy dokonać wstępnej walidacji (wymagane uprawnienia administratora). 5. Zostawiamy zaznaczone wszystkie dostępne testy i klikamy Dalej. 21
6. Czekamy z nadzieją, że wszystkie testy zostaną zaliczone, i zobaczymy poniższe okno. IX. Publikowanie aplikacji w Windows Store. Cel: Zapoznanie z czynnościami wymaganymi do opublikowania aplikacji w Windows Store. 1. Zaloguj się do własnego pulpitu developera. Wybierz aplikację do publikacji. W sekcji Submissions wybierz Pricing and availability. 2. Ustal Base price na Free i zatwierdź przyciskiem Save. Domyślnie aplikacja będzie dostępna na 242 rynkach, należy uważać jeśli zawiera ona kanał powiązany tematycznie z hazardem, alkoholem, nieodpowiednim humorem lub treściami dla dorosłych może nie przejść certyfikacji na pewnych rynkach zachodnich. 3. Następnie w App properties wybierz kategorię do której aplikacja będzie się zaliczać oraz wymagania wiekowe. Dla normalnych aplikacji 12+ jest optymalnym wyborem. 22
4. W sekcji Packages należy wybrać opcję browse to files i dodać pliki utworzonej aplikacji. W tym przypadku pliki z rozszerzeniem *.appxupload z folderu AppPackages. 5. W menu Descriptions musimy dodać przynajmniej opis i po jednym zrzucie ekranu działającej aplikacji dla wersji desktopowej i mobilnej. Dla wersji mobilnej należy skorzystać z symulatora. 6. Sekcja Notes for certification jest opcjonalna. Jeśli chcemy możemy poinformować testerów o szczegółach naszej aplikacji. W naszym przypadku nie jest to wymagane. 7. Po wykonaniu wymaganych 4 kroków nasza aplikacja jest gotowa do wysłania do sklepu. Proszę potwierdzić klikając Submit to the store. 8. Na koniec przypominam o wyrejestrowaniu konta developerskiego z komputera laboratoryjnego. Informacja jak to zrobić podana została na początku 2 rozdziału Tworzenie aplikacji Windows Store (C# i XAML). 23