Bagaimana cara mengubah ukuran otomatis dan menyelaraskan data GridViewColumn di WPF?

89

Bagaimana bisa saya:

  • rata kanan teks di kolom ID
  • membuat setiap kolom ukuran otomatis sesuai dengan panjang teks sel dengan data terlihat terpanjang?

Ini kodenya:

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

jawaban parsial:

Terima kasih Kjetil, GridViewColumn.CellTemplate berfungsi dengan baik dan Auto Width berfungsi dengan baik, tetapi ketika "Koleksi" ObservativeCollection diperbarui dengan data yang lebih panjang dari lebar kolom, ukuran kolom tidak diperbarui dengan sendirinya sehingga itu hanya solusi untuk tampilan awal data:

<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
sumber
1
Apakah Anda pernah menemukan solusi untuk masalah ukuran otomatis Anda? Saya mengalami hal yang sama.
Oskar
2
@Oskar - virtualisasi daftar mencegah solusi otomatis. Daftar ini hanya mengetahui item yang saat ini terlihat dan menyetel ukurannya sesuai dengan itu. Jika ada lebih banyak item di bawah daftar, ia tidak mengetahuinya dan karenanya tidak dapat menjelaskannya. Buku ProgrammingWPF - Sells-Griffith merekomendasikan lebar kolom manual jika Anda menggunakan penjilidan data. :(
Gishu
@Gishu Terima kasih, itu masuk akal ..
Oskar
Jika nilai MVVM dan Binding berubah, silakan lihat jawaban @Rolf Wessels.
Jake Berger

Jawaban:

104

Untuk membuat setiap kolom memiliki ukuran otomatis, Anda dapat mengatur Width = "Auto" pada GridViewColumn.

Untuk meratakan teks di kolom ID, Anda dapat membuat templat sel menggunakan TextBlock dan mengatur TextAlignment. Kemudian setel ListViewItem.HorizontalContentAlignment (menggunakan gaya dengan penyetel di ListViewItem) untuk membuat templat sel memenuhi seluruh GridViewCell.

Mungkin ada solusi yang lebih sederhana, tetapi ini seharusnya berhasil.

Catatan: solusinya membutuhkan HorizontalContentAlignment = Stretch in Window.Resources dan TextAlignment = Right di 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
sumber
@Kjetil - Dapatkah saya menerapkan pengaturan ini ke kolom tertentu?
Gishu
15
+1 untuk: <Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
Helge Klein
mengagumkan, tetapi saya memiliki 15 kolom, adakah cara agar saya tidak perlu mengulang template sel untuk semuanya?
Nitin Chaudhari
7
Ini juga tidak berfungsi jika Anda lupa untuk menghapus DisplayMemberBinding dari GridViewColumn. Template tidak akan berpengaruh apa pun.
floele
@Mohamed Mengapa bukan?
It'sNotALie.
37

Jika lebar konten berubah, Anda harus menggunakan sedikit kode ini untuk memperbarui setiap kolom:

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

    column.Width = double.NaN;
}

Anda harus mengaktifkannya setiap kali data untuk kolom itu diperbarui.

RandomEngy
sumber
1
Apa yang akan Anda lampirkan ini?
Armentage
1
Jalankan secara manual di GridViewColumn setelah Anda memperbarui data kisi. Jika Anda memiliki ViewModel, Anda dapat berlangganan acara PropertyChanged di atasnya dan menjalankannya kemudian.
RandomEngy
+1 Terima kasih untuk itu! Ini sangat membantuku! Tidak terkait dengan pertanyaan ini, tetapi bagaimanapun: Saya menerapkan Daftar / GridView yang disesuaikan di mana Anda dapat secara dinamis menambahkan / menghapus kolom saat runtime melalui GUI. Namun, ketika saya menghapus dan menambahkan kembali kolom, kolom itu tidak lagi muncul. Pertama, saya pikir itu tidak ditambahkan sama sekali (untuk beberapa alasan), tetapi kemudian (menggunakan Snoop) saya menemukan bahwa itu benar-benar ditambahkan, tetapi memiliki ActualWidth 0 (ukurannya otomatis dan jelas disetel ulang ketika kolom itu dihapus). Sekarang, saya menggunakan kode Anda untuk mengatur kolom ke lebar yang benar setelah saya menambahkannya kembali ke Kolom. Terimakasih banyak!
gehho
Solusi sederhana untuk masalah saya!
Gqqnbig
+1 Sempurna! Semoga ini ditandai sebagai jawabannya. Saya menambahkan x: Name = "gvcMyColumnName" ke XAML di mana kolom didefinisikan sehingga saya dapat mengaksesnya di kode di belakang. Bekerja seperti juara.
K0D4
19

Jika tampilan daftar Anda juga diubah ukurannya, Anda dapat menggunakan pola perilaku untuk mengubah ukuran kolom agar sesuai dengan lebar ListView penuh. Hampir sama seperti Anda menggunakan definisi 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>

Lihat tautan berikut untuk beberapa contoh dan tautan ke kode sumber http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

Rolf Wessels
sumber
Yang ini keren. Memecahkan masalah dan memberi Anda semua , n , fungsionalitas Otomatis yang Anda cari.
Designpattern
INILAH yang saya cari. : D
Jake Berger
Catatan: sepertinya ada bug. Saat ListView diubah ukurannya secara vertikal, ke titik yang menyebabkan scrollbar vertikal muncul, lebar kolom akan terus bertambah hingga scrollbar menghilang.
Jake Berger
1
Posting ini mungkin memberikan wawasan tentang perilaku yang dijelaskan dalam komentar saya sebelumnya.
Jake Berger
Keren, maksud saya kode dan situsnya :). Saya yakin ini akan berguna jika saya memiliki persyaratan yang lebih ketat.
Gqqnbig
12

Saya telah membuat kelas berikut dan digunakan di seluruh aplikasi di mana pun diperlukan sebagai pengganti 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);
    }
}
pengguna1333423
sumber
7

Karena saya memiliki ItemContainerStyle, saya harus meletakkan HorizontalContentAlignment di 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
sumber
6

Saya menyukai solusi user1333423 kecuali bahwa itu selalu mengubah ukuran setiap kolom; saya perlu mengizinkan beberapa kolom menjadi lebar tetap. Jadi dalam kolom versi ini dengan lebar yang disetel ke "Otomatis" akan berukuran otomatis dan kolom yang disetel ke jumlah tetap tidak akan berukuran otomatis.

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

Saya tahu ini sudah terlambat tetapi inilah pendekatan saya:

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

Ide utamanya adalah untuk mengikat lebar elemen cellTemplete dengan lebar ViewGridColumn. Lebar = 100 adalah lebar default yang digunakan sampai ukuran pertama diubah. Tidak ada kode apa pun di belakangnya. Semuanya ada di xaml.

Azzy Elvul
sumber
Ini menginspirasi saya untuk solusi ini untuk mengisi lebar satu kolom: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen
1

Saya mengalami masalah dengan jawaban yang diterima (karena saya melewatkan bagian HorizontalAlignment = Stretch dan telah menyesuaikan jawaban aslinya).

Ini adalah teknik lain. Ini menggunakan Grid dengan SharedSizeGroup.

Catatan: yang Grid.IsSharedScope = true pada 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
sumber
Anda memiliki lebar GridViewColumnsebagai 40dan Anda menetapkan lebar definisi kolom ke Auto? Itu tidak masuk akal.
BK
1

Saya membuat fungsi untuk memperbarui tajuk kolom GridView untuk daftar dan memanggilnya setiap kali jendela diubah ukurannya atau tampilan daftar memperbarui tata letaknya.

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

Ini adalah kode Anda

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

Coba ini

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