Làm cách nào để tự động định kích thước và căn phải dữ liệu GridViewColumn trong WPF?


89

Làm thế nào tôi có thể:

  • căn phải văn bản trong cột ID
  • làm cho mỗi cột tự động kích thước theo độ dài văn bản của ô có dữ liệu hiển thị dài nhất?

Đây là mã:

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

câu trả lời một phần:

Cảm ơn Kjetil, GridViewColumn.CellTemplate hoạt động tốt và Chiều rộng tự động hoạt động tất nhiên nhưng khi "Bộ sưu tập" ObservativeCollection được cập nhật với dữ liệu dài hơn chiều rộng cột, kích thước cột không tự cập nhật nên đó chỉ là giải pháp cho hiển thị ban đầu của dữ liệu:

<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
Bạn đã bao giờ tìm ra giải pháp cho vấn đề kích thước tự động của mình chưa? tôi đang trải nghiệm những điều tương tự.
Oskar

2
@Oskar - ảo hóa danh sách ngăn cản một giải pháp tự động. Danh sách chỉ biết các mục hiện đang hiển thị và đặt kích thước cho phù hợp. Nếu có nhiều mục hơn nữa trong danh sách, nó không biết về chúng và do đó không thể giải thích chúng. Cuốn sách ProgrammingWPF - Sells-Griffith đề xuất độ rộng cột thủ công nếu bạn đang sử dụng liên kết dữ liệu. :(
Gishu

@Gishu Cảm ơn, điều đó thực sự có ý nghĩa ..
Oskar

Nếu việc sử dụng MVVM và các giá trị Binding đang thay đổi, vui lòng xem câu trả lời của @Rolf Wessels.
Jake Berger

Câu trả lời:


104

Để tự động thay đổi kích thước từng cột, bạn có thể đặt Width = "Auto" trên GridViewColumn.

Để căn phải văn bản trong cột ID, bạn có thể tạo một mẫu ô bằng TextBlock và đặt TextAlignment. Sau đó, thiết lập ListViewItem.HorizontalContentAlignment (sử dụng một kiểu với một setter trên ListViewItem) để làm cho mẫu ô lấp đầy toàn bộ GridViewCell.

Có thể có một giải pháp đơn giản hơn, nhưng điều này sẽ hiệu quả.

Lưu ý: giải pháp yêu cầu cả HorizontalContentAlignment = Stretch trong Window.Resources và TextAlignment = Right trong 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 - Tôi có thể áp dụng cài đặt này cho một cột cụ thể không?
Gishu

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

tuyệt vời, nhưng tôi có 15 cột, có cách nào tôi không phải lặp lại celltemplate cho tất cả chúng không?
Nitin Chaudhari

7
Nó cũng không hoạt động nếu bạn quên xóa DisplayMemberBinding khỏi GridViewColumn. Sau đó, mẫu sẽ không có bất kỳ hiệu ứng nào.
floele

@Mohamed Tại sao không?
Không phải đâu.

37

Nếu chiều rộng của nội dung thay đổi, bạn sẽ phải sử dụng đoạn mã này để cập nhật từng cột:

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

    column.Width = double.NaN;
}

Bạn sẽ phải kích hoạt nó mỗi khi dữ liệu cho cột đó cập nhật.


1
Bạn sẽ đính kèm cái này với cái gì?
Armentage

1
Chạy nó theo cách thủ công trên GridViewColumn sau khi bạn đã cập nhật dữ liệu lưới. Nếu bạn có ViewModel, bạn có thể đăng ký sự kiện PropertyChanged trên đó và chạy nó sau đó.
RandomEngy

+1 Cảm ơn vì điều đó! Điều này đã giúp tôi rất nhiều! Không liên quan đến câu hỏi này, nhưng dù sao: Tôi đã triển khai Danh sách / GridView tùy chỉnh, nơi bạn có thể thêm / xóa động các cột trong thời gian chạy thông qua GUI. Tuy nhiên, khi tôi xóa và thêm lại một cột, nó không còn xuất hiện nữa. Đầu tiên, tôi nghĩ rằng nó hoàn toàn không được thêm vào (vì lý do nào đó), nhưng sau đó (bằng cách sử dụng Snoop), tôi phát hiện ra rằng nó thực sự được thêm vào, nhưng có Chiều rộng thực là 0 (nó được đặt kích thước tự động và rõ ràng được đặt lại khi cột đã loại bỏ). Bây giờ, tôi sử dụng mã của bạn để đặt cột thành chiều rộng chính xác sau khi tôi thêm lại nó vào Cột. Cảm ơn nhiều!
gehho

Một giải pháp đơn giản cho vấn đề của tôi!
Gqqnbig

+1 Hoàn hảo! Ước gì điều này được đánh dấu là câu trả lời. Tôi đã thêm x: Name = "gvcMyColumnName" vào XAML nơi cột được xác định để tôi có thể truy cập nó trong mã phía sau. Hoạt động như một nhà vô địch.
K0D4

19

Nếu chế độ xem danh sách của bạn cũng đang định lại kích thước thì bạn có thể sử dụng mẫu hành vi để định lại kích thước các cột cho vừa với chiều rộng ListView đầy đủ. Gần giống như bạn sử dụng định nghĩa 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>

Xem liên kết sau để biết một số ví dụ và liên kết đến mã nguồn http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto


Cái này thật tuyệt. Giải quyết vấn đề và cung cấp cho bạn tất cả , n , chức năng Tự động mà bạn đang tìm kiếm.
Designpattern

ĐÂY là những gì tôi đang tìm kiếm. : D
Jake Berger

Lưu ý: dường như có một lỗi. Khi ListView được thay đổi kích thước theo chiều dọc, đến mức làm xuất hiện thanh cuộn dọc, cột sẽ liên tục tăng chiều rộng cho đến khi thanh cuộn biến mất.
Jake Berger

1
Bài đăng này có thể cung cấp thông tin chi tiết về hành vi được mô tả trong nhận xét trước của tôi.
Jake Berger

Thật tuyệt, ý tôi là cả mã và trang web :). Tôi tin rằng nó sẽ hữu ích khi tôi có những yêu cầu khắt khe hơn.
Gqqnbig

12

Tôi đã tạo lớp sau và sử dụng trên ứng dụng bất cứ nơi nào được yêu cầu thay cho 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

Vì tôi đã có ItemContainerStyle, tôi phải đặt HorizontalContentAlignment trong 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

Tôi thích giải pháp của user1333423 ngoại trừ việc nó luôn định lại kích thước cho mọi cột; tôi cần cho phép một số cột có chiều rộng cố định. Vì vậy, trong phiên bản này, các cột có chiều rộng được đặt thành "Tự động" sẽ được đặt kích thước tự động và những cột được đặt thành số tiền cố định sẽ không được đặt kích thước tự động.

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

Tôi biết rằng điều này là quá muộn nhưng đây là cách tiếp cận của tôi:

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

Ý tưởng chính là liên kết chiều rộng của phần tử cellTemplete với chiều rộng của ViewGridColumn. Width = 100 là chiều rộng mặc định được sử dụng cho đến khi thay đổi kích thước đầu tiên. Không có bất kỳ mã nào đằng sau. Mọi thứ đều có trong xaml.


Điều này đã thôi thúc tôi giải pháp này để lấp đầy chiều rộng của một cột: <GridViewColumn Width = "{Binding RelativeSource = {RelativeSource AncestorType = ListView}, Path = ActualWidth}">
J. Andersen

1

Tôi gặp khó khăn với câu trả lời được chấp nhận (vì tôi đã bỏ lỡ phần HorizontalAlignment = Stretch và đã điều chỉnh câu trả lời ban đầu).

Đây là một kỹ thuật khác. Nó sử dụng Grid với SharedSizeGroup.

Lưu ý: các Grid.IsSharedScope = true vào 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>

Bạn có chiều rộng GridViewColumnlà as 40và bạn đặt chiều rộng định nghĩa cột là Auto? Điều đó không có ý nghĩa.
BK

1

Tôi đã tạo một hàm để cập nhật tiêu đề cột GridView cho một danh sách và gọi nó bất cứ khi nào cửa sổ được thay đổi kích thước hoặc chế độ xem danh sách cập nhật bố cục của nó.

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

Đây là mã của bạn

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

Thử đi

<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>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.