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>
Edward Tanguay
fonte
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 Watnedal
fonte
@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.

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

Rolf Wessels
fonte
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);
    }
}
user1333423
fonte
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" /> 
    ....
paparazzo
fonte
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);
    }        
}
jv_
fonte
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.

Azzy Elvul
fonte
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>
Adam Tegen
fonte
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;
    }
}
Andrei Rogobete
fonte
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>
StriderWaR
fonte