如何对齐 GridView(ListView)列中的文本或内容
How to align text or content in GridView (ListView) columns
我想在 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>
但问题在于列 headers 以及单元格未按我希望的方式对齐,我的应用程序中有几个 ListView
,我想要他们的一些专栏 headers 以及要居中对齐的单元格和其他要对齐的单元格,我在这个网站上找到的解决方案是做这样的事情:
<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>
或类似的东西?
一个简单的解决方案是编写附加行为。此实现使用两个属性。 ListView.IsColumnContentAlignmentEnabled
必须在对应的 GridViewColumn
类型 TextAlignment
的 ListView
和 ListView.ColumnContentAlignment
上设置(如果此 属性 未设置,则该值将默认为 TextAlignment.Left
)。
这是因为没有简单的方法来获取 GridViewColumn
的父级 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
中这样声明 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>
但问题在于列 headers 以及单元格未按我希望的方式对齐,我的应用程序中有几个 ListView
,我想要他们的一些专栏 headers 以及要居中对齐的单元格和其他要对齐的单元格,我在这个网站上找到的解决方案是做这样的事情:
<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>
或类似的东西?
一个简单的解决方案是编写附加行为。此实现使用两个属性。 ListView.IsColumnContentAlignmentEnabled
必须在对应的 GridViewColumn
类型 TextAlignment
的 ListView
和 ListView.ColumnContentAlignment
上设置(如果此 属性 未设置,则该值将默认为 TextAlignment.Left
)。
这是因为没有简单的方法来获取 GridViewColumn
的父级 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>