Fermer

juin 5, 2024

Perfectionner les collections avec .NET MAUI CollectionView

Perfectionner les collections avec .NET MAUI CollectionView


Commencez avec .NET MAUI CollectionView, l’alternative la plus flexible et la plus performante à ListView qui permettra vos scénarios les plus courageux de présentation de listes de données.

Lorsque vous pensez aux éléments les plus courants que nous voyons dans les applications que nous utilisons dans notre vie de tous les jours, à quoi pensez-vous ? Si vous avez pensé aux listes, vous avez bien deviné et vous pouvez également vérifier vous-même vos capacités psychiques puisque vous venez de lire dans mes pensées ! Les possibilités de listes sur un écran sont aussi illimitées que celles du cerveau humain. Qu’il s’agisse des contacts, des discussions, des e-mails ou des listes de tâches sans fin de votre appareil. 😅

Et pour vous faire découvrir encore plus mon monde, dans cet article de blog, je vais partager l’une de mes tâches : vous présenter le composant le plus récent, le plus flexible et le plus performant de présentation de listes de données : le Vue de la collection .NET MAUI. 🌟

Plan

Comme bien d’autres fois, j’ai pensé à un exemple qui sera construit étape par étape dans le cadre de cet article de blog et dévoilera naturellement la plupart des fonctionnalités clés que propose le composant CollectionView de Progress. Interface utilisateur Telerik pour .NET MAUI des offres. Sur la base des listes couramment utilisées mentionnées au tout début, je suggère de recréer le début de la matinée de travail habituelle de la plupart des êtres humains.

Scénario : une tasse de café chaud et la liste de diffusion qui la complète.

Démarrer

J’ai promis que nous prendrions les choses lentement. En supposant que vous disposez déjà d’une nouvelle application .NET MAUI et de Telerik.UI.for.Maui NuGet, l’apport du composant CollectionView ne prendra qu’une seule ligne de code (XAML ou C#).

XAML :

<telerik:RadCollectionView x:Name="collectionView" />

C# :

RadCollectionView collectionView = new RadCollectionView();

Et comme ça, vous êtes déjà un collectionneur.

Boostation de la collecte

Si nous devions voir votre collection maintenant, nous verrions un espace vide. Pas de soucis, ça se change en un clin d’œil (presque). Mais pour de tels scénarios, il est bon de savoir que vous pouvez bénéficier des fonctionnalités de Collection View. Modèle de contenu vide pour afficher quelque chose de significatif pour l’utilisateur final. C’est aussi agréable de bénéficier d’un superbe existant exemple à partir de la documentation en ligne du composant.

Bien qu’une vie avec une boîte de réception vide puisse être agréable, ce n’est pas réaliste, alors jetons un coup d’œil à la future collecte de nos données de courrier électronique :

Vue de la collection

Liaison de données et virtualisation de l’interface utilisateur

Le composant CollectionView de Telerik UI pour .NET MAUI est spécialement conçu pour simplifier la liaison de données et (inutile de le dire) il prend en charge la virtualisation de l’interface utilisateur qui permet une visualisation plus rapide des données et un affichage sans effort des éléments lors du défilement, quelle que soit la taille de l’ensemble de données.

Tant de mots, si peu de code jusqu’à présent. Préparons le modèle de données de nos emails.

public class Email : ViewModelBase
{
    private string sender;
    private string subject;
    private DateTime received;
    private string message;
    private bool isUnread;

    public string Sender
    {
        get => this.sender;
        set => this.UpdateValue(ref this.sender, value);
    }

    public string Subject
    {
        get => this.subject;
        set => this.UpdateValue(ref this.subject, value);
    }

    public DateTime Received
    {
        get => this.received;
        set => this.UpdateValue(ref this.received, value);
    }

    public string Message
    {
        get => this.message;
        set => this.UpdateValue(ref this.message, value);
    }

    public bool IsUnread
    {
        get => this.isUnread;
        set => this.UpdateValue(ref this.isUnread, value);
    }
}

Et maintenant, créez un modèle de vue dans lequel nous allons verser quelques messages :

public class InboxViewModel : ExampleViewModel
{
    public InboxViewModel()
    {
        this.Messages = new ObservableCollection<Email>
        {
            new Email { Sender = "Internal Communications", Subject = "All Hands Confirmation", Received = DateTime.ParseExact("2024-05-15-11:47", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Thank you for registering for the All Hands meeting. If you can’t attend live, you may join the webinar by following the link in this email.", IsUnread = true },
            new Email { Sender = "Jane Poe", Subject = "Social Hour", Received = DateTime.ParseExact("2024-05-15-12:11", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Hi all, if you are in the office, please join us for a social hour after the All Hands in the cafeteria. Food and drinks will be provided.", IsUnread = true },
            new Email { Sender = "Fig Nelson", Subject = "Buttons Customization Example", Received = DateTime.ParseExact("2024-05-15-10:13", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Showcase the customization capabilities of all supported types of buttons present in the suite.", IsUnread = true },
            new Email { Sender = "Norman Gordon", Subject = "CollectionView Configuration Example", Received = DateTime.ParseExact("2024-05-02-15:26", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Please find attached the proposals for the configuration screens for the RadCollectionView." },
            new Email { Sender = "Hilary Ouse", Subject = "Chat (Conversational UI) scenarios and design", Received = DateTime.ParseExact("2023-09-25-09:15", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "We have Travel Assistance and Chat Room demos. Texts, images and all other assets are can be downloaded from Figma." },
            new Email { Sender = "Spruce Springclean", Subject = "CryptoTracker App", Received = DateTime.ParseExact("2024-05-15-07:02", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Hi, there are some design improvements which can be found in the XD artboard for the app. Please, review and implement them.", IsUnread = true },
            new Email { Sender = "Phillip Anthropy", Subject = ".NET MAUI AI Prompt Documentation", Received = DateTime.ParseExact("2024-05-15-17:51", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Could you please update me on the status of the .NET MAUI AI Prompt documentation?", IsUnread = true },
            new Email { Sender = "Azure DevOps", Subject = "PR build succeeded", Received = DateTime.ParseExact("2024-05-14-10:13", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "We sent you this notification due to a default subscription. BUILD #MAUI CI SUCCEEDED." },
            new Email { Sender = "Azure DevOps", Subject = "Product Backlog Item was assigned to you", Received = DateTime.ParseExact("2024-05-14-10:13", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Product Backlog Item—Change the CryptoTracker’s header so that it is consistent with iOS was assigned to you." },
            new Email { Sender = "workday-donotreply", Subject = "Request Time Off—Successfully Completed", Received = DateTime.ParseExact("2024-05-14-15:04", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Your time off request has been successfully completed. Click here to view the notification details." },
            new Email { Sender = "workday-donotreply", Subject = "Feedback Requested", Received = DateTime.ParseExact("2024-05-14-09:00", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "You have been requested to provide feedback. Your insights and perspective are greatly appreciated.", IsUnread = true },
            new Email { Sender = "Information Security Team", Subject = "You’ve completed your training", Received = DateTime.ParseExact("2024-05-14-08:46", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Thank you for completing your assigned training." },
            new Email { Sender = "IT HelpDesk", Subject = "Your password is about to expire", Received = DateTime.ParseExact("2024-05-14-10:56", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Your Domain password is expiring. If you don’t update it within the given period, you will receive a prompt to change it when the old one has expired.", IsUnread = true },
            new Email { Sender = "IT HelpDesk", Subject = "Planned System Upgrade", Received = DateTime.ParseExact("2024-05-14-11:02", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "We would like to inform you that IT will be performing a planned system upgrade of Azure DevOps Server. During the maintenance period, all Azure DevOps Server services will be unavailable." },
            new Email { Sender = "John Doe", Subject = "DevTools Monthly Series", Received = DateTime.ParseExact("2024-04-25-10:13", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Hi all, our next session for this month will be hosted next Wednesday. See you there! If you are not able to make it, do not worry—the session will be recorded." },
            new Email { Sender = "Shannon Riley", Subject = "Monthly Support KPI Report", Received = DateTime.ParseExact("2024-04-25-18:24", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Hi team, here is the monthly support KPI report. Click on the KPI name to view the detailed data regarding each KPI." },
            new Email { Sender = "Karren Koe", Subject = "[Design] MAUI—Control Samples—Calendar", Received = DateTime.ParseExact("2023-09-24-16:13", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Hello, I am sending over some proposals for Calendar First Look demo design. I’ll be more than happy to discuss them with you and make a decision." },
            new Email { Sender = "Karren Koe", Subject = "[Design] Buttons Desktop Hover States", Received = DateTime.ParseExact("2024-05-14-17:10", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Please find attached the updates regarding the newly added hover state for the new Buttons." },
            new Email { Sender = "Karren Koe", Subject = "ControlsSamples App", Received = DateTime.ParseExact("2024-04-25-17:37", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "Hi guys, I am happy to announce the new ControlsSamples App design! If you have any questions or concerns, please don’t hesitate to contact me." },
            new Email { Sender = "Figma", Subject = "4 new comments in MAUI Designs", Received = DateTime.ParseExact("2024-04-25-08:35", "yyyy-MM-dd-HH:mm", CultureInfo.InvariantCulture), Message = "There are 4 new comments from Karren. Click on the link to view the comments in the file." },
        };
    }

    public ObservableCollection<Email> Messages { get; set; }
}

Il reste une chose essentielle : remplir notre CollectionView avec ces messages en utilisant le ArticlesSource propriété:

<telerik:RadCollectionView x:Name="collectionView" ItemsSource="{Binding Messages}" />

Ce qui précède remplira sûrement la vue de la collection avec des éléments, mais ils n’obtiendront pas automatiquement l’apparence de la capture d’écran que j’ai fournie plus tôt. Suivez-moi à la section suivante où nous définirons un Modèle d’élément correspondant à l’aspect visuel souhaité.

Apparence de l’article

Nous voulons avoir une apparence plus élégante décrivant nos e-mails et c’est pourquoi nous le ferons définir un ItemTemplate personnalisé:

<telerik:RadCollectionView.ItemTemplate>
    <DataTemplate>
        <Grid RowDefinitions="Auto, Auto, Auto"
              ColumnDefinitions="20, *, Auto"
              Padding="8, 8, 12, 8">
            <Label IsVisible="{Binding IsUnread}"
                   FontFamily="TelerikFontExamples"
                   FontSize="16"
                   Text="&#xe80b;"
                   TextColor="#0285F0"
                   Margin="2, 0" />
            <telerik:RadHighlightLabel Grid.Column="1"
                                       FontSize="16"
                                       UnformattedText="{Binding Sender}"
                                       HighlightText="{Binding Source={x:Reference searchEntry}, Path=Text}"
                                       HighlightTextColor="#FF6600"
                                       TextColor="Black"
                                       FontAttributes="{Binding IsUnread, Converter={StaticResource IsEmailUnreadToFontAttributesConverter}}"
                                       HorizontalOptions="Start"
                                       VerticalOptions="Center" />
            <Label Grid.Column="2"
                   Text="{Binding Received, Converter={StaticResource FormattedDateTimeConverter}}"
                   TextColor="{Binding IsUnread, Converter={StaticResource IsEmailUnreadToPlaceholderTextColorConverter}}"
                   FontAttributes="{Binding IsUnread, Converter={StaticResource IsEmailUnreadToFontAttributesConverter}}"
                   FontSize="12"
                   HorizontalOptions="End"
                   VerticalOptions="Center" />
            <telerik:RadHighlightLabel Grid.Column="1"
                                       Grid.Row="1"
                                       Grid.ColumnSpan="2"
                                       FontSize="14"
                                       UnformattedText="{Binding Subject}"
                                       HighlightText="{Binding Source={x:Reference searchEntry}, Path=Text}"
                                       HighlightTextColor="#FF6600"
                                       TextColor="{Binding IsUnread, Converter={StaticResource IsEmailUnreadToTextColorConverter}}"
                                       FontAttributes="{Binding IsUnread, Converter={StaticResource IsEmailUnreadToFontAttributesConverter}}" />
            <telerik:RadHighlightLabel Grid.Column="1"
                                       Grid.Row="2"
                                       Grid.ColumnSpan="2"
                                       FontSize="14"
                                       MaxLines="1"
                                       LineBreakMode="TailTruncation"
                                       UnformattedText="{Binding Message}"
                                       HighlightText="{Binding Source={x:Reference searchEntry}, Path=Text}"
                                       HighlightTextColor="#FF6600"
                                       Opacity="0.6" />
        </Grid>
    </DataTemplate>
</telerik:RadCollectionView.ItemTemplate>

Je vous épargne les convertisseurs utilisés dans ce modèle, car ils sont simples et explicites et, surtout, pour ne pas vous inonder de code. Au lieu de cela, je peux vous montrer nos progrès :

Vue de collection – Modèle d'élément

Semble proche de l’original, mais il manque quelques éléments essentiels. Si vous examinez plus attentivement les données, vous remarquerez que les groupes sont manquants et, plus important encore, que l’ordre est perturbé. Et actuellement, je peux garantir que la sélection d’un e-mail non lu n’entraînera pas son marquage comme lu. 🤭

Regroupement et tri

J’aime les choses ordonnées, et quand on parle d’e-mails, on s’attend quelque peu à ce qu’ils soient triés par date du plus récent au plus ancien, bien sûr. Et si possible, ce serait bien qu’ils apparaissent regroupés pour que les emails d’aujourd’hui et d’hier soient séparés du reste. Comment fait-on cela? Laisse moi te montrer.

this.collectionView.GroupDescriptors.Add(new Telerik.Maui.Controls.Data.DelegateGroupDescriptor
{
    KeyLookup = new EmailDateKeyLookup(),
    SortOrder = Telerik.Maui.Controls.Data.SortOrder.Descending
});

J’ai choisi d’utiliser le Descripteur de groupe de délégués puisqu’il permet de regrouper les données par une clé personnalisée. Nous aurions pu simplement les regrouper par date—le Received propriété utilisant un PropertyGroupDescriptor… mais notre scénario est différent.

Le DelegateGroupDescriptor nécessite KeyLookupqui dans notre cas ressemble à ceci :

internal class EmailDateKeyLookup : Telerik.Maui.Controls.Data.IKeyLookup
{
    private static DateTime Today = DateTime.ParseExact("2024-05-15-00:00", "yyyy-MM-dd-HH:mm", System.Globalization.CultureInfo.InvariantCulture).Date;
    private static DateTime Yesterday = Today.AddDays(-1);
    private static DateTime Older = Yesterday.AddDays(-1);

    public object GetKey(object arg)
    {
        var email = arg as Email;
        var receivedDate = email.Received.Date;

        if (receivedDate == Today)
        {
            return Today;
        }

        if (receivedDate == Yesterday)
        {
            return Yesterday;
        }

        return Older;
    }
}

Maintenant, trions les e-mails par date de réception. Cette fois, nous allons utiliser un PropriétéTrierDescripteur.

<telerik:RadCollectionView.SortDescriptors>
    <telerik:PropertySortDescriptor PropertyName="Received" SortOrder="Descending" />
</telerik:RadCollectionView.SortDescriptors>

Et il y a encore une chose que j’ai faite pour que l’en-tête de la vue de groupe affiche les chaînes personnalisées que nous voulons : j’ai remplacé le GroupHeaderTemplate:

<telerik:RadCollectionView.GroupHeaderTemplate>
    <DataTemplate>
        <Label TextColor="#000000"
               Text="{Binding Key, Converter={StaticResource DateTimeToDateCategoryConverter}}"
               FontAttributes="Bold"
               FontSize="14"
               VerticalTextAlignment="Center" />
    </DataTemplate>
</telerik:RadCollectionView.GroupHeaderTemplate>

Vue de la collection : regroupée et triée

Ouf, c’est mieux ! Pourtant encore un peu incomplet car qu’est-ce qu’une liste d’emails sans moyen de les filtrer ? Nous devrions ajouter une recherche.

Filtration

Directement au travail : ajout d’une instance de RadEntry:

<Grid RowDefinitions="Auto, *"
      RowSpacing="20">
    <telerik:RadEntry x:Name="searchEntry"
                      Placeholder="Search emails..."
                      ReserveSpaceForErrorView="False" />
    <telerik:RadCollectionView x:Name="collectionView"
                               ItemsSource="{Binding Messages}" ... />
</Grid>

Après cela, en ajoutant un DelegateFilterDescriptor:

private Telerik.Maui.Controls.Data.DelegateFilterDescriptor emailFilterDescriptor;

Et en l’instanciant dans le constructeur du code-behind de notre exemple :

this.emailFilterDescriptor = new Telerik.Maui.Controls.Data.DelegateFilterDescriptor();

Nous devrons également préparer notre filtre personnalisé et je suggère que nous le fassions de manière à ce qu’il nous permette d’effectuer une recherche par expéditeur, sujet et, bien sûr, contenu d’un e-mail :

internal class SearchFilter : Telerik.Maui.Controls.Data.IFilter
{
    private string searchText;

    public SearchFilter(string text)
    {
        this.searchText = text;
    } 

    public bool PassesFilter(object item)
    {
        if (item is Email email 
            && (email.Sender.Contains(this.searchText, StringComparison.InvariantCultureIgnoreCase)
                || email.Sender.Contains(this.searchText, StringComparison.InvariantCultureIgnoreCase)
                || email.Message.Contains(this.searchText, StringComparison.InvariantCultureIgnoreCase)))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Nous sommes maintenant prêts à ajouter le filtre à notre CollectionView lorsque le texte de l’entrée de recherche change :

this.searchEntry.TextChanged += (s, e) =>
{
    this.emailFilterDescriptor.Filter = new SearchFilter(e.NewTextValue);

    if (!this.collectionView.FilterDescriptors.Contains(this.emailFilterDescriptor))
    {
        this.collectionView.FilterDescriptors.Add(this.emailFilterDescriptor);
    }
};

Que diriez-vous si on change de plateforme avant de vérifier le résultat ? Mesdames et messieurs, veuillez accueillir la liste WinUI filtrée sur notre scène :

Vue de collection : filtrage (WinUI)

Je l’aime! 💟

N’est-il pas temps de vérifier si l’on peut « lire » un email non lu ?

Sélection

Qu’est-ce qu’une collection sans la possibilité d’y piocher un article ? Un must absolu pour une liste. Le .NET MAUI CollectionView fournit trois modes de sélection vous permettant de manipuler le type de sélection, le tout contrôlé par le Mode de selection propriété. Notre exemple n’a besoin de rien de plus que sa valeur par défaut : Single. Pour d’autres scénarios, Aucun ou Multiple pourraient être plus adaptés. 🤷

Revenons maintenant aux e-mails non lus. Notre objectif est clair : marquer un e-mail non lu comme lu une fois que nous nous en « éloignons » et en sélectionnons un autre.

this.collectionView.SelectionChanged += (s, e) =>
{
    if (e.RemovedItems.Count() > 0 && e.RemovedItems.First() is Email email && email.IsUnread)
    {
        email.IsUnread = false;
    }
};

Vue Collection – Sélection (WinUI)

Capacités de personnalisation

D’après ce que vous avez vu et lu jusqu’à cette section, .NET MAUI CollectionView est extrêmement flexible et peut facilement être personnalisé pour répondre à vos besoins.

Concernant l’apparence visuelle, il y a plusieurs autres choses qui n’ont pas été mentionnées jusqu’à présent et qui concernent les éléments et les groupes.

Apparence de l’article personnalisé

Dans notre exemple, nous avons profité du ItemTemplate mais il y a aussi le ItemViewStyle, qui peut être utilisé pour affiner davantage les couleurs, les décalages, les états visuels de l’élément, etc. Que se passe-t-il si votre scénario nécessite d’appliquer différents styles à chaque élément en fonction d’une condition spécifique ? Pas de soucis-ItemViewStyleSélecteur sera utile.

Apparence de groupe personnalisée

Tout comme les éléments, nous avons utilisé un GroupHeaderTemplate personnalisé pour ajuster le contenu de la vue de groupe à notre guise. Pour personnaliser davantage cette vue de groupe, nous pourrions également utiliser le GroupViewStyle– il expose le DévelopperRéduireIndicatorStyle propriété qui permet de styliser l’étiquette en chevron par défaut utilisée pour développer/réduire un groupe.

Les scénarios nécessitant un style conditionnel des groupes de vues de collection sont également couverts : GroupViewStyleSelector est la voie à suivre pour cela.

Il est temps de dire la vérité et d’avouer que j’ai utilisé ExpandCollapseIndicatorStyle pour remplacer son remplissage afin qu’il soit le même pour toutes les plates-formes, juste pour le bien de notre exemple :

<telerik:RadCollectionView.GroupViewStyle>
    <Style TargetType="telerik:RadCollectionViewGroupView">
        <Setter Property="ExpandCollapseIndicatorStyle">
            <Style TargetType="Label">
                <Setter Property="Padding" Value="8, 0" />
            </Style>
        </Setter>
    </Style>
</telerik:RadCollectionView.GroupViewStyle>

Avons-nous raté quelque chose ? Oui, nous n’avons pas vérifié nos emails sur mobile !

Vue Collection – Mobile

Il y a beaucoup plus à explorer et encore plus à venir dans les prochaines versions, alors restez à l’écoute. Consultez la liste complète des fonctionnalités de .NET MAUI CollectionView dans son documentation en ligne.

Montrer la voie

Ce n’est pas une coïncidence si .NET MAUI CollectionView est l’une des étoiles les plus brillantes de la dernière version de Telerik UI pour .NET MAUI, mais il y en a beaucoup plus dans la galaxie Progress, alors assurez-vous d’en obtenir les éléments maintenant et d’explorer chez vous. propre rythme.

Si par hasard vous êtes un nouvel explorateur il vous suffit de vous inscrire à notre essai gratuit de 30 joursqui vous donne accès aux composants ainsi qu’à notre légendaire support technique sans frais.

Une fois que vous avez branché le .NET MAUI CollectionView dans votre application MAUI, n’oubliez pas de nous faire savoir quelle impression cela a fait. Assurez-vous de partager votre opinion et vos idées soit dans la section commentaires ci-dessous, soit en visitant l’interface utilisateur Telerik pour .NET MAUI. Portail de commentaires.

Un immense merci de ma part pour avoir lu cet article de blog et bonne exploration : vous savez que vous ouvrez la voie ! ✨




Source link