TextBlock 文本不在 DataGridCell 内垂直居中

Posted

技术标签:

【中文标题】TextBlock 文本不在 DataGridCell 内垂直居中【英文标题】:TextBlock text not vertically centering within DataGridCell 【发布时间】:2014-10-19 22:36:36 【问题描述】:

我正在 C# 中创建一个 DataGrid(来自代码隐藏/不是 XAML),但无论我尝试什么,我都无法让文本在数据单元格中垂直居中:

我一开始是:

var CellStyle = new Style(typeof(DataGridCell)) 
    Setters = 
        new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Center)
    
;

正确定位单元格并将文本水平居中(根据上面的屏幕截图)。

尝试将文本垂直居中,我知道 TextBlock 不支持垂直内容对齐,只支持它自己在父元素中的垂直对齐。

根据这个问题 (Text vertical alignment in WPF TextBlock) 我尝试使用 Padding 伪造它:

var CellStyle = new Style(typeof(DataGridCell)) 
    Setters = 
        new Setter(TextBlock.PaddingProperty, new Thickness(5)),
        new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Center)
    
;

这没什么区别。然后我尝试了这个:

var CellStyle = new Style(typeof(DataGridCell)) 
    Setters = 
        new Setter(DataGridCell.VerticalContentAlignmentProperty, VerticalAlignment.Center),
        new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Center),
        new Setter(TextBlock.VerticalAlignmentProperty, VerticalAlignment.Center)
    
;

导致:

添加 new Setter(DataGridCell.HeightProperty, 50d), 会生成屏幕截图 #1。

如何将数据单元格中的文本垂直居中?

【问题讨论】:

您是在使用 CellTemplate,而不是仅仅为您的单元格样式填充 TextColumns 或其他一些模板吗?我假设是这样,因为这看起来像是一种非常非默认的样式。 @ChrisW。不,没有模板!这是完整的源代码(一周内自毁):pastebin.com/Sc5tWnDH - Screen 类是最小的并且不包含任何样式。 尝试在 VerticalAlignment 上拉伸而不是居中 【参考方案1】:

使用 Blend for Visual Studio,我们为 DataGridCell 提供了这种样式:

<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="x:Type DataGridCell">
                <Border BorderBrush="TemplateBinding BorderBrush" 
                        BorderThickness="TemplateBinding BorderThickness" 
                        Background="TemplateBinding Background" 
                        SnapsToDevicePixels="True"                          
                >
                    <ContentPresenter SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
</Setter>

所以看起来这里没有任何默认支持更改对齐方式。通常&lt;ContentPresenter&gt; 应该有这样的代码:

<ContentPresenter SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels"
                  VerticalAlignment="TemplateBinding VerticalContentAlignment" 
                  HorizontalAlignment="TemplateBinding HorizontalContentAlignment"/>

然后我们可以改变DataGridCell的样式中的VerticalContentAlignmentHorizontalContentAlignment来改变对齐方式。

这意味着如果使用 XAML 代码,只需附加上述代码即可解决您的解决方案。但是如果你想在后面使用代码,当然要更长更复杂。

这里我给大家介绍2个解决方案。首先为ControlTemplate 构建VisualTree 并为DataGridCellTemplate 属性设置该模板:

//root visual of the ControlTemplate for DataGridCell is a Border
var border = new FrameworkElementFactory(typeof(Border));
border.SetBinding(Border.BorderBrushProperty, new Binding("BorderBrush")  
        RelativeSource = RelativeSource.TemplatedParent
);
border.SetBinding(Border.BackgroundProperty, new Binding("Background") RelativeSource = RelativeSource.TemplatedParent );
border.SetBinding(Border.BorderThicknessProperty, new Binding("BorderThickness") RelativeSource = RelativeSource.TemplatedParent );
border.SetValue(SnapsToDevicePixelsProperty, true);
//the only child visual of the border is the ContentPresenter
var contentPresenter = new FrameworkElementFactory(typeof(ContentPresenter));
contentPresenter.SetBinding(SnapsToDevicePixelsProperty, new Binding("SnapsToDevicePixelsProperty") RelativeSource=RelativeSource.TemplatedParent );
contentPresenter.SetBinding(VerticalAlignmentProperty, new Binding("VerticalContentAlignment")  RelativeSource = RelativeSource.TemplatedParent );
contentPresenter.SetBinding(HorizontalAlignmentProperty, new Binding("HorizontalContentAlignment") RelativeSource = RelativeSource.TemplatedParent );
//add the child visual to the root visual
border.AppendChild(contentPresenter);

//here is the instance of ControlTemplate for DataGridCell
var template = new ControlTemplate(typeof(DataGridCell));
template.VisualTree = border;
//define the style
var style = new Style(typeof(DataGridCell));
style.Setters.Add(new Setter(TemplateProperty, template));
style.Setters.Add(new Setter(VerticalContentAlignmentProperty, 
                             VerticalAlignment.Center));
style.Setters.Add(new Setter(HorizontalContentAlignmentProperty, 
                             HorizontalAlignment.Center));
yourDataGrid.CellStyle = style;

第二种解决方案是使用XamlReader 直接解析XAML 代码,这意味着我们需要在字符串中保存之前给出的确切XAML 代码,XamlReader 将解析该字符串并给出Style 的实例:

var xaml = "<Style TargetType=\"x:Type DataGridCell\"><Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>" +
           "<Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>" +
           "<Setter Property=\"Template\">" +
           "<Setter.Value><ControlTemplate TargetType=\"DataGridCell\">" +
           "<Border BorderBrush=\"TemplateBinding BorderBrush\" BorderThickness=\"TemplateBinding BorderThickness\" Background=\"TemplateBinding Background\" SnapsToDevicePixels=\"True\">" +
           "<ContentPresenter SnapsToDevicePixels=\"TemplateBinding SnapsToDevicePixels\" VerticalAlignment=\"TemplateBinding VerticalContentAlignment\" HorizontalAlignment=\"TemplateBinding HorizontalContentAlignment\"/>" +
           "</Border></ControlTemplate></Setter.Value></Setter></Style>";

var parserContext = new System.Windows.Markup.ParserContext();          
parserContext.XmlnsDictionary
             .Add("","http://schemas.microsoft.com/winfx/2006/xaml/presentation");
parserContext.XmlnsDictionary
             .Add("x","http://schemas.microsoft.com/winfx/2006/xaml");            
yourDataGrid.CellStyle = (Style)System.Windows.Markup.XamlReader.Parse(xaml,parserContext); 

您可以看到这两种解决方案都相当长,但它们实际上是您应该使用后面的代码执行的操作。这意味着我们应该始终尽可能多地使用 XAML 代码。 WPF 中的许多功能主要是为 XAML 代码设计的,因此使用后面的代码当然并不简单,而且通常很冗长。

注意:我在开头发布的XAML 代码不是DataGridCell 的完整默认样式,它还有更多Triggers。这意味着代码可能会更长,抱歉,这是完整的默认 XAML 代码:

<Style TargetType="x:Type DataGridCell">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="x:Type DataGridCell">
                <Border BorderBrush="TemplateBinding BorderBrush" BorderThickness="TemplateBinding BorderThickness" Background="TemplateBinding Background" SnapsToDevicePixels="True"                            
                >
                    <ContentPresenter SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="Background" Value="DynamicResource x:Static SystemColors.HighlightBrushKey"/>
            <Setter Property="Foreground" Value="DynamicResource x:Static SystemColors.HighlightTextBrushKey"/>
            <Setter Property="BorderBrush" Value="DynamicResource x:Static SystemColors.HighlightBrushKey"/>
        </Trigger>
        <Trigger Property="IsKeyboardFocusWithin" Value="True">
            <Setter Property="BorderBrush" Value="DynamicResource x:Static DataGrid.FocusBorderBrushKey"/>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsSelected" Value="true"/>
                <Condition Property="Selector.IsSelectionActive" Value="false"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" Value="DynamicResource x:Static SystemColors.InactiveSelectionHighlightBrushKey"/>
            <Setter Property="BorderBrush" Value="DynamicResource x:Static SystemColors.InactiveSelectionHighlightBrushKey"/>
            <Setter Property="Foreground" Value="DynamicResource x:Static SystemColors.InactiveSelectionHighlightTextBrushKey"/>
        </MultiTrigger>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Foreground" Value="DynamicResource x:Static SystemColors.GrayTextBrushKey"/>
        </Trigger>
    </Style.Triggers>
</Style>

但是我刚刚测试过,看起来默认样式始终应用于DataGridCell,它只是被您添加的Setter 覆盖(设置相同的属性)。这是测试代码,Trigger 仍然有效:

//change the highlight selected brush to Red (default by blue).
yourDataGrid.Resources.Add(SystemColors.HighlightBrushKey, Brushes.Red);

【讨论】:

+1 只是为了将其指向 XAML 而不是后面的代码。谢谢你:) 感谢您的全面回答!我使用了XamlReader.Parse() 解决方案,因为我已经有一个ParserContext 用于我可以重复使用的另一种样式。它有效,非常感谢! @ChrisW。我已经在 XAML 中有这个,并且必须将它转换为 C# 以便在一些动态内容创建中使用。 我设法将其缩短为(使用原始 XAML):var xaml = "&lt;Style x:Key=\"DataGridCellStyle\" TargetType=\"DataGridCell\"&gt;&lt;Setter Property=\"Template\"&gt;&lt;Setter.Value&gt;&lt;ControlTemplate TargetType=\"x:Type DataGridCell\"&gt;&lt;ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/&gt;&lt;/ControlTemplate&gt;&lt;/Setter.Value&gt;&lt;/Setter&gt;&lt;/Style&gt;"; @DannyBeckett 是的,我打算将TemplateBinding 用于VerticalAlignmentHorizontalAlignment。然后我们可以在一段时间后将VerticalContentAignmentHorizontalContentAlignment 更改为我们想要的DataGridCell 样式。【参考方案2】:

我的回答只是总结了King King,以防它对某人有所帮助。在 XAML 中:

DataGrid where 中使用属性CellStyle="StaticResource CustomCell"

<Style x:Key="CustomCell" TargetType="x:Type DataGridCell">
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="x:Type DataGridCell">
                <ContentPresenter VerticalAlignment="TemplateBinding VerticalContentAlignment"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

【讨论】:

以上是关于TextBlock 文本不在 DataGridCell 内垂直居中的主要内容,如果未能解决你的问题,请参考以下文章

背水一战 Windows 10 (27) - 控件(文本类): TextBlock

TextBlock 中的 XAML 自定义文本

WPF TextBlock 文本换行的2种方式

WPF TextBlock文本纵向排列

如何将 TextBlock 绑定到包含格式化文本的资源?

从 TextBlock 获取显示的文本