【问题标题】:How can I have a WPF TextBlock that has the same size for all font weights?我怎样才能拥有一个所有字体粗细都具有相同大小的 WPF TextBlock?
【发布时间】:2013-12-31 14:54:41
【问题描述】:

我会开始追逐:有什么方法可以告诉 WPF TextBlock 进行自我测量,以使其大小在 FontWeight 发生变化时不会发生变化?

我有一个TextBlock,它可以根据样式动态更改字体粗细。 TextBlockRadioButton 内,因此选中时为粗体,否则为正常:

<Style x:Key="BoldWhenChecked" TargetType="RadioButton">
    <Style.Triggers>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="TextElement.FontWeight" Value="Bold" />
        </Trigger>
    </Style.Triggers>
</Style

这里是单选按钮本身:

<StackPanel Orientation="Horizontal">
    <RadioButton Style="{StaticResource BoldWhenChecked}">
        <TextBlock Text="Item 1" />
    </RadioButton>
    <RadioButton Style="{StaticResource BoldWhenChecked}">
        <TextBlock Text="Item 2" />
    </RadioButton>
    <RadioButton Style="{StaticResource BoldWhenChecked}">
        <TextBlock Text="Item 3" />
    </RadioButton>
    etc...
</StackPanel>

不幸的是,由于我没有使用固定宽度的字体,TextBlock 的宽度会在字体粗细发生变化时发生变化,并且整个单选按钮面板也会相应地发生变化,这在视觉上很不协调。

【问题讨论】:

  • 你能显示RadioButtons的xaml吗?
  • 您是否在为 RadioButtons 使用控件模板?

标签: wpf textblock


【解决方案1】:

我个人会尝试更好地组织我的布局,以便它们不依赖于单选按钮的大小。也就是说,如果您绝对坚持这样做,那么您需要根据粗体时的大小设置每个 TextBlock 的宽度,并且如果您需要更改文本和/或字体系列等。为此,您需要将 Width 属性绑定到接受所有其他值的转换器。

从设置文本框宽度的样式开始:

<Style TargetType="{x:Type TextBlock}">
        <Setter Property="Width">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource TextToWidthConverter}">
                    <Binding Path="Text" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/>
                    <Binding Path="FontFamily" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/>
                    <Binding Path="FontStyle" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/>
                    <Binding Path="FontStretch" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/>
                    <Binding Path="FontSize" RelativeSource="{RelativeSource Self}" UpdateSourceTrigger="PropertyChanged"/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>

现在添加值转换器本身的代码(注意我使用code posted by Clarke Kent in another StackOverflow answer 来测量文本):

public class TextToWidthConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var text = values[0] as String;
        var fontFamily = values[1] as FontFamily;
        var fontStyle = (FontStyle)values[2];
        var fontStretch = (FontStretch)values[3];
        var fontSize = (Double)values[4];
        var size = MeasureText(text, fontFamily, fontStyle, FontWeights.Bold, fontStretch, fontSize);
        return size.Width;
    }

    public object[] ConvertBack(object values, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Get the required height and width of the specified text. Uses Glyph's
    /// </summary>
    public static Size MeasureText(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
    {
        Typeface typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
        GlyphTypeface glyphTypeface;

        if (!typeface.TryGetGlyphTypeface(out glyphTypeface))
        {
            return MeasureTextSize(text, fontFamily, fontStyle, fontWeight, fontStretch, fontSize);
        }

        double totalWidth = 0;
        double height = 0;

        for (int n = 0; n < text.Length; n++)
        {
            ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[n]];

            double width = glyphTypeface.AdvanceWidths[glyphIndex] * fontSize;

            double glyphHeight = glyphTypeface.AdvanceHeights[glyphIndex] * fontSize;

            if (glyphHeight > height)
            {
                height = glyphHeight;
            }

            totalWidth += width;
        }

        return new Size(totalWidth, height);
    }

    /// <summary>
    /// Get the required height and width of the specified text. Uses FortammedText
    /// </summary>
    public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
    {
        FormattedText ft = new FormattedText(text,
                                             CultureInfo.CurrentCulture,
                                             FlowDirection.LeftToRight,
                                             new Typeface(fontFamily, fontStyle, fontWeight, fontStretch),
                                             fontSize,
                                             Brushes.Black);
        return new Size(ft.Width, ft.Height);
    }
}

但同样,在一个真正的应用程序中,我会尝试通过将它们放在网格或其他东西中来正确解决这个问题。

【讨论】:

  • "...通过将它们放在网格或其他东西中。"那将如何运作?这正是我正在寻找的,但还没有找到方法。
【解决方案2】:

我通过在RadioButton 的内容中添加隐藏的TextBlock 并将其FontStyle 明确设置为Bold,为此创建了一个解决方法:

<RadioButton Style="{StaticResource BoldWhenChecked}">
    <Grid>
        <TextBlock Text="Item 1" />
        <TextBlock Text="Item 1" FontStyle="Bold" Visibility="Hidden" />
    </Grid>
</RadioButton>

这样,当 RadioButton 被选中并且可见的 TextBlock 变为粗体时,宽度不会改变,因为隐藏的 TextBlock 已经适当地调整了网格的大小。

【讨论】:

    【解决方案3】:

    来自your comment here

    “...通过将它们放在网格或其他东西中。”那将如何运作?这正是我正在寻找的,但还没有找到办法。

    我知道这是一个老问题。虽然您实际上并没有在问题中这么说,也没有包含一个很好的 Minimal, Complete, Verifiable example 来充分说明您的情况,但我确实怀疑您有(或者更确切地说,有)一些需要记住的群体单选按钮不会随着字体样式的变化而改变大小,但 do 会以某种方式动态调整大小。为此,我认为您使用隐藏的TextBlock 的解决方法还不错。

    但我会说,如果您可以在外部控制 整个组 单选按钮的布局,那么使用其中一个网格元素来安排单选按钮本身会更好,正如所暗示的那样马克在他的回答中。

    关键是网格对象可以将宽度和高度均匀地分布在它们的列和行上,因此可以独立于它们标称的所需大小来布置单选按钮。

    例如:

    <Window x:Class="TestSO20556328BoldRadioButtons.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
    
      <Window.Resources>
        <p:Style x:Key="BoldWhenChecked" TargetType="RadioButton">
          <p:Style.Triggers>
            <Trigger Property="IsChecked" Value="True">
              <Setter Property="TextElement.FontWeight" Value="Bold" />
            </Trigger>
          </p:Style.Triggers>
        </p:Style>
      </Window.Resources>
    
      <StackPanel>
        <UniformGrid Columns="3">
          <RadioButton Style="{StaticResource BoldWhenChecked}">
            <TextBlock Text="Item 1" />
          </RadioButton>
          <RadioButton Style="{StaticResource BoldWhenChecked}">
            <TextBlock Text="Item 2" />
          </RadioButton>
          <RadioButton Style="{StaticResource BoldWhenChecked}">
            <TextBlock Text="Item 3" />
          </RadioButton>
        </UniformGrid>
    
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <RadioButton Style="{StaticResource BoldWhenChecked}">
            <TextBlock Text="Item 1" />
          </RadioButton>
          <RadioButton Style="{StaticResource BoldWhenChecked}" Grid.Column="1">
            <TextBlock Text="Item 2" />
          </RadioButton>
          <RadioButton Style="{StaticResource BoldWhenChecked}" Grid.Column="2">
            <TextBlock Text="Item 3" />
          </RadioButton>
        </Grid>
      </StackPanel>
    </Window>
    

    只是为了展示解决问题的几种不同方法。

    请注意,即使您试图以某种方式动态调整单选按钮组的大小,上述方法也可以工作。但如果问题中没有更多细节,我将无法说出具体的方法可以实现该目标。

    【讨论】:

      猜你喜欢
      • 2015-09-26
      • 1970-01-01
      • 2016-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-09
      相关资源
      最近更新 更多