¿Cómo auto dimensionar y alinear a la derecha los datos de GridViewColumn en WPF?

89

Cómo puedo:

  • alinear a la derecha el texto en la columna ID
  • ¿Hacer que cada una de las columnas tenga un tamaño automático de acuerdo con la longitud del texto de la celda con los datos visibles más largos?

Aquí está el código:

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

respuesta parcial:

Gracias Kjetil, GridViewColumn.CellTemplate funciona bien y el ancho automático funciona, por supuesto, pero cuando la "Colección" de ObservativeCollection se actualiza con datos más largos que el ancho de columna, los tamaños de columna no se actualizan por sí mismos, por lo que es solo una solución para el visualización inicial de datos:

<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
fuente
1
¿Alguna vez encontró una solución a su problema de tamaño automático? Estoy pasando por la misma situación.
Oskar
2
@Oskar: la virtualización de la lista evita una solución automática. La lista solo conoce los elementos actualmente visibles y establece el tamaño en consecuencia. Si hay más elementos más abajo en la lista, no los conoce y, por lo tanto, no puede dar cuenta de ellos. El libro ProgrammingWPF - Sells-Griffith recomienda anchos de columna manuales si está utilizando el enlace de datos. :(
Gishu
@Gishu Gracias, eso en realidad tiene sentido ..
Oskar
Si el uso de MVVM y los valores de enlace están cambiando, consulte la respuesta de @Rolf Wessels.
Jake Berger

Respuestas:

104

Para que cada una de las columnas tenga un tamaño automático, puede establecer Width = "Auto" en GridViewColumn.

Para alinear a la derecha el texto en la columna de ID, puede crear una plantilla de celda usando un TextBlock y establecer TextAlignment. Luego configure ListViewItem.HorizontalContentAlignment (usando un estilo con un setter en ListViewItem) para hacer que la plantilla de celda llene todo el GridViewCell.

Tal vez haya una solución más simple, pero debería funcionar.

Nota: la solución requiere HorizontalContentAlignment = Stretch en Window.Resources y TextAlignment = Right en 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
fuente
@Kjetil: ¿Puedo aplicar esta configuración a una columna específica?
Gishu
15
+1 para: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein
impresionante, pero tengo 15 columnas, ¿hay alguna forma de que no tenga que repetir la plantilla de celda para todas?
Nitin Chaudhari
7
Tampoco funciona si olvida eliminar DisplayMemberBinding de GridViewColumn. La plantilla no tendrá ningún efecto entonces.
floele
@Mohamed ¿Por qué no?
It'sNotALie.
37

Si cambia el ancho del contenido, tendrá que usar este fragmento de código para actualizar cada columna:

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

    column.Width = double.NaN;
}

Tendría que dispararlo cada vez que se actualicen los datos de esa columna.

RandomEngy
fuente
1
¿A qué adjuntarías esto?
Armentage
1
Ejecútelo manualmente en GridViewColumn después de haber actualizado los datos de la cuadrícula. Si tiene un ViewModel, puede suscribirse al evento PropertyChanged en él y ejecutarlo.
RandomEngy
+1 ¡Gracias por eso! ¡Esto me ayudó mucho! No está relacionado con esta pregunta, pero de todos modos: implementé una List / GridView personalizada donde puede agregar / eliminar columnas dinámicamente en tiempo de ejecución a través de la GUI. Sin embargo, cuando eliminé y volví a agregar una columna, ya no apareció. Primero, pensé que no se agregó en absoluto (por alguna razón), pero luego (usando Snoop) descubrí que en realidad se agregó, pero tiene un Ancho real de 0 (fue de tamaño automático y obviamente se restableció cuando la columna se remoto). Ahora, uso su código para configurar la columna con el ancho correcto después de volver a agregarla a las Columnas. ¡Muchas gracias!
gehho
¡Una solución sencilla a mi problema!
Gqqnbig
+1 ¡Perfecto! Ojalá esto estuviera marcado como la respuesta. Agregué x: Name = "gvcMyColumnName" al XAML donde se definió la columna para poder acceder a ella en el código subyacente. Funciona como un campeón.
K0D4
19

Si su vista de lista también está redimensionando, puede usar un patrón de comportamiento para cambiar el tamaño de las columnas para que se ajusten al ancho completo de ListView. Casi lo mismo que cuando usas definiciones 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>

Consulte el siguiente enlace para ver algunos ejemplos y el enlace al código fuente http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

Rolf Wessels
fuente
Este es genial. Resuelve el problema y le brinda toda la funcionalidad , n , Auto que está buscando.
Designpattern
Esto es lo que estaba buscando. : D
Jake Berger
Nota: parece haber un error. Cuando se cambia el tamaño de ListView verticalmente, hasta el punto que hace que aparezca una barra de desplazamiento vertical, la columna aumentará continuamente de ancho hasta que la barra de desplazamiento desaparezca.
Jake Berger
1
Esta publicación puede proporcionar información sobre el comportamiento descrito en mi comentario anterior.
Jake Berger
Es genial, me refiero tanto al código como al sitio :). Creo que será útil cuando tenga requisitos más estrictos.
Gqqnbig
12

He creado la siguiente clase y la he usado en toda la aplicación donde sea necesario en lugar 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);
    }
}
usuario1333423
fuente
7

Como tenía un ItemContainerStyle, tuve que poner HorizontalContentAlignment en 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" /> 
    ....
paparazzo
fuente
6

Me gustó la solución de user1333423, excepto que siempre cambia el tamaño de cada columna; Necesitaba permitir que algunas columnas tuvieran un ancho fijo. Por lo tanto, en esta versión, las columnas con un ancho establecido en "Auto" se ajustarán automáticamente y las que tengan una cantidad fija no se ajustarán automáticamente.

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_
fuente
2

Sé que es demasiado tarde, pero este es mi enfoque:

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

La idea principal es vincular el ancho del elemento cellTemplete al ancho de ViewGridColumn. Width = 100 es el ancho predeterminado utilizado hasta el primer cambio de tamaño. No hay ningún código detrás. Todo está en xaml.

Azzy Elvul
fuente
Esto me inspiró a esta solución para llenar el ancho de una columna: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen
1

Tuve problemas con la respuesta aceptada (porque me perdí la parte HorizontalAlignment = Stretch y ajusté la respuesta original).

Esta es otra técnica. Utiliza una cuadrícula con SharedSizeGroup.

Nota: el Grid.IsSharedScope = true en el 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
fuente
Tiene un ancho de GridViewColumnas 40y establece el ancho de definición de columna en Auto? Eso no tiene sentido.
BK
1

Creé una función para actualizar los encabezados de columna de GridView para una lista y la llamo cada vez que se cambia el tamaño de la ventana o la vista de lista actualiza su diseño.

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

Este es tu codigo

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

Prueba esto

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