Question

J'ai une classe appelée Book;

class Book
{
    public string Name { get; set; }
    public string Author { get; set; }
    public int PagesCount { get; set; }
    public int Category { get; set; }
}

Le contrôle ListBox affiche une liste de livres et le modèle ItemTemplate a été modifié afin de représenter visuellement le livre. Le texte indique le nom du livre, son auteur et le nombre de pages. La catégorie, cependant, est représentée par une certaine couleur (par exemple, l'historique est bleu, la romance est rouge, etc.) Le texte comporte maintenant un effet OuterGlowBitmap et un convertisseur de valeur de la catégorie (int) vers la couleur appropriée. Tout est lié dans DataTemplate pour ListBoxItem. Techniquement, tout fonctionne bien.

Le problème, cependant, est la performance. Il semble que l’effet bitmap outerGlow pèse lourdement sur le processeur. Ainsi, lorsque j’ai une liste d’environ 500 livres, il faut environ 500 ms pour récupérer les données de la base de données, mais environ 10 secondes pour charger les éléments dans le ListBox. Et même lorsque le chargement est terminé, le défilement est très lent. J'ai essayé de définir le VirtualizingStackPanel.IsVirtualizing sur True, mais en vain. (Le nombre maximum de livres pouvant être dans la base de données à un moment donné est d'environ 30000.)

Cependant, même lorsque la liste contient plus de 100 éléments, l’esprit humain ne peut pas traiter aussi rapidement. Par conséquent, je ne cherche pas à charger et à répertorier tous les livres recherchés par l’utilisateur. C'est pourquoi j'ai créé une classe de navigation wrapper, BookNavigator, qui lie la liste de sélection à son objet ObservableCollection. Tous les livres sont chargés dans ce BookNavigator, mais seuls X d'entre eux sont affichés dans la liste (en les ajoutant à la collection observable).

Le problème, c’est que je veux que le nombre de livres affichés soit suffisamment petit pour que listbox ne puisse pas afficher la barre de défilement, de sorte que je puisse implémenter mes propres méthodes de défilement (Premier, Précédent, Suivant, Dernier ou simplement le mien). barre de défilement, ça n'a pas d'importance).

Comment puis-je calculer le nombre d'éléments à afficher pour que la barre de défilement ne soit pas affichée?

Deux problèmes apparaissent: - Le redimensionnement de l'application peut modifier la taille de la zone de liste - Tous les éléments de la zone de liste ne sont pas de la même hauteur (en fonction du nombre d'auteurs).

Y a-t-il un moyen de réaliser ce que j'essaie de faire?

EDIT (en réponse à Martin Harris)

Le problème avec le code suggéré par Martin Harris est que la boucle foreach utilise FrameworkElement, mais que la zone de liste est remplie d’objets de type Book qui n’hérite pas de FrameworkElement et ne dispose d’aucun autre moyen de calculer sa hauteur. L'élément racine de ListBoxItem est une grille, alors il serait peut-être possible de récupérer cette grille, mais je ne sais pas comment faire cela?

Existe-t-il un moyen quelconque d'obtenir les éléments d'interface utilisateur créés pour représenter l'élément de liste déroulante?

MODIFIER

J'ai trouvé ce Q / A qui semble être ce dont j'ai besoin. ItemContainerGenerator

Était-ce utile?

La solution 4

La solution se trouve implicitement ici: Solution

Autres conseils

Après avoir essayé de trouver quelque chose de similaire, j'ai pensé partager mon résultat ici (car cela semble plus facile que les autres réponses):

Le test de visibilité simple que j'ai obtenu de ici .

private static bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible)
        return false;

    Rect bounds =
        element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

Ensuite, vous pouvez parcourir les éléments de la liste et utiliser ce test pour déterminer ceux qui sont visibles. Le nombre de cette liste vous donnera le nombre d’éléments visibles dans la liste.

private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility)
{
    var items = new List<object>();

    foreach (var item in PhotosListBox.Items)
    {
        if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility))
        {
            items.Add(item);
        }
        else if (items.Any())
        {
            break;
        }
    }

    return items;
}

Ceci contiendrait alors la liste des éléments actuellement affichés dans la liste (y compris ceux cachés par le défilement ou quelque chose de similaire).

Voulez-vous probablement savoir la taille de tous les livres avant de les ajouter à la liste (éventuellement en analysant et en renseignant le modèle XAML dans le code derrière, puis en demandant au contrôle externe de déterminer sa taille) ce serait beaucoup de travail pour pas beaucoup de gain. Ne pouvez-vous pas simplement sélectionner un nombre de livres suffisant pour remplir la liste, mais pas autant pour ralentir le rendu et désactiver la barre de défilement? Cela pourrait quand même être lié à la taille de la zone de liste, de sorte que davantage d’éléments ont été ajoutés au fur et à mesure de sa croissance, mais le coût en performances lié à l’ajout de quelques éléments supplémentaires devrait être inférieur au coût de calcul de toutes les tailles.

Exemple rapide: disons qu'un livre avec un auteur est de 150 pixels. Vous pourriez prendre la taille de la liste et la diviser par 125 pour obtenir une estimation approximative du nombre d’articles qui seraient dans l’ordre, sans que leur calcul ne soit coûteux. Ce que vous voulez éviter, c’est trop peu d’éléments, ce qui laisserait un espace vide.

Modifié à la lumière de votre commentaire.

Dans ce cas, vous pouvez ajouter les éléments à la liste (en utilisant la méthode ci-dessus pour obtenir une estimation précise), puis calculer quel est le dernier élément complètement visible, puis supprimer les éléments supplémentaires.

Cette méthode d'extension obtiendra le dernier élément complètement affiché dans une zone de liste ou null si aucun élément n'est visible:

public static class ListBoxExtensions
{
    public static FrameworkElement GetLastItem(this ListBox listBox)
    {
        double height = listBox.ActualHeight;

        double currentHeight = 0;
        FrameworkElement previous = null;

        foreach (FrameworkElement item in listBox.Items)
        {
            currentHeight += item.ActualHeight;
            if (currentHeight > height)
            {
                return previous;
            }

            previous = item;
        }

        return previous;
    }
}

Si la liste est agrandie, vous pouvez ajouter suffisamment d'éléments à la collection pour combler le vide et relancer le processus.

"Comment puis-je calculer le nombre d'éléments à afficher pour que la barre de défilement ne soit pas affichée? "

Réponse courte: (listBox1.Height / listBox1.ItemHeight)

Ceci vous donne le nombre de lignes affichées / disponibles, de sorte que vous ne pouvez lire que ce nombre de lignes et que vous remplissiez toute la liste.

Maintenant, la démo:

        int listbox_total_visible_lines = 2;
        listBox1.Height = (listbox_total_visible_lines + 1) * listBox1.ItemHeight;
        listBox1.Items.Add("um");
        listBox1.Items.Add("dois");
        listBox1.Items.Add("tres");
        listBox1.Items.Add("quatro");
        listBox1.Items.Add("cinco");
        listBox1.SelectedIndex = listBox1.Items.Count - 1;
        this.Text = (listBox1.Height/ listBox1.ItemHeight).ToString();

Cet exemple vous permet de choisir le nombre d'éléments qui seront visibles. Il s'agit donc du nombre de lignes actuellement disponibles à afficher. Par conséquent, vous n’ajoutez que les " listbox_total_visible_items " les éléments et la zone de liste seront pleins et ne montreront pas les barres de défilement.

Code expliquer:

  1. listbox_total_visible_items contient le nombre de lignes à afficher

  2. zone de liste de configuration avec la taille appropriée, 2 lignes uniquement

3-7. ajouter des lignes

  1. aller à la dernière ligne, juste pour le plaisir

  2. affiche, dans la barre de texte du formulaire, le nombre de lignes de la zone de liste, en fonction de la hauteur de celle-ci divisée par la taille de chaque élément.

C'est tout.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top