Maîtriser le shell .NET MAUI – Partie 3

Apprenez tout ce dont vous avez besoin pour maîtriser l’utilisation de Shell dans vos applications .NET MAUI. Dans le dernier article de la série : naviguez entre les pages et créez des SearchHandlers pour ajouter une fonctionnalité de recherche à vos pages.
C’est la dernière partie du Maîtriser le shell .NET MAUI série, où vous avez appris ce qui est nécessaire pour créer rapidement des applications multiplateformes à l’aide de Shell. Si vous avez suivi la série, vous savez déjà comment créer votre propre Shell, comment ajouter de nouvelles pages à la hiérarchie via le Flyout et les onglets, ainsi que comment personnaliser les couleurs. Dans cet article, vous apprendrez à naviguer entre les pages et à créer des SearchHandlers pour ajouter une fonctionnalité de recherche à vos pages. Commençons !
Navigation dans le shell .NET MAUI
Dans un article précédentnous avons vu comment créer une structure hiérarchique dans un fichier Shell, qui crée un système de navigation automatique. Cependant, que se passe-t-il si nous voulons naviguer du code vers d’autres pages ? C’est ce que nous verrons ensuite.
Affectation de routes aux éléments de coque
La première chose à savoir est que le système de navigation Shell permet d’attribuer le Route
propriété à chaque élément du Shell, dans le but de les identifier, comme dans l’exemple suivant :
<FlyoutItem
Title="Productivity Tools"
Icon="dotnet_bot.png"
Route="ProductivityTools">
<Tab
Title="Text Tools"
Icon="dotnet_bot.png"
Route="TextTools">
<ShellContent
Title="Word Counter"
ContentTemplate="{DataTemplate UtilPages:WordCounter}"
Icon="dotnet_bot.png"
Route="WordCounter" />
<ShellContent
Title="Color Generator"
ContentTemplate="{DataTemplate UtilPages:RandomColor}"
Icon="dotnet_bot.png"
Route="ColorGenerator" />
</Tab>
</FlyoutItem>
Le parcours complet de chaque élément sera concaténé avec le nom de l’élément hiérarchique supérieur. Autrement dit, l’itinéraire pour WordCounter
dans l’exemple précédent sera :
//ProductivityTools/TextTools/WordCounter
De cette façon, si deux ShellContent
les éléments ont le même nom, l’itinéraire ne sera pas le même, ce qui évite les conflits de navigation. Maintenant que vous savez comment attribuer des routes aux éléments Shell, voyons comment naviguer du code vers eux.
Ce que je ne vous ai pas dit auparavant, c’est qu’il est possible d’ajouter MenuItem
éléments dans un Flyout. Ces MenuItems peuvent être utilisés pour effectuer des opérations qui ne sont pas associées à la navigation vers une autre page.
Des exemples de ce type pourraient être si vous souhaitez que l’utilisateur se déconnecte ou l’envoie vers une page Web pour obtenir des informations supplémentaires.
Dans mon cas, je vais utiliser un MenuItem
pour effectuer différents tests de navigation. Le code que vous devez ajouter au Shell devrait ressembler à ceci :
<Shell...>
...
<Shell.MenuItemTemplate>
<DataTemplate>
<Grid
ColumnDefinitions=".2*, .8*"
HeightRequest="75"
RowSpacing="0">
<Rectangle
x:Name="background"
Grid.ColumnSpan="2"
Fill="Black"
Opacity=".5" />
<Image
HeightRequest="30"
Source="{Binding FlyoutIcon}"
VerticalOptions="Center" />
<Label
Grid.Column="1"
Margin="20,0,0,0"
FontSize="20"
Text="{Binding Title}"
TextColor="White"
VerticalOptions="Center" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter TargetName="background" Property="Rectangle.Fill" Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter TargetName="background" Property="Rectangle.Fill" Value="DarkRed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</Shell.MenuItemTemplate>
...
<FlyoutItem...>
<FlyoutItem...>
<MenuItem Clicked="Navigation_Clicked" Text="Navigate" IconImageSource="dotnet_bot.png"/>
</Shell>
Dans le code ci-dessus, j’ai ajouté un MenuItem
avec du texte, une référence pour le Clicked
événement et une icône. Pour que l’apparence soit la même que celle des éléments du menu, vous devez appliquer le même style de Shell.ItemTemplate
à Shell.MenuItemTemplate
.
Navigation entre les éléments du shell via le code
Une fois le code XAML prêt, l’étape suivante consiste à accéder au code-behind de MyShell.xaml
(bien que vous puissiez également le faire à partir d’un ViewModel), en particulier vers le Navigation_Clicked
gestionnaire d’événements. Pour effectuer la navigation, nous devons utiliser le GoToAsync
méthode qui fait partie de l’instance globale de Shell, à laquelle nous pouvons accéder comme suit :
private async void Navigation_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("Route");
}
Dans le code ci-dessus, vous devez changer le mot Route
à l’itinéraire vers lequel vous souhaitez naviguer. Dans une section précédente, j’avais mentionné que le tracé final d’un ContentPage
est déterminé par les noms des pages ancêtres, nous pouvons donc accéder à WordCounter en utilisant l’itinéraire complet que je vous ai montré plus tôt :
await Shell.Current.GoToAsync("//ProductivityTools/TextTools/WordCounter");
De cette façon, en appuyant sur le MenuItem
nous avons ajouté, la navigation vers le Compteur de mots outil sera réalisé.
Maintenant, vous vous demandez peut-être ce qui se passe si nous n’avons pas attribué de nom à l’un des éléments hiérarchiques supérieurs à ShellContent. Dans ce cas, le framework attribuera un nom générique à l’élément Shell, nous ne pourrons donc pas naviguer avec un itinéraire complet comme nous l’avons fait précédemment.
L’alternative est d’utiliser directement la route du ShellContent, à l’aide de trois barres obliques (///), comme suit :
await Shell.Current.GoToAsync("///WordCounter");
De cette façon, nous pouvons désormais effectuer une navigation correcte. Dans ce cas, nous devons avoir des itinéraires uniques pour chacun ShellContent
pour éviter les problèmes de navigation.
Cinquième utilitaire : dictionnaire
Jusqu’à présent, nous avons créé des outils d’une seule page. Créons un nouvel exemple de type dictionnaire, qui sera composé d’une première page où l’utilisateur pourra saisir un mot à rechercher et d’une deuxième page pour les résultats de la recherche.
La première page que nous ajouterons s’appellera Dictionary.xaml
et sera composé des éléments suivants :
- UN
VerticalStackLayout
pour regrouper les champs - UN
Label
pour afficher du texte à l’utilisateur - UN
RadBorder
pour améliorer l’esthétique visuelle d’unRadEntry
- UN
RadEntry
pour que l’utilisateur saisisse le mot à rechercher
Le RadBorder
Le contrôle est très utile pour regrouper des éléments et en même temps, appliquer une bordure pour donner une apparence visuelle différente. Le code XAML est le suivant :
<VerticalStackLayout
HorizontalOptions="Center"
Spacing="15"
VerticalOptions="Center">
<Label
FontSize="25"
HorizontalOptions="Center"
Text="Search in dictionary:"
VerticalOptions="Center" />
<telerik:RadBorder
AutomationId="border"
BorderColor="#2679FF"
BorderThickness="3"
CornerRadius="5">
<telerik:RadEntry
x:Name="searchTerm"
Completed="RadEntry_Completed"
FontSize="14"
Placeholder="Telerik"
PlaceholderColor="#99000000"
WidthRequest="250" />
</telerik:RadBorder>
<Button Clicked="Search_Clicked" Text="Search!" />
</VerticalStackLayout>
La deuxième page s’appellera DictionaryDefinition.xaml
et sera composé des contrôles suivants :
- UN
Grid
comme conteneur principal, divisé en deux rangées : la première avec 20 % d’espace et la seconde avec les 80 % restants. - UN
VerticalStackLayout
pour l’en-tête, qui à son tour contiendra deuxLabel
commandes pour afficher le mot et son expression phonétique. - Une seconde
VerticalStackLayout
qui contiendra une série deLabel
contrôles pour afficher le type de mot, les définitions, les synonymes et les antonymes.
Le code XAML de la page est le suivant :
<Grid RowDefinitions=".2*,.8*">
<Grid Background="#4a4a4a">
<VerticalStackLayout Spacing="15" VerticalOptions="Center">
<Label
FontSize="25"
HorizontalOptions="Center"
Text="{Binding Word}"
TextColor="White"
VerticalOptions="Center" />
<Label
FontSize="20"
HorizontalOptions="Center"
Text="{Binding Phonetic}"
TextColor="#9c9a9a"
VerticalOptions="Center" />
</VerticalStackLayout>
</Grid>
<ScrollView Grid.Row="1">
<VerticalStackLayout Margin="20">
<Label
FontAttributes="Bold"
FontSize="16"
Text="Noun"
TextColor="#363535" />
<Label Text="{Binding Definitions}" />
<Label
Margin="0,20,0,0"
FontAttributes="Bold"
FontSize="16"
Text="Synonyms"
TextColor="#363535" />
<Label Text="{Binding Synonyms}" />
<Label
Margin="0,20,0,0"
FontAttributes="Bold"
FontSize="16"
Text="Antonyms"
TextColor="#363535" />
<Label Text="{Binding Antonyms}" />
</VerticalStackLayout>
</ScrollView>
</Grid>
Une fois les deux pages XAML prêtes, n’oubliez pas d’ajouter à la hiérarchie la page principale qui appartiendra à la navigation Shell. Pour cet exemple, j’ai ajouté le Dictionary.xaml
page vers le TextTools
languette:
<Tab
Title="Text Tools"
Icon="dotnet_bot.png"
Route="TextTools">
...
<ShellContent
Title="Dictionary"
ContentTemplate="{DataTemplate UtilPages:Dictionary}"
Icon="dotnet_bot.png" />
</Tab>
Grâce à cela, il est désormais possible de naviguer vers la nouvelle page dans l’interface graphique, comme indiqué ci-dessous :
Voyons maintenant comment accéder à la page de résultats.
Accès à la page de définition de mot
Une fois que nous pouvons naviguer dans l’interface de l’outil Dictionnaire, l’étape suivante consiste à pouvoir accéder au DictionaryDefinition.xaml
page qui ne fait pas partie du Shell. Pour y parvenir, nous devons enregistrer les pages qui ne font pas partie de la hiérarchie Shell initiale, mais que nous souhaitons utiliser pour les actions de navigation. Nous le ferons à travers le RegisterRoute
méthode.
Le processus d’enregistrement doit être effectué avant que le Shell ne soit affiché à l’utilisateur. L’endroit idéal est donc dans App.xaml.cs
comme suit:
public partial class App : Application
{
public App()
{
InitializeComponent();
RegisterRoutes();
MainPage = new MyShell();
}
private void RegisterRoutes()
{
Routing.RegisterRoute("dictionarydefinition", typeof(DictionaryDefinition));
}
}
Dans le code ci-dessus, vous pouvez voir que RegisterRoute
reçoit un identifiant pour l’itinéraire avec lequel nous arriverons à la page (il peut s’agir de n’importe quel nom que vous souhaitez) et le type de page. Grâce à ce code, il sera possible de naviguer vers la page de détails.
Dans notre exemple, nous y parviendrons en allant au Dictionary.xaml.cs
fichier et création d’une méthode appelée Search
pour exécuter la navigation à travers le code suivant :
public partial class Dictionary : ContentPage
{
public Dictionary()
{
InitializeComponent();
}
private void RadEntry_Completed(object sender, EventArgs e)
{
Search();
}
private void Search_Clicked(object sender, EventArgs e)
{
Search();
}
private async void Search()
{
if (!string.IsNullOrEmpty(searchTerm.Text))
{
await Shell.Current.GoToAsync($"dictionarydefinition");
}
}
}
Lorsque vous exécutez l’application, vous verrez que si vous entrez un mot dans la zone de recherche et appuyez sur le bouton Rechercher, vous accéderez à la page de détails. La page de détails est vide pour le moment car nous devons transmettre les informations du mot recherché, ce que nous ferons ensuite.
Transmission d’informations aux pages de détails
Une exigence dans la plupart des applications mobiles est de pouvoir envoyer des informations d’une page à une autre. Cela se produit généralement lorsque vous souhaitez transmettre des données d’une page faisant partie de la hiérarchie Shell vers une page de détails.
Dans .NET MAUI, il existe plusieurs façons de transmettre des informations, que nous analyserons ci-dessous.
Envoi d’une seule donnée à la page de détails à l’aide de QueryProperty
Pour continuer notre démonstration, nous avons besoin que des informations circulent entre les pages. C’est pourquoi je vais modifier la méthode Search que nous avons vue précédemment, pour interroger un service gratuit qui nous permet d’obtenir les données d’un mot dans un dictionnaire, comme suit :
private async void Search()
{
if (string.IsNullOrEmpty(searchTerm.Text))
{
return;
}
using (var client = new HttpClient())
{
string url = $"https://api.dictionaryapi.dev/api/v2/entries/en/{searchTerm.Text}";
try
{
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var definitions = JsonSerializer.Deserialize<List<DictionaryRecord>>(content);
var firstDefinition = definitions?.FirstOrDefault();
if (firstDefinition != null)
{
var word = firstDefinition.word;
await Shell.Current.GoToAsync($"dictionarydefinition?word={word}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error fetching definition: {ex.Message}");
}
}
}
Vous pouvez voir que le code ci-dessus appelle le service en utilisant HttpClient
en effectuant une désérialisation et en obtenant le premier enregistrement renvoyé. Une fois ce premier enregistrement obtenu, le word
la propriété qui représente le mot recherché est obtenue et envoyée en paramètre à la page enregistrée sous dictionarydefinition
.
En revanche, la page qui recevra l’information (dans notre exemple DictionaryDefinition.xaml.cs
) doit être prêt à recevoir les données que nous avons envoyées. Ceci est facile à réaliser, car cela nécessite uniquement une propriété où la valeur sera stockée, ainsi que l’ajout du QueryProperty
attribut, qui reçoit le nom de la propriété de destination, ainsi que le nom du paramètre spécifié dans la requête à partir de laquelle la navigation a été effectuée (dans notre exemple word
). Dans notre exemple, le code est le suivant :
[QueryProperty(nameof(Word), "word")]
public partial class DictionaryDefinition : ContentPage
{
private string word;
public string Word
{
get => word;
set
{
word = value;
OnPropertyChanged(nameof(Word));
}
}
public DictionaryDefinition()
{
InitializeComponent();
BindingContext = this;
}
}
Lorsque nous exécuterons à nouveau l’application, nous verrons correctement le mot recherché sur la page de détails :
Passer des objets à la page de détails à l’aide de IQueryAttributable
Il peut arriver que vous souhaitiez envoyer plusieurs données à la page de détails. Dans cette situation, nous devrions utiliser le IQueryAttributable
interface, ce qui nécessite la mise en œuvre de l’interface ApplyQueryAttributes
méthode qui a la forme suivante :
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
throw new NotImplementedException();
}
Vous pouvez voir que la méthode reçoit un dictionnaire appelé query
où la clé représente le nom du paramètre reçu de la page précédente, tandis que la valeur correspond à l’objet envoyé. Pour notre exemple, notre classe ressemblerait à ceci :
public partial class DictionaryDefinition : ContentPage, IQueryAttributable
{
private string word;
private string phonetic;
private string definitions;
private string synonyms;
private string antonyms;
public string Word
{
get => word;
set
{
word = value;
OnPropertyChanged(nameof(Word));
}
}
public string Phonetic
{
get => phonetic;
set
{
phonetic = value;
OnPropertyChanged(nameof(Phonetic));
}
}
public string Definitions
{
get => definitions;
set
{
definitions = value;
OnPropertyChanged(nameof(Definitions));
}
}
public string Synonyms
{
get => synonyms;
set
{
synonyms = value;
OnPropertyChanged(nameof(Synonyms));
}
}
public string Antonyms
{
get => antonyms;
set
{
antonyms = value;
OnPropertyChanged(nameof(Antonyms));
}
}
public DictionaryDefinition()
{
InitializeComponent();
BindingContext = this;
}
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
var dictionaryRecord = query["definition"] as DictionaryRecord;
Word = dictionaryRecord.word;
Phonetic = dictionaryRecord.phonetics.FirstOrDefault().text;
Definitions = string.Join("\n\n", dictionaryRecord.meanings.FirstOrDefault().definitions.Select(d => d.definition));
Synonyms = string.Join("\n\n", dictionaryRecord.meanings.FirstOrDefault().synonyms);
Antonyms = string.Join("\n\n", dictionaryRecord.meanings.FirstOrDefault().antonyms);
}
}
Par contre, nous remplacerons le Search
méthode dans le Dictionary.xaml.cs
déposer comme suit :
private async void Search()
{
if (string.IsNullOrEmpty(searchTerm.Text))
{
return;
}
using (var client = new HttpClient())
{
string url = $"https://api.dictionaryapi.dev/api/v2/entries/en/{searchTerm.Text}";
try
{
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var definitions = JsonSerializer.Deserialize<List<DictionaryRecord>>(content);
var firstDefinition = definitions?.FirstOrDefault();
if (firstDefinition != null)
{
var parameters = new Dictionary<string, object>
{
{ "definition", firstDefinition }
};
await Shell.Current.GoToAsync($"dictionarydefinition", parameters);
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error fetching definition: {ex.Message}");
}
}
}
Dans le code ci-dessus, vous pouvez voir que l’on crée une variable de type Dictionary
appelés paramètres, qui ont la même signature que le paramètre attendu par le ApplyQueryAttributes
méthode, et que nous avons initialisé avec la clé definition
et la valeur de firstDefinition
.
Aussi, dans le GoToAsync
méthode, nous avons remplacé le paramètre dans la chaîne par un paramètre de méthode. Avec cela, lors de l’exécution de l’application, nous pourrons voir les informations du mot recherché :
Dans l’image ci-dessus, vous pouvez voir le résultat de la recherche d’un mot.
Ajout d’une fonction de recherche avec .NET MAUI Shell
.NET MAUI Shell comprend une fonction de recherche que nous pouvons ajouter pour effectuer des recherches d’informations. Pour l’activer, nous devons créer une classe qui hérite de SearchHandler
comme dans l’exemple suivant :
public class DataSearchHandler : SearchHandler
{
public DataSearchHandler()
{
ItemsSource = new List<string>
{
"Apple",
"Banana",
"Cherry",
"Date",
"Grape",
"Lemon",
"Mango",
"Orange",
"Pineapple",
"Strawberry",
"Watermelon"
};
}
}
Le point le plus important à souligner est qu’une fois que nous héritons de SearchHandler, nous avons le ItemsSource
propriété, qui contiendra les informations sur les éléments que nous souhaitons inclure dans la recherche.
Une fois que nous avons un SearchHandler
nous pouvons l’ajouter à n’importe quelle page. À titre d’exemple, j’ai créé une nouvelle page appelée SearchPage.xaml
avec le SearchHandler
comme contenu :
<Shell.SearchHandler>
<search:DataSearchHandler Placeholder="Search Item" ShowsResults="True" />
</Shell.SearchHandler>
Après avoir ajouté cette page à la structure Shell, nous pouvons voir que la recherche apparaît sur la nouvelle page :
Dans le cas où nous souhaitons traiter les informations de recherche, il existe deux méthodes qui nous seront très utiles, comme nous le voyons ci-dessous :
protected override void OnQueryChanged(string oldValue, string newValue)
{
var items = OriginalList;
if (string.IsNullOrWhiteSpace(newValue))
{
ItemsSource = items;
}
else
{
ItemsSource = items.Where(fruit => fruit.ToLower().Contains(newValue.ToLower())).ToList();
}
}
protected override void OnItemSelected(object item)
{
var fruit = item as string;
Shell.Current.GoToAsync($"fruitdetails?name={fruit}");
}
La première méthode appelée OnQueryChanged
sera exécuté dès qu’une modification sera apportée au terme de recherche. D’un autre côté, le OnItemSelected
La méthode nous permettra de récupérer la valeur sélectionnée et de la traiter selon nos besoins. Dans notre exemple, j’ai mis une navigation vers une page de détails, en envoyant le paramètre.
Conclusion de la série
Avec cela, nous avons atteint la fin du Maîtriser le shell .NET MAUI série. Vous savez maintenant ce qu’est .NET MAUI Shell et comment l’utiliser en combinaison avec Progress Telerik pour .NET MAUI contrôles à votre avantage pour créer des applications plus rapidement.
Si vous n’avez pas encore essayé Telerik UI pour .NET MAUI par vous-même, il est accompagné d’un essai gratuit !
Source link