Como dimensionar automaticamente e alinhar à direita os dados GridViewColumn no WPF?


89

Como posso:

  • alinhe à direita o texto na coluna ID
  • tornar cada uma das colunas dimensionada automaticamente de acordo com o comprimento do texto da célula com os dados visíveis mais longos?

Aqui está o 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>

resposta parcial:

Obrigado Kjetil, o GridViewColumn.CellTemplate funciona bem e o Auto Width funciona, é claro, mas quando a "Coleção" ObservativeCollection é atualizada com dados maiores que a largura da coluna, os tamanhos das colunas não se atualizam, então é apenas uma solução para o exibição inicial de dados:

<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
Você já encontrou uma solução para o seu problema de dimensionamento automático? Estou passando pelo mesmo.
— Oskar de

2
@Oskar - a virtualização da lista impede uma solução automática. A lista conhece apenas os itens atualmente visíveis e define o tamanho de acordo. Se houver mais itens mais abaixo na lista, ele não os conhece e, portanto, não pode contabilizá-los. O livro ProgrammingWPF - Sells-Griffith recomenda larguras de coluna manuais se você estiver usando vinculação de dados. :(
— Gishu

@Gishu Obrigado, isso realmente faz sentido ..
— Oskar

Se os valores de uso do MVVM e Binding estiverem mudando, consulte a resposta de @Rolf Wessels.
— Jake Berger,

Respostas:


104

Para fazer cada uma das colunas dimensionar automaticamente, você pode definir Width = "Auto" no GridViewColumn.

Para alinhar à direita o texto na coluna ID, você pode criar um modelo de célula usando um TextBlock e definir o TextAlignment. Em seguida, defina ListViewItem.HorizontalContentAlignment (usando um estilo com um setter no ListViewItem) para fazer com que o modelo de célula preencha todo o GridViewCell.

Talvez haja uma solução mais simples, mas deve funcionar.

Nota: a solução requer HorizontalContentAlignment = Stretch in Window.Resources e TextAlignment = Right no 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 aplicar essa configuração a uma coluna específica?
— Gishu

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

incrível, mas eu tenho 15 colunas, há alguma maneira de não precisar repetir o modelo de célula para todas elas?
— Nitin Chaudhari

7
Também não funciona se você esquecer de remover DisplayMemberBinding do GridViewColumn. O modelo não terá nenhum efeito então.
— floele

@Mohamed Por que não é?
— It'sNotALie.

37

Se a largura do conteúdo mudar, você terá que usar este trecho de código para atualizar cada coluna:

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

    column.Width = double.NaN;
}

Você teria que disparar cada vez que os dados dessa coluna fossem atualizados.


1
A que você atribuiria isso?
— Armentage

1
Execute-o manualmente no GridViewColumn depois de atualizar os dados da grade. Se você tiver um ViewModel, poderá se inscrever no evento PropertyChanged e executá-lo em seguida.
— RandomEngy

+1 Obrigado por isso! Isso me ajudou muito! Não relacionado a esta questão, mas de qualquer maneira: Eu implementei um List / GridView personalizado onde você pode adicionar / remover colunas dinamicamente em tempo de execução por meio da GUI. No entanto, quando removi e adicionei novamente uma coluna, ela não apareceu mais. Primeiro, pensei que não havia sido adicionado (por algum motivo), mas então (usando o Snoop) descobri que ele foi realmente adicionado, mas tem uma largura real de 0 (foi dimensionado automaticamente e obviamente redefinido quando a coluna foi removido). Agora, eu uso seu código para definir a coluna com a largura correta depois de adicioná-la novamente às colunas. Muito Obrigado!
— gehho

Uma solução simples para o meu problema!
— Gqqnbig

+1 perfeito! Gostaria que isso fosse marcado como a resposta. Eu adicionei x: Name = "gvcMyColumnName" ao XAML onde a coluna foi definida para que eu pudesse acessá-la no código por trás. Trabalha como um campeão.
— K0D4

19

Se seu listview também estiver redimensionando, você pode usar um padrão de comportamento para redimensionar as colunas para caber na largura total de ListView. Quase o mesmo que você usa as definições 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>

Veja o seguinte link para alguns exemplos e link para o código-fonte http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto


Este é legal. Resolve o problema e oferece todas as funcionalidades do , n , Auto que você está procurando.
— Designpattern

ISSO é o que eu estava procurando. : D
— Jake Berger,

Nota: parece haver um bug. Quando o ListView é redimensionado verticalmente, a ponto de fazer com que uma barra de rolagem vertical apareça, a coluna aumentará continuamente em largura até que a barra de rolagem desapareça.
— Jake Berger,

1
Esta postagem pode fornecer uma visão sobre o comportamento descrito em meu comentário anterior.
— Jake Berger,

É legal, quero dizer tanto o código quanto o site :). Acredito que será útil quando tiver requisitos mais rígidos.
— Gqqnbig

12

Eu criei a seguinte classe e usei em todo o aplicativo sempre que necessário no 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);
    }
}

7

Como eu tinha um ItemContainerStyle, tive que colocar o HorizontalContentAlignment no 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

Gostei da solução do usuário 1333423, exceto que sempre redimensionava cada coluna; Eu precisava permitir que algumas colunas tivessem largura fixa. Portanto, nesta versão, as colunas com uma largura definida como "Auto" serão dimensionadas automaticamente e aquelas definidas com um valor fixo não serão dimensionadas 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

Eu sei que é tarde demais, mas aqui está minha abordagem:

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

A ideia principal é vincular a largura do elemento cellTemplete à largura de ViewGridColumn. Largura = 100 é a largura padrão usada até o primeiro redimensionamento. Não há nenhum código por trás. Tudo está em xaml.


Isso me inspirou a esta solução para preencher a largura de uma coluna: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
— J. Andersen

1

Tive problemas com a resposta aceita (porque perdi a parte HorizontalAlignment = Stretch e ajustei a resposta original).

Esta é outra técnica. Ele usa um Grid com um SharedSizeGroup.

Nota: o Grid.IsSharedScope = true no 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>

Você tem a largura de GridViewColumnas 40e configurou a largura da definição da coluna para Auto? Isso não faz sentido.
— BK

1

Eu criei uma função para atualizar cabeçalhos de coluna GridView para uma lista e chamá-la sempre que a janela for redimensionada ou o listview atualizar seu 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

Este é o seu 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>

Tente isto

<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>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.