Come ridimensionare automaticamente e allineare a destra i dati GridViewColumn in WPF?


89

Come posso:

  • allineare a destra il testo nella colonna ID
  • fare in modo che ciascuna delle colonne si ridimensiona automaticamente in base alla lunghezza del testo della cella con i dati visibili più lunghi?

Ecco il codice:

<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>

risposta parziale:

Grazie Kjetil, GridViewColumn.CellTemplate funziona bene e la larghezza automatica funziona ovviamente, ma quando la "Collection" di ObservativeCollection viene aggiornata con dati di larghezza maggiore della colonna, le dimensioni delle colonne non si aggiornano da sole, quindi è solo una soluzione per il visualizzazione iniziale dei dati:

<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>

1
Hai mai trovato una soluzione al tuo problema di dimensionamento automatico? Sto sperimentando la stessa cosa.
Oskar

2
@Oskar: la virtualizzazione dell'elenco impedisce una soluzione automatica. L'elenco conosce solo gli elementi attualmente visibili e imposta le dimensioni di conseguenza. Se ci sono più elementi più in basso nell'elenco, non li conosce e quindi non può renderne conto. Il libro ProgrammingWPF - Sells-Griffith consiglia larghezze manuali delle colonne se si utilizza l'associazione dei dati. :(
Gishu

@Gishu Grazie, in realtà ha senso ..
Oskar

Se l'utilizzo di MVVM e i valori di Binding stanno cambiando, vedere la risposta di @Rolf Wessels.
Jake Berger

Risposte:


104

Per fare in modo che ciascuna delle colonne si ridimensiona automaticamente, puoi impostare Width = "Auto" in GridViewColumn.

Per allineare a destra il testo nella colonna ID è possibile creare un modello di cella utilizzando un TextBlock e impostare TextAlignment. Quindi impostare ListViewItem.HorizontalContentAlignment (usando uno stile con un setter su ListViewItem) per fare in modo che il modello di cella riempia l'intero GridViewCell.

Forse esiste una soluzione più semplice, ma dovrebbe funzionare.

Nota: la soluzione richiede sia HorizontalContentAlignment = Stretch in Window.Resources che TextAlignment = Right in 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 - Posso applicare questa impostazione a una colonna specifica?
Gishu

15
+1 per: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein

fantastico, ma ho 15 colonne, c'è un modo in cui non devo ripetere il modello di cella per tutte?
Nitin Chaudhari,

7
Inoltre non funziona se si dimentica di rimuovere DisplayMemberBinding da GridViewColumn. Il modello non avrà alcun effetto allora.
floele

@ Mohamed Perché non è così?
ÈNotALie.

37

Se la larghezza del contenuto cambia, dovrai usare questo bit di codice per aggiornare ogni colonna:

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

    column.Width = double.NaN;
}

Dovresti attivarlo ogni volta che i dati per quella colonna vengono aggiornati.


1
A cosa lo collegheresti?
Armentage

1
Eseguilo manualmente in GridViewColumn dopo aver aggiornato i dati della griglia. Se hai un ViewModel puoi iscriverti all'evento PropertyChanged su di esso ed eseguirlo.
RandomEngy

+1 Grazie per questo! Questo mi ha aiutato molto! Non correlato a questa domanda, ma comunque: ho implementato un List / GridView personalizzato in cui è possibile aggiungere / rimuovere dinamicamente colonne in fase di esecuzione tramite la GUI. Tuttavia, quando ho rimosso e aggiunto di nuovo una colonna, non appariva più. Innanzitutto, pensavo che non fosse stato aggiunto (per qualche motivo), ma poi (usando Snoop) ho scoperto che è effettivamente aggiunto, ma ha una larghezza effettiva di 0 (è stato ridimensionato automaticamente e ovviamente ripristinato quando la colonna era rimosso). Ora, utilizzo il tuo codice per impostare la colonna sulla larghezza corretta dopo averla aggiunta di nuovo alle colonne. Grazie molto!
gehho

Una semplice soluzione al mio problema!
Gqqnbig

+1 Perfetto! Vorrei che questo fosse contrassegnato come risposta. Ho aggiunto x: Name = "gvcMyColumnName" all'XAML in cui è stata definita la colonna in modo da potervi accedere nel codice sottostante. Funziona come un campione.
K0D4

19

Se anche il listview viene ridimensionato, è possibile utilizzare un modello di comportamento per ridimensionare le colonne per adattarle alla larghezza completa di ListView. Quasi lo stesso che utilizzi per le definizioni 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>

Vedere il seguente collegamento per alcuni esempi e collegamento al codice sorgente http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto


Questo è fantastico. Risolve il problema e ti offre tutte le , n , funzionalità Auto che stai cercando.
Designpattern

QUESTO è quello che stavo cercando. : D
Jake Berger

Nota: sembra esserci un bug. Quando il ListView viene ridimensionato verticalmente, al punto che causa la visualizzazione di una barra di scorrimento verticale, la colonna aumenterà continuamente di larghezza finché la barra di scorrimento non scompare.
Jake Berger

1
Questo post può fornire informazioni sul comportamento descritto nel mio commento precedente.
Jake Berger

È bello, intendo sia il codice che il sito :). Credo che sarà utile quando avrò requisiti più severi.
Gqqnbig

12

Ho creato la seguente classe e l'ho utilizzata nell'applicazione laddove richiesto al posto di 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);
    }
}

7

Dato che avevo un ItemContainerStyle, dovevo mettere HorizontalContentAlignment in 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" /> 
    ....

6

Mi è piaciuta la soluzione di user1333423 tranne per il fatto che ha sempre ridimensionato ogni colonna; avevo bisogno di consentire ad alcune colonne di essere fissate in larghezza. Pertanto, in questa versione le colonne con una larghezza impostata su "Auto" verranno ridimensionate automaticamente e quelle impostate su un importo fisso non verranno ridimensionate automaticamente.

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);
    }        
}

2

So che è troppo tardi, ma ecco il mio approccio:

<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'idea principale è associare la larghezza dell'elemento cellTemplete alla larghezza di ViewGridColumn. Larghezza = 100 è la larghezza predefinita utilizzata fino al primo ridimensionamento. Non c'è alcun codice dietro. Tutto è in xaml.


Questo mi ha ispirato a questa soluzione per riempire la larghezza di una colonna: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen

1

Ho avuto problemi con la risposta accettata (perché ho perso la parte HorizontalAlignment = Stretch e ho modificato la risposta originale).

Questa è un'altra tecnica. Usa una griglia con SharedSizeGroup.

Nota: il Grid.IsSharedScope = true sul 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>

Hai una larghezza di GridViewColumncome 40e hai impostato la larghezza della definizione della colonna su Auto? Non ha senso.
BK

1

Ho creato una funzione per aggiornare le intestazioni di colonna GridView per un elenco e chiamarla ogni volta che la finestra viene ridimensionata o la visualizzazione a elenco aggiorna il layout.

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;
    }
}

0

Questo è il tuo codice

<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>

Prova questo

<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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.