【问题标题】:How to align text or content in GridView (ListView) columns如何对齐 GridView (ListView) 列中的文本或内容
【发布时间】:2019-11-17 10:03:23
【问题描述】:

我想以这种方式在ListView 中声明GridViewColumn

<ListView ItemsSource="{Binding Sells}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="No. of order" Width="80"
                    DisplayMemberBinding="{Binding NoOfOrder, StringFormat=N0}"/>
            <GridViewColumn Header="Quantity" Width="100"
                    DisplayMemberBinding="{Binding Quantity, StringFormat=N0}"/>
            <GridViewColumn Header="Price" Width="100"
                    DisplayMemberBinding="{Binding Price, StringFormat=N2}"/>
        </GridView>
    </ListView.View>
</ListView>

但是这个问题是列标题以及单元格没有按照我想要的方式对齐,我的应用中有几个ListView,我想要他们的一些列标题以及单元格要居中对齐,其他单元格要正确,我在此站点上找到的解决方案是执行以下操作:

<ListView ItemsSource="{Binding Buys}">
    <ListView.Resources>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Width="80">
                <GridViewColumn.Header>
                    <GridViewColumnHeader Content="No. of order" HorizontalContentAlignment="Center"/>
                </GridViewColumn.Header>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding NoOfOrder}" TextAlignment="Center"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn Width="100">
                <GridViewColumn.Header>
                    <GridViewColumnHeader Content="Quantity" HorizontalContentAlignment="Right"/>
                </GridViewColumn.Header>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Quantity, StringFormat=N0}" TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn Width="100">
                <GridViewColumn.Header>
                    <GridViewColumnHeader Content="Price" HorizontalContentAlignment="Right"/>
                </GridViewColumn.Header>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Price, StringFormat=N2}" TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

对于 3 列 ListView 来说,这已经很多了!我还有一些其他的有 7/8 列,所以像这样重写所有这些肯定会很痛苦!有没有办法以我在第一个示例中为Sells 定义的方式声明这些ListView,并具有一些模板和绑定机制,以便我可以这样编写这些列:

<ListView ItemsSource="{Binding Sells}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="No. of order" Align="Center" Width="80"
                    DisplayMemberBinding="{Binding NoOfOrder, StringFormat=N0}"/>
            <GridViewColumn Header="Quantity" Align="Right" Width="100"
                    DisplayMemberBinding="{Binding Quantity, StringFormat=N0}"/>
            <GridViewColumn Header="Price" Align="Right" Width="100"
                    DisplayMemberBinding="{Binding Price, StringFormat=N2}"/>
        </GridView>
    </ListView.View>
</ListView>

还是类似的?

【问题讨论】:

    标签: c# wpf listview gridview


    【解决方案1】:

    一个简单的解决方案是编写一个附加行为。此实现使用两个属性。 ListView.IsColumnContentAlignmentEnabled 必须在对应的GridViewColumn 上设置ListViewListView.ColumnContentAlignment 类型的TextAlignment(如果未设置此属性,则值将默认为TextAlignment.Left)。

    这是因为没有简单的方法来获取 ListView 的父 ListView

    以下附加行为允许对齐任何FrameworkElement(不仅是TextBlock 元素):

    ListView.cs

    // Attached Behavior
    public class ListView : DependencyObject
    {
      #region IsColumnContentAlignmentEnabled attached property
    
      public static readonly DependencyProperty IsColumnContentAlignmentEnabledProperty = DependencyProperty.RegisterAttached(
        "IsColumnContentAlignmentEnabled", typeof(bool), typeof(ListView), new PropertyMetadata(false, ListView.OnIsColumnContentAlignmentEnabledChanged));
    
      public static void SetIsColumnContentAlignmentEnabled(DependencyObject attachingElement, bool value) => attachingElement.SetValue(ListView.IsColumnContentAlignmentEnabledProperty, value);
    
      public static bool GetIsColumnContentAlignmentEnabled(DependencyObject attachingElement) => (bool) attachingElement.GetValue(ListView.IsColumnContentAlignmentEnabledProperty);
    
      #endregion
    
      #region ColumnContentAlignment attached property
    
      public static readonly DependencyProperty ColumnContentAlignmentProperty = DependencyProperty.RegisterAttached(
        "ColumnContentAlignment", typeof(TextAlignment), typeof(ListView), new PropertyMetadata(TextAlignment.Left, ListView.OnColumnContentAlignmentChanged));
    
      public static void SetColumnContentAlignment(DependencyObject attachingElement, TextAlignment value) => attachingElement.SetValue(ListView.ColumnContentAlignmentProperty, value);
    
      public static TextAlignment GetColumnContentAlignment(DependencyObject attachingElement) => (TextAlignment) attachingElement.GetValue(ListView.ColumnContentAlignmentProperty);
    
      #endregion
    
      private static Dictionary<GridViewColumn, System.Windows.Controls.ListView> ColumnListViewTable { get; }
    
      static ListView()
      {
        ListView.ColumnListViewTable = new Dictionary<GridViewColumn, System.Windows.Controls.ListView>();
      }
    
      private static void OnColumnContentAlignmentChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
      {
        // Get the parent ListView of the current column
        if (attachingElement is GridViewColumn gridViewColumn 
          && ListView.ColumnListViewTable.TryGetValue(gridViewColumn, out System.Windows.Controls.ListView listView))
        {
          ListView.AlignColumns(listView);
        }
      }
    
      private static void OnIsColumnContentAlignmentEnabledChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
      {
        if (!(attachingElement is System.Windows.Controls.ListView listView))
        {
          return;
        }
    
        bool isEnabled = (bool) e.NewValue;
        if (isEnabled)
        {
          if (!listView.IsLoaded)
          {
            listView.Loaded += ListView.InitializeColumnsOnListViewLoaded;
            return;
          }
          ListView.InitializeColumns(listView);
        }
        else
        {
          ListView.UnregisterColumns(listView);
        }
      }
    
      private static void InitializeColumnsOnListViewLoaded(object sender, RoutedEventArgs e)
      {
        var listView = sender as System.Windows.Controls.ListView;
        listView.Loaded -= ListView.InitializeColumnsOnListViewLoaded;
        ListView.InitializeColumns(listView);
      }
    
      private static void InitializeColumns(System.Windows.Controls.ListView listView)
      {
        if (ListView.TryRegisterColumns(listView))
        {
          ListView.AlignColumns(listView);
        }
      }
    
      private static void AlignColumns(System.Windows.Controls.ListView listView)
      {
        IEnumerable<ListBoxItem> listViewItemContainers = listView.Items
          .OfType<object>()
          .Select(item => listView.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem);
    
        foreach (ListBoxItem listViewItemContainer in listViewItemContainers)
        {
          if (!listViewItemContainer.TryFindVisualChildElement(out GridViewRowPresenter rowPresenter))
          {
            continue;
          }
    
          List<object> rowElements = LogicalTreeHelper.GetChildren(rowPresenter)
            .Cast<object>()
            .ToList();
    
          for (int columnIndex = 0; columnIndex < rowPresenter.Columns.Count; columnIndex++)
          {
            TextAlignment columnContentAlignment = ListView.GetColumnContentAlignment(rowPresenter.Columns.ElementAt(columnIndex));
            if (rowElements.ElementAt(columnIndex) is TextBlock textBlock)
            {
              textBlock.TextAlignment = columnContentAlignment;
            }
    
            // Because the TextBlock's width is set to Auto
            // the aligned text will always appear to be left aligned.
            // Therefore set equivalent HorizontalAlignment to compensate.
            // Also setting the HorizontalAlignment enables alignment of all FrameworkElements
            if (rowElements.ElementAt(columnIndex) is FrameworkElement frameworkElement)
            {
              frameworkElement.HorizontalAlignment = ListView.GetHorizontalAlignment(columnContentAlignment);
            }
          }
        }
      }
    
      private static HorizontalAlignment GetHorizontalAlignment(TextAlignment textBlockTextAlignment)
      {
        switch (textBlockTextAlignment)
        {
          case TextAlignment.Center: return HorizontalAlignment.Center;
          case TextAlignment.Right: return HorizontalAlignment.Right;
          case TextAlignment.Justify: return HorizontalAlignment.Stretch;
          default: return HorizontalAlignment.Left;
        }
      }
    
      private static void UnregisterColumns(System.Windows.Controls.ListView listView)
      {
        if (!(listView.View is System.Windows.Controls.GridView gridView))
        {
          return;
        }
        gridView.Columns
          .ToList()
          .ForEach(column => ListView.ColumnListViewTable.Remove(column));
      }
    
      private static bool TryRegisterColumns(System.Windows.Controls.ListView listView)
      {
        if (!(listView.View is System.Windows.Controls.GridView gridView))
        {
          return false;
        }
        gridView.Columns
          .ToList()
          .ForEach(column => ListView.ColumnListViewTable.Add(column, listView));
        return true;
      }
    }
    

    使用示例

    <ListView ListView.IsColumnContentAlignmentEnabled="True"
              ItemsSource="{Binding Persons}">
      <ListView.View>
        <GridView>
          <GridViewColumn DisplayMemberBinding="{Binding FirstName}"
                          ListView.ColumnContentAlignment="Right"
                          Header="First Name" />
          </GridViewColumn>
          <GridViewColumn DisplayMemberBinding="{Binding LastName}"
                          ListView.ColumnContentAlignment="Center"
                          Header="Last Name" />
          </GridViewColumn>
        </GridView>
      </ListView.View>
    </ListView>
    

    【讨论】:

    • 我必须在所有ListView 中定义相同的资源!是否可以将该资源带到App.xaml,这样我就可以避免在每个ListView 中定义它?
    • 是的,您可以这样做。我只是选择了最接近作用域的ResourceDictionary,使代码sn-p简洁。
    • 还有一个问题,有时是NoOfOrder,有时是QuantityPriceValue等。如何参数化App.xaml中的那些绑定?
    猜你喜欢
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 2011-05-02
    • 2018-07-21
    相关资源
    最近更新 更多