Comment dimensionner automatiquement et aligner à droite les données GridViewColumn dans WPF?

89

Comment puis-je:

  • aligner à droite le texte dans la colonne ID
  • faire de chacune des colonnes la taille automatique en fonction de la longueur du texte de la cellule avec les données visibles les plus longues?

Voici le code:

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="40"/>
            <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="100" />
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
        </GridView>
    </ListView.View>
</ListView>

réponse partielle:

Merci Kjetil, le GridViewColumn.CellTemplate fonctionne bien et la largeur automatique fonctionne bien sûr, mais lorsque la "Collection" ObservativeCollection est mise à jour avec des données plus longues que la largeur de la colonne, les tailles de colonne ne se mettent pas à jour, ce qui n'est qu'une solution pour le affichage initial des données:

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="ID" Width="Auto">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Id}" TextAlignment="Right" Width="40"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="Auto" />
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}" Width="Auto"/>
        </GridView>
    </ListView.View>
</ListView>
Edward Tanguay
la source
1
Avez-vous déjà trouvé une solution à votre problème de taille automatique? Je vis la même chose.
Oskar
2
@Oskar - la virtualisation de la liste empêche une solution automatique. La liste ne connaît que les éléments actuellement visibles et définit la taille en conséquence. S'il y a plus d'éléments plus bas dans la liste, il ne les connaît pas et ne peut donc pas en rendre compte. Le livre ProgrammingWPF - Sells-Griffith recommande des largeurs de colonne manuelles si vous utilisez la liaison de données. :(
Gishu
@Gishu Merci, cela a du sens ..
Oskar
Si l'utilisation de MVVM et les valeurs de liaison changent, veuillez consulter la réponse @Rolf Wessels.
Jake Berger

Réponses:

104

Pour que chacune des colonnes soit automatiquement dimensionnée, vous pouvez définir Width = "Auto" sur GridViewColumn.

Pour aligner à droite le texte dans la colonne ID, vous pouvez créer un modèle de cellule à l'aide d'un TextBlock et définir le TextAlignment. Définissez ensuite le ListViewItem.HorizontalContentAlignment (à l'aide d'un style avec un setter sur le ListViewItem) pour que le modèle de cellule remplisse l'intégralité de GridViewCell.

Il existe peut-être une solution plus simple, mais cela devrait fonctionner.

Remarque: la solution nécessite à la fois HorizontalContentAlignment = Stretch dans Window.Resources et TextAlignment = Right dans le CellTemplate.

<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
    <Style TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    </Style>
</Window.Resources>
<Grid>
    <ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="ID" Width="40">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Id}" TextAlignment="Right" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="Auto" />
                <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}" Width="Auto"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>
Kjetil Watnedal
la source
@Kjetil - Puis-je appliquer ce paramètre à une colonne spécifique?
Gishu
15
+1 pour: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein
génial, mais j'ai 15 colonnes, y a-t-il un moyen de ne pas avoir à répéter le modèle de cellule pour toutes?
Nitin Chaudhari
7
Cela ne fonctionne pas non plus si vous oubliez de supprimer le DisplayMemberBinding de GridViewColumn. Le modèle n'aura alors aucun effet.
floele
@Mohamed Pourquoi n'est-ce pas?
C'estNOTALie.
37

Si la largeur du contenu change, vous devrez utiliser ce bit de code pour mettre à jour chaque colonne:

private void ResizeGridViewColumn(GridViewColumn column)
{
    if (double.IsNaN(column.Width))
    {
        column.Width = column.ActualWidth;
    }

    column.Width = double.NaN;
}

Vous devrez le déclencher chaque fois que les données de cette colonne sont mises à jour.

RandomEngy
la source
1
À quoi attacheriez-vous cela?
Armentage
1
Exécutez-le manuellement sur GridViewColumn après avoir mis à jour les données de la grille. Si vous avez un ViewModel, vous pouvez vous abonner à l'événement PropertyChanged et l'exécuter ensuite.
RandomEngy
+1 Merci pour ça! Cela m'a beaucoup aidé! Pas lié à cette question, mais quand même: j'ai implémenté un List / GridView personnalisé où vous pouvez dynamiquement ajouter / supprimer des colonnes au moment de l'exécution via l'interface graphique. Cependant, lorsque j'ai supprimé et rajouté une colonne, elle n'apparaissait plus. Tout d'abord, je pensais qu'il n'avait pas été ajouté du tout (pour une raison quelconque), mais ensuite (en utilisant Snoop) j'ai découvert qu'il était en fait ajouté, mais qu'il avait une ActualWidth de 0 (il était auto-dimensionné et évidemment réinitialisé lorsque la colonne était supprimé). Maintenant, j'utilise votre code pour définir la colonne à la largeur correcte après l'avoir ré-ajoutée aux colonnes. Merci beaucoup!
gehho
Une solution simple à mon problème!
Gqqnbig
+1 Parfait! J'aimerais que cela soit marqué comme la réponse. J'ai ajouté x: Name = "gvcMyColumnName" au XAML où la colonne a été définie afin que je puisse y accéder dans le code derrière. Fonctionne comme un champion.
K0D4
19

Si votre liste est également redimensionnée, vous pouvez utiliser un modèle de comportement pour redimensionner les colonnes afin qu'elles correspondent à la largeur complète de ListView. Presque la même chose que vous en utilisant les définitions de grid.column

<ListView HorizontalAlignment="Stretch"
          Behaviours:GridViewColumnResize.Enabled="True">
        <ListViewItem></ListViewItem>
        <ListView.View>
            <GridView>
                <GridViewColumn  Header="Column *"
                                   Behaviours:GridViewColumnResize.Width="*" >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox HorizontalAlignment="Stretch" Text="Example1" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>

Voir le lien suivant pour quelques exemples et un lien vers le code source http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

Rolf Wessels
la source
Celui-ci est cool. Résout le problème et vous offre toutes les fonctionnalités n , Auto que vous recherchez.
Designpattern
C'est ce que je cherchais. : D
Jake Berger
Remarque: il semble y avoir un bug. Lorsque le ListView est redimensionné verticalement, au point qui provoque l'apparition d'une barre de défilement verticale, la colonne augmente continuellement en largeur jusqu'à ce que la barre de défilement disparaisse.
Jake Berger
1
Cet article peut donner un aperçu du comportement décrit dans mon commentaire précédent.
Jake Berger
C'est cool, je veux dire à la fois le code et le site :). Je pense que ce sera utile lorsque j'aurai des exigences plus strictes.
Gqqnbig
12

J'ai créé la classe suivante et utilisée dans toute l'application là où c'est nécessaire à la place de GridView:

/// <summary>
/// Represents a view mode that displays data items in columns for a System.Windows.Controls.ListView control with auto sized columns based on the column content     
/// </summary>
public class AutoSizedGridView : GridView
{        
    protected override void PrepareItem(ListViewItem item)
    {
        foreach (GridViewColumn column in Columns)
        {
            // Setting NaN for the column width automatically determines the required
            // width enough to hold the content completely.

            // If the width is NaN, first set it to ActualWidth temporarily.
            if (double.IsNaN(column.Width))
              column.Width = column.ActualWidth;

            // Finally, set the column with to NaN. This raises the property change
            // event and re computes the width.
            column.Width = double.NaN;              
        }            
        base.PrepareItem(item);
    }
}
user1333423
la source
7

Depuis que j'avais un ItemContainerStyle, je devais mettre le HorizontalContentAlignment dans le ItemContainerStyle

    <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=FieldDef.DispDetail, Mode=OneWay}" Value="False">
                         <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
                <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 
    ....
paparazzi
la source
6

J'ai aimé la solution de user1333423 sauf qu'elle redimensionnait toujours chaque colonne; J'avais besoin d'autoriser certaines colonnes à avoir une largeur fixe. Ainsi, dans cette version, les colonnes avec une largeur définie sur «Auto» seront automatiquement dimensionnées et celles définies sur un montant fixe ne seront pas dimensionnées automatiquement.

public class AutoSizedGridView : GridView
{
    HashSet<int> _autoWidthColumns;

    protected override void PrepareItem(ListViewItem item)
    {
        if (_autoWidthColumns == null)
        {
            _autoWidthColumns = new HashSet<int>();

            foreach (var column in Columns)
            {
                if(double.IsNaN(column.Width))
                    _autoWidthColumns.Add(column.GetHashCode());
            }                
        }

        foreach (GridViewColumn column in Columns)
        {
            if (_autoWidthColumns.Contains(column.GetHashCode()))
            {
                if (double.IsNaN(column.Width))
                    column.Width = column.ActualWidth;

                column.Width = double.NaN;                    
            }          
        }

        base.PrepareItem(item);
    }        
}
jv_
la source
2

Je sais que c'est trop tard mais voici mon approche:

<GridViewColumn x:Name="GridHeaderLocalSize"  Width="100">      
<GridViewColumn.Header>
    <GridViewColumnHeader HorizontalContentAlignment="Right">
        <Grid Width="Auto" HorizontalAlignment="Right">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="Local size" TextAlignment="Right" Padding="0,0,5,0"/>
        </Grid>
    </GridViewColumnHeader>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
    <DataTemplate>
        <TextBlock Width="{Binding ElementName=GridHeaderLocalSize, Path=Width, FallbackValue=100}"  HorizontalAlignment="Right" TextAlignment="Right" Padding="0,0,5,0" Text="Text" >
        </TextBlock>
    </DataTemplate>
</GridViewColumn.CellTemplate>

L'idée principale est de lier la largeur de l'élément cellTemplete à la largeur de ViewGridColumn. Width = 100 est la largeur par défaut utilisée jusqu'au premier redimensionnement. Il n'y a aucun code derrière. Tout est en xaml.

Azzy Elvul
la source
Cela m'a inspiré cette solution pour remplir la largeur d'une colonne: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen
1

J'ai eu des problèmes avec la réponse acceptée (car j'ai manqué la partie HorizontalAlignment = Stretch et j'ai ajusté la réponse d'origine).

Ceci est une autre technique. Il utilise une grille avec un SharedSizeGroup.

Remarque: le Grid.IsSharedScope = true sur le ListView.

<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
    <ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}" Grid.IsSharedSizeScope="True">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="ID" Width="40">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                             <Grid>
                                  <Grid.ColumnDefinitions>
                                       <ColumnDefinition Width="Auto" SharedSizeGroup="IdColumn"/>
                                  </Grid.ColumnDefinitions>
                                  <TextBlock HorizontalAlignment="Right" Text={Binding Path=Id}"/>
                             </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="Auto" />
                <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}" Width="Auto"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>
Adam Tegen
la source
Vous avez une largeur de GridViewColumnas 40et vous définissez la largeur de la définition de colonne sur Auto? Cela n'a pas de sens.
BK
1

J'ai créé une fonction pour mettre à jour les en-têtes de colonne GridView pour une liste et l'appeler chaque fois que la fenêtre est redimensionnée ou que la liste met à jour sa mise en page.

public void correctColumnWidths()
{
    double remainingSpace = myList.ActualWidth;

    if (remainingSpace > 0)
    {
         for (int i = 0; i < (myList.View as GridView).Columns.Count; i++)
              if (i != 2)
                   remainingSpace -= (myList.View as GridView).Columns[i].ActualWidth;

          //Leave 15 px free for scrollbar
          remainingSpace -= 15;

          (myList.View as GridView).Columns[2].Width = remainingSpace;
    }
}
Andrei Rogobete
la source
0

Ceci est votre code

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="40"/>
            <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}" Width="100" />
            <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
        </GridView>
    </ListView.View>
</ListView>

Essaye ça

<ListView Name="lstCustomers" ItemsSource="{Binding Path=Collection}">
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Id}" Width="Auto">
               <GridViewColumnHeader Content="ID" Width="Auto" />
            </GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding FirstName}" Width="Auto">
              <GridViewColumnHeader Content="First Name" Width="Auto" />
            </GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding LastName}" Width="Auto">
              <GridViewColumnHeader Content="Last Name" Width="Auto" />
            </GridViewColumn
        </GridView>
    </ListView.View>
</ListView>
StriderWaR
la source