{"id":1368,"date":"2022-12-04T17:35:08","date_gmt":"2022-12-04T17:35:08","guid":{"rendered":"https:\/\/www.budus.it\/Blog\/?p=1368"},"modified":"2022-12-05T07:57:30","modified_gmt":"2022-12-05T07:57:30","slug":"c-winui-wints-template-studio-inavigationservice","status":"publish","type":"post","link":"https:\/\/www.budus.it\/Blog\/c-winui-wints-template-studio-inavigationservice\/","title":{"rendered":"C# + WinUI + WinTS (Template Studio): INavigationService"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Ho gi\u00e0 trattato di WinUI 3 in un altro articolo (<a href=\"https:\/\/www.budus.it\/Blog\/c-winui-3-mvvm-toolkit-enter-like-tab-key-to-move-focus-on-next-element\/\" data-type=\"post\" data-id=\"1365\">C# + WinUI 3 + MVVM Toolkit (Enter like Tab Key to Move Focus on Next Element)<\/a>), qui mi soffermo sull&#8217;uso di <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=WASTeamAccount.WindowsTemplateStudio\" target=\"_blank\" rel=\"noreferrer noopener\">Template Studio<\/a> e alla possibilit\u00e0 di <em>navigare tra le pagine<\/em>, un po&#8217; come fosse un sito web.<\/p>\n\n\n\n<!--more-->\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Template Studio<\/p>\n<cite>Rappresenta un Wizard per <em>imbastire un&#8217;App in modo semplice<\/em> tramite Visual Studio (io uso Community, ma andr\u00e0 bene anche per i fratelli maggiori).<\/cite><\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Sia chiaro: <strong>non prendete niente per oro colato, quello che scrivo qui \u00e8 un archivio di soluzioni che ho trovato a problemi che devo affrontare e che voglio custodire per poterle ritrovare, nonch\u00e9 tutta roba che potrebbe pure essere <em>di pessimo esempio<\/em>.<\/strong> Sta a chi legge valutare il contenuto, anche sulla base di quanto trova altrove (se lo trova &#8230;).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Date le doverose premesse, passiamo a vedere come ho risolto la faccenda.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Non sto ad elencare per filo e per segno; sostanzialmente ho creato la base di un&#8217;App con Template Studio, che ho chiamato &#8220;<code>TemplateStudioForWinUISample<\/code>&#8220;, dove la pagina &#8220;<strong>Main<\/strong>&#8221; era vuota e l&#8217;ho riempita con un piccolo form.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quando invio il form, voglio che venga visualizzata una pagina riepilogativa, tutto qui, anzi, non ho nemmeno implementato la validazione (che comunque ho descritto nell&#8217;altro articolo linkato sopra), mi serviva giusto il minimo indispensabile per vedere se funziona e capire il meccanismo di <strong>passaggio da una pagina all&#8217;altra, <em>decidendo io se i dati devono essere mantenuti o meno<\/em><\/strong> (per default vengono persi  o mantenuti sempre in base all&#8217;impostazione di &#8220;<code>NavigationCacheMode = Microsoft.UI.Xaml.Navigation.NavigationCacheMode.Enabled;<\/code>&#8220;).<\/p>\n\n\n\n<pre class=\"wp-block-verse\">S\u00ec, sto brancolando nel buio e mi arrangio alla meno peggio, per questo <strong>invito a prendere tutto con le pinze !<\/strong><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Iniziamo con <code>TemplateStudioForWinUISample\\Views\\MainPage.xaml<\/code>, che rappresenta la UI della pagina <strong>Main<\/strong>, inizialmente vuota, quella dove io ci ho messo il form:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;Page\n    x:Class=\"TemplateStudioForWinUISample.Views.MainPage\"\n    xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n    xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\n    xmlns:d=\"http:\/\/schemas.microsoft.com\/expression\/blend\/2008\"\n    xmlns:mc=\"http:\/\/schemas.openxmlformats.org\/markup-compatibility\/2006\"\n    mc:Ignorable=\"d\">\n\n    &lt;Grid x:Name=\"ContentArea\">\n        &lt;StackPanel Spacing=\"20\">\n            &lt;TextBox x:Name=\"TextBoxName\" Header=\"Nome:\" Text=\"{x:Bind ViewModel.Name, Mode=TwoWay}\" KeyDown=\"KeyDownEventHandler\" \/>\n            &lt;TextBox Header=\"Nome:\" Text=\"{x:Bind ViewModel.Surname, Mode=TwoWay}\" KeyDown=\"KeyDownEventHandler\" \/>\n            &lt;Border BorderThickness=\"1\" BorderBrush=\"Aqua\" \/>\n            &lt;Button Content=\"Invia ...\" HorizontalAlignment=\"Right\" Click=\"SubmitButtonEventHandler\" \/>\n        &lt;\/StackPanel>\n    &lt;\/Grid>\n&lt;\/Page>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La UI viene amministrata da questa classe <code>TemplateStudioForWinUISample\\Views\\MainPage.xaml.cs<\/code> (Template Studio usa il pattern MVVM e qui siamo nella <em><strong>View<\/strong><\/em>):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System.Diagnostics;\nusing Microsoft.UI.Xaml;\nusing Microsoft.UI.Xaml.Controls;\nusing Microsoft.UI.Xaml.Input;\nusing TemplateStudioForWinUISample.Contracts.Services;\nusing TemplateStudioForWinUISample.Contracts.ViewModels;\nusing TemplateStudioForWinUISample.ViewModels;\n\nnamespace TemplateStudioForWinUISample.Views;\n\npublic sealed partial class MainPage : Page\n{\n    public MainViewModel ViewModel\n    {\n        get;\n    }\n\n    public MainPage()\n    {\n        ViewModel = App.GetService&lt;MainViewModel>();\n        InitializeComponent();\n\n        NavigationCacheMode = Microsoft.UI.Xaml.Navigation.NavigationCacheMode.Enabled;\n\n        Loaded += MainPage_Loaded;\n    }\n\n    private void MainPage_Loaded(object sender, RoutedEventArgs e) {\n        TextBoxName.Focus(FocusState.Programmatic);\n    }\n\n    \/\/\/ &lt;summary>\n    \/\/\/ Gestisce la pressione del \"Return\" sui campi del form, affinch\u00e9 sia come premere TAB.\n    \/\/\/ &lt;\/summary>\n    \/\/\/ &lt;param name=\"sender\">&lt;\/param>\n    \/\/\/ &lt;param name=\"e\">&lt;\/param>\n    \/\/\/ &lt;exception cref=\"ArgumentNullException\">&lt;\/exception>\n    private void KeyDownEventHandler(object sender, KeyRoutedEventArgs e)\n    {\n        if (sender is null)\n        {\n            throw new ArgumentNullException(nameof(sender));\n        }\n\n        if (e is null)\n        {\n            throw new ArgumentNullException(nameof(e));\n        }\n\n        if (e.Key == Windows.System.VirtualKey.Enter)\n        {\n            try\n            {\n                FindNextElementOptions options = new()\n                {\n                    SearchRoot = (DependencyObject)((Control)sender).XamlRoot.Content\n                };\n\n                FocusManager.TryMoveFocus(FocusNavigationDirection.Next, options);\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"ERRORE: {ex.Message}\");\n            }\n        }\n    }\n\n    \/\/\/ &lt;summary>\n    \/\/\/ Gestione dell'Invio del Form: passa alla pagina di riepilogo.\n    \/\/\/ &lt;\/summary>\n    \/\/\/ &lt;param name=\"sender\">&lt;\/param>\n    \/\/\/ &lt;param name=\"e\">&lt;\/param>\n    \/\/\/ &lt;exception cref=\"ArgumentNullException\">&lt;\/exception>\n    private void SubmitButtonEventHandler(object sender, RoutedEventArgs e)\n    {\n        if (sender is null)\n        {\n            throw new ArgumentNullException(nameof(sender));\n        }\n\n        if (e is null)\n        {\n            throw new ArgumentNullException(nameof(e));\n        }\n\n        Debug.WriteLine($\"NOME ..: {ViewModel.Name}\\nCOGNOME: {ViewModel.Surname}\");\n        try\n        {\n            var service = App.GetService&lt;INavigationService>();\n            service.NavigateTo(typeof(ReportViewModel).FullName!, ViewModel);\n        }\n        catch (Exception ex)\n        {\n            Debug.WriteLine($\"ERRORE: {ex.Message}\");\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La <em><strong>View<\/strong><\/em> va a <em>pescare i dati<\/em> dalla <em><strong>ViewModel<\/strong><\/em> relativa, quindi abbiamo <code>TemplateStudioForWinUISample\\ViewModels\\MainViewModel.cs<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System.Diagnostics;\nusing CommunityToolkit.Mvvm.ComponentModel;\nusing TemplateStudioForWinUISample.Contracts.ViewModels;\n\nnamespace TemplateStudioForWinUISample.ViewModels;\n\npublic partial class MainViewModel : ObservableRecipient, INavigationAware\n{\n    public MainViewModel()\n    {\n    }\n\n    public void OnNavigatedTo(object parameter)\n    {\n        if (parameter is bool &amp;&amp; (bool)parameter)\n        {\n            \/\/ \u00c8 stato chiesto il reset.\n            Name = string.Empty;\n            Surname= string.Empty;\n        }\n    }\n\n    public void OnNavigatedFrom()\n    {\n    }\n\n    &#91;ObservableProperty]\n    private string name = string.Empty;\n\n    &#91;ObservableProperty]\n    private string surname = string.Empty;\n\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nel codice sopra, non bisogna farsi <em>ingannare<\/em> dalle poche righe, una classe che <em>sembra<\/em> insignificante: usando &#8220;<code>ObservableRecipient<\/code>&#8221; (<a rel=\"noreferrer noopener\" href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/communitytoolkit\/mvvm\/introduction\" target=\"_blank\">MVVM Toolkit<\/a>), avviene una magia, tale per cui tutto il codice che <em>un tapino deve normalmente scrivere, viene creato in automatico dal tookit!<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sostanzialmente viene fatto in modo che mentre si scrive nella UI, le corrispondenti variabili del <em><strong>ViewModel<\/strong><\/em> vengono inizializzate al valore corrente. La <em>magia<\/em> avviene dichiarando le variabili &#8220;<code>private string name ...<\/code>&#8221; ed usando il tag &#8220;<code>[ObservableProperty]<\/code>&#8220;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ok, ora passiamo alla pagina del report: io l&#8217;ho aggiunta usando Template Studio (tasto destro sul progetto, Add ma poi si usa il menu apposito, non quello per aggiungere una classe qualsiasi) e poi l&#8217;ho rimossa da <code>Views\\ShellPage.xaml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...cut...\n&lt;\/NavigationViewItem&gt;\n                &lt;!--\n                &lt;NavigationViewItem x:Uid=\"Shell_Report\" helpers:NavigationHelper.NavigateTo=\"TemplateStudioForWinUISample.ViewModels.ReportViewModel\"&gt;\n                    &lt;NavigationViewItem.Icon&gt;\n                        &lt;FontIcon FontFamily=\"{StaticResource SymbolThemeFontFamily}\" Glyph=\"&amp;#xe7c3;\"\/&gt;\n                    &lt;\/NavigationViewItem.Icon&gt;\n                &lt;\/NavigationViewItem&gt;\n                --&gt;\n            &lt;\/NavigationView.MenuItems&gt;\n...cut...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Praticamente un banale commento &#8220;<code>&lt;!-- ... --&gt;<\/code>&#8220;, potevo anche cancellare &#8230; ma mi \u00e8 risultato pi\u00f9 utile cos\u00ec: posso ripristinarla per modificare la UI agevolmente, per poi <em>cancellarla<\/em> di nuovo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Iniziamo con la UI del report <code>TemplateStudioForWinUISample\\Views\\ReportPage.xaml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;Page\n    x:Class=\"TemplateStudioForWinUISample.Views.ReportPage\"\n    xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n    xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\n    xmlns:d=\"http:\/\/schemas.microsoft.com\/expression\/blend\/2008\"\n    xmlns:mc=\"http:\/\/schemas.openxmlformats.org\/markup-compatibility\/2006\"\n    mc:Ignorable=\"d\">\n\n    &lt;Grid x:Name=\"ContentArea\">\n        &lt;StackPanel Spacing=\"10\">\n            &lt;Grid ColumnSpacing=\"10\">\n                &lt;Grid.ColumnDefinitions>\n                    &lt;ColumnDefinition Width=\"Auto\"\/>\n                    &lt;ColumnDefinition Width=\"*\"\/>\n                &lt;\/Grid.ColumnDefinitions>\n\n                &lt;StackPanel Grid.Column=\"0\" Spacing=\"10\">\n                    &lt;TextBlock Text=\"Nome:\" HorizontalAlignment=\"Right\" \/>\n                    &lt;TextBlock Text=\"Cognome:\" HorizontalAlignment=\"Right\" \/>\n                &lt;\/StackPanel>\n                &lt;StackPanel Grid.Column=\"1\" Spacing=\"10\" >\n                    &lt;TextBlock Text=\"{x:Bind ViewModel.MainViewModel.Name,Mode=OneTime}\"\/>\n                    &lt;TextBlock Text=\"{x:Bind ViewModel.MainViewModel.Surname,Mode=OneTime}\"\/>\n                &lt;\/StackPanel>\n            &lt;\/Grid>\n            \n            &lt;Border BorderThickness=\"1\" BorderBrush=\"Aqua\" \/>\n            &lt;StackPanel Orientation=\"Horizontal\" Spacing=\"30\" HorizontalAlignment=\"Right\">\n                &lt;Button x:Name=\"ButtonAnnulla\" Content=\"Annulla\" Click=\"ButtonAnnullaEventHandler\" \/>\n                &lt;Button Content=\"Conferma\" Click=\"ButtonConfermaEventHandler\" \/>\n            &lt;\/StackPanel>\n            &lt;!-- Click=\"\" -->\n        &lt;\/StackPanel>\n    &lt;\/Grid>\n&lt;\/Page>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Quindi al relativo codice <em><strong>View<\/strong><\/em> <code>TemplateStudioForWinUISample\\Views\\ReportPage.xaml.cs<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System.Diagnostics;\nusing Microsoft.UI.Xaml.Controls;\nusing TemplateStudioForWinUISample.Contracts.Services;\nusing TemplateStudioForWinUISample.ViewModels;\n\nnamespace TemplateStudioForWinUISample.Views;\n\npublic sealed partial class ReportPage : Page\n{\n    public ReportViewModel ViewModel\n    {\n        get;\n    }\n\n    public ReportPage()\n    {\n        ViewModel = App.GetService&lt;ReportViewModel>();\n        InitializeComponent();\n\n        Loaded += ReportPage_Loaded;\n    }\n\n    private void ReportPage_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\n    {\n        ButtonAnnulla.Focus(Microsoft.UI.Xaml.FocusState.Programmatic);\n    }\n\n    \/\/\/ &lt;summary>\n    \/\/\/ Gestione dell'Annullamento del Form: Ritorna indietro.\n    \/\/\/ &lt;\/summary>\n    \/\/\/ &lt;param name=\"sender\">&lt;\/param>\n    \/\/\/ &lt;param name=\"e\">&lt;\/param>\n    \/\/\/ &lt;exception cref=\"ArgumentNullException\">&lt;\/exception>\n    private void ButtonAnnullaEventHandler(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\n    {\n        if (sender is null)\n        {\n            throw new ArgumentNullException(nameof(sender));\n        }\n\n        if (e is null)\n        {\n            throw new ArgumentNullException(nameof(e));\n        }\n\n        try\n        {\n            var service = App.GetService&lt;INavigationService>();\n            service.NavigateTo(typeof(MainViewModel).FullName!, false, clearNavigation: true);\n        }\n        catch (Exception ex)\n        {\n            Debug.WriteLine($\"ERRORE: {ex.Message}\");\n        }\n    }\n\n    private void ButtonConfermaEventHandler(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)\n    {\n        if (sender is null)\n        {\n            throw new ArgumentNullException(nameof(sender));\n        }\n\n        if (e is null)\n        {\n            throw new ArgumentNullException(nameof(e));\n        }\n\n        try\n        {\n            Debug.WriteLine($\"SUBMIT:\\n\\nNOME ..: {ViewModel.MainViewModel.Name}\\nCOGNOME: {ViewModel.MainViewModel.Surname}\");\n\n            var service = App.GetService&lt;INavigationService>();\n            service.NavigateTo(typeof(MainViewModel).FullName!, true, clearNavigation: true);\n        }\n        catch (Exception ex)\n        {\n            Debug.WriteLine($\"ERRORE: {ex.Message}\");\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ed infine al corrispondente <em><strong>ViewModel<\/strong><\/em> <code>TemplateStudioForWinUISample\\ViewModels\\ReportViewModel.cs<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using CommunityToolkit.Mvvm.ComponentModel;\nusing TemplateStudioForWinUISample.Contracts.ViewModels;\n\nnamespace TemplateStudioForWinUISample.ViewModels;\n\npublic class ReportViewModel : ObservableRecipient, INavigationAware\n{\n\n    private MainViewModel mainViewModel = new();\n\n    public MainViewModel MainViewModel => mainViewModel;\n\n    public ReportViewModel()\n    {\n    }\n\n    public void OnNavigatedTo(object parameter)\n    {\n        mainViewModel = (MainViewModel)parameter ?? throw new ArgumentNullException(nameof(parameter));\n    }\n\n    public void OnNavigatedFrom()\n    {\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Qui le parti salienti sono:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Il fatto di ereditare la classe &#8220;<code>ReportViewModel<\/code>&#8221; da &#8220;<code>INavigationAware<\/code>&#8220;.<\/li>\n\n\n\n<li>L&#8217;implementazione dell&#8217;Interfaccia, con le funzioni &#8220;<code>OnNavigatedTo<\/code>&#8221; e &#8220;<code>OnNavigatedFrom<\/code>&#8221; (quest&#8217;ultima peraltro vuota, ma l&#8217;Interfaccia in C# prevede che tutti i metodi siano implementati).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Spero che anche questo articolo sia di aiuto a qualche sfigato che, come me, si sta sbattendo per imparare a realizzare App per Windows 10\/11 &#8230; una tribolazione inaudita (almeno, nel momento in cui scrivo e per chi \u00e8 nelle mie &#8211; pessime &#8211; condizioni).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ho gi\u00e0 trattato di WinUI 3 in un altro articolo (C# + WinUI 3 + MVVM Toolkit (Enter like Tab Key to Move Focus on Next Element)), qui mi soffermo sull&#8217;uso di Template Studio e alla possibilit\u00e0 di navigare tra le pagine, un po&#8217; come fosse un sito web.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[57,6],"tags":[278,281,98,280,279],"class_list":["post-1368","post","type-post","status-publish","format-standard","hentry","category-development","category-software","tag-c-2","tag-mvvm","tag-windows","tag-wints","tag-winui"],"_links":{"self":[{"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/posts\/1368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/comments?post=1368"}],"version-history":[{"count":2,"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/posts\/1368\/revisions"}],"predecessor-version":[{"id":1371,"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/posts\/1368\/revisions\/1371"}],"wp:attachment":[{"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/media?parent=1368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/categories?post=1368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.budus.it\/Blog\/wp-json\/wp\/v2\/tags?post=1368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}