web-dev-qa-db-fra.com

WPF ListView avec disposition horizontale des éléments?

Je veux disposer les éléments dans un ListView d'une manière similaire à WinForms ListView en mode liste. Autrement dit, lorsque les éléments sont disposés non seulement verticalement mais horizontalement dans ListView également.

Cela ne me dérange pas si les articles sont disposés comme ceci:

1 4 7
2 5 8
3 6 9

Ou comme ça:

1 2 3
4 5 6
7 8 9

Tant qu'ils sont présentés à la fois verticalement et horizontalement afin de maximiser l'utilisation de l'espace disponible.

Le plus proche que j'ai pu trouver était cette question:

Comment faire pour que les éléments WPF ListView se répètent horizontalement, comme une barre de défilement horizontale?

Ce qui ne présente les éléments que horizontalement.

55
Grokys

Cela ressemble à ce que vous recherchez est un WrapPannel , qui disposera les éléments horizontalement jusqu'à ce qu'il n'y ait plus de place, puis passera à la ligne suivante, comme ceci:

( MSDN )
texte alternatif http://i.msdn.Microsoft.com/Cc295081.b1c415fb-9a32-4a18-aa0b-308fca994ac9(en-us,Expression.10).png

Vous pouvez également utiliser un niformGrid , qui disposera les éléments dans un nombre défini de lignes ou de colonnes.

La façon dont nous obtenons les éléments à classer à l'aide de ces autres panneaux dans un ListView, ListBox ou toute autre forme de ItemsControl est en modifiant la propriété ItemsPanel . En définissant le ItemsPanel, vous pouvez le modifier à partir du StackPanel par défaut utilisé par ItemsControls. Avec le WrapPanel, nous devrions également définir les largeurs comme illustré ici .

<ListView>
   <ListView.ItemsPanel>
      <ItemsPanelTemplate>
         <WrapPanel Width="{Binding (FrameworkElement.ActualWidth), 
            RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
            ItemWidth="{Binding (ListView.View).ItemWidth, 
            RelativeSource={RelativeSource AncestorType=ListView}}"
            MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
            ItemHeight="{Binding (ListView.View).ItemHeight, 
            RelativeSource={RelativeSource AncestorType=ListView}}" />
      </ItemsPanelTemplate>
   </ListView.ItemsPanel>
...
</ListView>
100
rmoore

J'ai récemment recherché comment y parvenir dans WPF et trouvé une bonne solution. Ce que je voulais, c'était répliquer le mode Liste dans l'Explorateur Windows, c'est-à-dire de haut en bas, puis de gauche à droite.

Fondamentalement, ce que vous voulez faire remplace le ListBox.ItemsPanel propriété pour utiliser un WrapPanel avec son orientation définie sur Vertical.

<ListBox>
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>      
      <WrapPanel Orientation="Vertical"/>
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</ListBox>

Cependant, cela [~ # ~] sera [~ # ~] lent lors du chargement d'un grand ensemble de données car le panneau d'habillage n'est pas virtualisé. C'est important. Donc, cette tâche devient maintenant un peu plus, car vous devez maintenant écrire votre propre VirtualizedWrapPanel en étendant VirtualizedPanel et en implémentant IScrollInfo.

public class VirtualizedWrapPanel : VirtualizedPanel, IScrollInfo
{
   // ...
}

C'est tout ce que j'ai obtenu dans mes recherches avant de devoir passer à une autre tâche. Si vous souhaitez plus d'informations ou d'exemples, veuillez commenter.

[~ # ~] mise à jour [~ # ~] . Ben Constable a un grand série sur la façon d'implémenter IScrollInfo .

Il y a 4 articles au total. Une très bonne lecture.

Depuis, j'ai mis en place un panneau d'enveloppe virtualisé, ce n'est pas une tâche facile, même avec l'aide de la série d'articles ci-dessus.

21
Dennis

Dans mon cas, la meilleure option était d'utiliser:

        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical"
                    MaxHeight="{Binding (FrameworkElement.ActualHeight), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
                               ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
                               MinHeight="{Binding ItemHeight, RelativeSource={RelativeSource Self}}"
                               ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>

Cela m'a donné une analogie décente avec l'option Liste de l'Explorateur Windows

8
Arsen Zahray

En plus de la réponse de @ Dennis, à propos de la perte de virtualisation de WrapPanel, j'ai trouvé une classe Nice qui implémente correctement cela. Alors que le message suggéré par Ben Constable ( Partie 1 , Partie 2 , Partie , Partie 4 ) est un Belle introduction, je n'ai pas pu tout à fait terminer la tâche pour un panneau Wrap.

Voici une implémentation: https://virtualwrappanel.codeplex.com/ Je l'ai testée avec un total de 3.300 vidéos et photos, le chargement de la liste elle-même est bien sûr un peu long, mais finalement c'est le cas virtualiser correctement la liste, aucun décalage de défilement que ce soit.

  • Il y a quelques problèmes avec ce code, voir l'onglet problèmes sur la page ci-dessus.

Après avoir ajouté le code source à votre projet, exemple de code source:

   <!--in your <Window> or <UserControl> tag -->
  <UserControl
        xmlns:hw="clr-namespace:Project.Namespace.ToClassFile" >
   <!--...-->

    <ListView x:Name="lvImages" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Margin="10" Height="auto" 
             ItemsSource="{Binding ListImages}"
              ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <hw:VirtualizingWrapPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Vertical" Margin="5" MaxHeight="150">
                    <TextBlock Text="{Binding title}" FontWeight="Bold"/>
                    <Image Source="{Binding path, IsAsync=True}" Height="100"/>
                    <TextBlock Text="{Binding createDate, StringFormat=dd-MM-yyyy}"/>

                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

Back-end de style MVVM, donc c'est à l'intérieur du ViewModel:

    public ObservableCollection<Media> ListImages
    {
        get
        {
            return listImages;
        }
        set { listImages = value; OnPropertyChanged(); }
    }


     //Just load the images however you do it, then assign it to above list.
//Below is the class defined that I have used.
public class Media
{
    private static int nextMediaId = 1;
    public int mediaId { get; }
    public string title { get; set; }
    public string path { get; set; }
    public DateTime createDate { get; set; }
    public bool isSelected { get; set; }

    public Media()
    {
        mediaId = nextMediaId;
        nextMediaId++;
    }
}
2
CularBytes

de gauche à droite puis de haut en bas

      <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal" 
                     MaxWidth="{Binding ActualWidth, Mode=OneWay, 
                       RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type er:MainWindow}}}"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
2
kns98