【问题标题】:How to autosize and right-align GridViewColumn data in WPF?如何在 WPF 中自动调整和右对齐 GridViewColumn 数据?
【发布时间】:2010-10-08 07:36:11
【问题描述】:

我该怎么做:

  • 右对齐 ID 列中的文本
  • 根据具有最长可见数据的单元格的文本长度使每一列自动调整大小?

代码如下:

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

部分答案:

感谢 Kjetil,GridViewColumn.CellTemplate 运行良好,并且 Auto Width 运行良好,但是当 ObservativeCollection“Collection”使用长于列宽的数据进行更新时,列大小不会自行更新,因此这只是一个数据初始显示的解决方案:

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

【问题讨论】:

  • 您有没有找到解决自动调整大小问题的方法?我正在经历同样的事情。
  • @Oskar - 列表的虚拟化阻止了自动解决方案。该列表只知道当前可见的项目并相应地设置大小。如果列表后面还有更多项目,它不知道它们,因此无法解释它们。如果您使用数据绑定,ProgrammingWPF - Sells-Griffith 书建议使用手动列宽。 :(
  • @Gishu 谢谢,这实际上是有道理的..
  • 如果使用 MVVM 和 Binding 值发生变化,请参阅@Rolf Wessels 回答。

标签: wpf xaml listview gridview gridviewcolumn


【解决方案1】:

要使每个列自动调整大小,您可以在 GridViewColumn 上设置 Width="Auto"。

要右对齐 ID 列中的文本,您可以使用 TextBlock 创建单元格模板并设置 TextAlignment。然后设置 ListViewItem.Horizo​​ntalContentAlignment(在 ListViewItem 上使用带有 setter 的样式),使单元格模板填满整个 GridViewCell。

也许有一个更简单的解决方案,但这应该可行。

注意:该解决方案需要 Window.Resources 中的 Horizo​​ntalContentAlignment=Stretch 和 CellTemplate 中的 TextAlignment=Right

<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 - 我可以将此设置应用于特定列吗?
  • +1 为:
  • 棒极了,但我有 15 列,有什么办法不必为所有列重复单元格模板?
  • 如果您忘记从 GridViewColumn 中删除 DisplayMemberBinding,它也不起作用。模板不会有任何效果。
  • @Mohamed 为什么不是?
【解决方案2】:

如果内容的宽度发生变化,您将不得不使用这段代码来更新每一列:

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

    column.Width = double.NaN;
}

每次该列的数据更新时,您都必须触发它。

【讨论】:

  • 你会把这个附在什么上面?
  • 更新网格数据后在 GridViewColumn 上手动运行它。如果你有一个 ViewModel,你可以订阅它的 PropertyChanged 事件然后运行它。
  • +1 谢谢!这对我帮助很大!与这个问题无关,但无论如何:我实现了一个自定义的 List/GridView,您可以在运行时通过 GUI 动态添加/删除列。但是,当我删除并重新添加一列时,它不再出现。首先,我认为它根本没有添加(出于某种原因),但后来(使用 Snoop)我发现它实际上是添加的,但 ActualWidth 为 0(它是自动调整大小的,并且在列时明显重置删除)。现在,在将列重新添加到列后,我使用您的代码将列设置为正确的宽度。非常感谢!
  • 解决我问题的简单方法!
  • +1 完美!希望这被标记为答案。我将 x:Name="gvcMyColumnName" 添加到定义列的 XAML 中,以便我可以在后面的代码中访问它。像冠军一样工作。
【解决方案3】:

如果您的列表视图也在重新调整大小,那么您可以使用行为模式来重新调整列的大小以适应整个 ListView 宽度。几乎和你使用 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>

有关一些示例和源代码链接,请参见以下链接 http://lazycowprojects.tumblr.com/post/7063214400/wpf-c-listview-column-width-auto

【讨论】:

  • 这个很酷。解决问题并为您提供您正在寻找的所有 、n、Auto 功能。
  • 这就是我要找的。 :D
  • 注意:似乎有一个错误。当 ListView 垂直调整大小到导致出现垂直滚动条的程度时,列的宽度会不断增加,直到滚动条消失。
  • This post 可能会提供有关我之前评论中描述的行为的见解。
  • 这很酷,我的意思是代码和网站:)。我相信当我有更严格的要求时它会很有用。
【解决方案4】:

我创建了以下类,并在需要时在应用程序中使用以代替 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);
    }
}

【讨论】:

    【解决方案5】:

    因为我有一个 ItemContainerStyle,所以我不得不将 Horizo​​ntalContentAlignment 放在 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】:

      我喜欢 user1333423 的解决方案,只是它总是重新调整每一列的大小;我需要允许一些列固定宽度。因此,在此版本中,宽度设置为“自动”的列将自动调整大小,而设置为固定数量的列将不会自动调整大小。

      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);
          }        
      }
      

      【讨论】:

        【解决方案7】:

        我知道这为时已晚,但这是我的方法:

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

        主要思想是将cellTemplete元素的宽度绑定到ViewGridColumn的宽度上。 Width=100 是在第一次调整大小之前使用的默认宽度。后面没有任何代码。一切都在 xaml 中。

        【讨论】:

        • 这启发了我这个解决方案来填充一列的宽度:
        【解决方案8】:

        我接受的答案有问题(因为我错过了 Horizo​​ntalAlignment=Stretch 部分并调整了原始答案)。

        这是另一种技术。它使用带有 SharedSizeGroup 的 Grid。

        注意: ListView 上的 Grid.IsSharedScope=true

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

        【讨论】:

        • 您将GridViewColumn 的宽度设置为40,并将列定义宽度设置为Auto?这没有意义。
        【解决方案9】:

        我创建了一个用于更新列表的 GridView 列标题的函数,并在调整窗口大小或列表视图更新其布局时调用它。

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

        【讨论】:

          【解决方案10】:

          @RandomEngy 上面给出的解决方案有效,但似乎有一个问题, 仅根据可见数据调整大小(因此,前 X 行),不一定是每列的最长(最宽行)值。

          要解决上述问题,可以完成以下操作。

          附上即使您的列表视图更改的集合,如下所示,

          ((INotifyCollectionChanged) MyListView.ItemsSource).CollectionChanged += CollectionChanged_Handler;
          

          还声明一个私有 maxWidth 属性来存储您的视图遇到的最长内容。

          private double maxWidth = 200;//Whatever your default width is.
          

          现在处理程序如下,

          private void CollectionChanged_Handler(object sender, NotifyCollectionChangedEventArgs args)
          {
             var gridView = (GridView)MyListView.View;
             if(gridView != null)
             {
                foreach( var column in gridView.Columns)
                {
                   if(column.ActualWidth > maxWidth)
                   {
                      if (double.IsNaN(column.Width))
                      {
                             maxWidth = column.ActualWidth;
                             column.Width = maxWidth ;
                      }
                      column.Width = double.NaN;
                   }       
                 }   
          }
          

          也有可能一旦您启动对话框,寡妇已经填充了多行,而顶部的行不是最长的。仅当集合更改时才会触发上面的代码,但是由于数据已经加载并且加载对话框时可见的行不是最宽的行,因此上面的代码将无法调整列的大小。为了解决此问题,请在 ListView_OnPreviewMouseLeftButton 事件上调用上述处理程序。如下图,

          private void MyListView_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
          {
          CollectionChanged_Handler(sender, null);
          }
          

          一旦有人滚动到视图的最宽行内容,上面的代码将刷新您的列宽。

          【讨论】:

            【解决方案11】:

            这是你的代码

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

            试试这个

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

            【讨论】:

              【解决方案12】:

              嗯,我刚遇到这个问题,我用 IValueConverter 解决了它。

              public class GridViewColumHeaderWidthConverter : IValueConverter
              {
                  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
                  {
                      return (double)value / 8;
                  }
              
                  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
                  {
                      throw new NotImplementedException();
                  }
              }
              

              注意:8 是 GridViewColumns 的数量

              现在在 Xaml 中:

              <ListView x:Name="TheListView" ItemsSource="{Binding Customers}">
                  <ListView.View>
                      <GridView>
                          <!--  Id  -->
                          <GridViewColumn Width="{Binding ElementName=TheListView, Path=ActualWidth, Converter={StaticResource GridViewColumHeaderWidthConverter}}">
                              <GridViewColumnHeader Content="Id" />
                              <GridViewColumn.CellTemplate>
                                  <DataTemplate>
                                      <TextBlock Text="{Binding CustomerId}" />
                                  </DataTemplate>
                              </GridViewColumn.CellTemplate>
                          </GridViewColumn>
                          <!--  Code  -->
                          <GridViewColumn Width="{Binding ElementName=TheListView, Path=ActualWidth, Converter={StaticResource GridViewColumHeaderWidthConverter}}">
                              <GridViewColumnHeader Content="Code" />
                              <GridViewColumn.CellTemplate>
                                  <DataTemplate>
                                      <TextBlock Text="{Binding CustomerCode}" />
                                  </DataTemplate>
                              </GridViewColumn.CellTemplate>
                          </GridViewColumn>
              

              不要忘记包含转换器文件。在这种情况下,我在 App.xaml 中定义了转换器

              <Application.Resources>
                  <converters:GridViewColumHeaderWidthConverter x:Key="GridViewColumHeaderWidthConverter" />
              </Application.Resources>
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-01-19
                • 2021-05-29
                • 1970-01-01
                • 2016-05-06
                • 1970-01-01
                • 2011-07-18
                • 1970-01-01
                相关资源
                最近更新 更多