【问题标题】:How to get rid of whitespace between Runs in TextBlock?如何摆脱 TextBlock 中运行之间的空白?
【发布时间】:2012-06-18 19:55:22
【问题描述】:

我有以下 XAML:

<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
                                               FontSize="10" FontFamily="Arial" Foreground="#414141">        
                                            <Run Text="{Binding LoadsCount}" />        
                                            <Run Text="+" />        
                                            <Run Text="{Binding BrokerLoadsCount}" />
                                        </TextBlock>

我得到这样的显示:12 + 11 不知何故,它在每个 Run 之间插入了额外的空间 如何让它显示12+11

【问题讨论】:

标签: wpf xaml


【解决方案1】:

运行标签之间的空格导致空格,这是最简单的解决方法。

<TextBlock 
   HorizontalAlignment="Center" 
   VerticalAlignment="Center"
   FontSize="10" 
   FontFamily="Arial" 
   Foreground="#414141">        
      <Run Text="{Binding LoadsCount}" /><Run Text="+" /><Run Text="{Binding BrokerLoadsCount}" />
</TextBlock>

因为 &lt;TextBlock&gt;&lt;/TextBlock&gt; 之间的任何内容都针对 TextBlock 的文本属性,所以运行之间的空白处会导致您看到的效果。你也可以把它缩短到这个。

<Run Text="{Binding LoadsCount}" />+<Run Text="{Binding BrokerLoadsCount}" />

这篇 MSDN 文章详细介绍了 xaml 如何处理空格

http://msdn.microsoft.com/en-us/library/ms788746.aspx

如果你好奇为什么一个中断和大量的制表符会转化为一个空格

所有空白字符(空格、换行符、制表符)都转换为 空格。

所有连续的空格被删除并替换为一个空格

【讨论】:

  • 这个解决方案的问题是,一旦您应用了格式化程序(如 XAML Styler),一切都会回到原来的问题。因此,在包含数百行的大型 XAML 文件中,您必须确保返回并删除每种格式后的所有换行符。
【解决方案2】:

另一种选择是注释 Run 标记之间的空格,保持代码可读性并删除多余的空格。

<TextBlock HorizontalAlignment="Center"
           VerticalAlignment="Center"
           FontSize="10" FontFamily="Arial" Foreground="#414141">        
    <Run Text="{Binding LoadsCount}" /><!--
 --><Run Text="+" /><!--
 --><Run Text="{Binding BrokerLoadsCount}" />
</TextBlock>

【讨论】:

    【解决方案3】:

    Kevin 的好解决方案的一个问题是,当您应用某些 XAML/XML 自动重新格式化功能时,XAML 标记的单行格式会被撤消,例如“ctrl-K + ctrl-D”。我发现的一种解决方法是将Run 标签格式化如下:

    <TextBlock>
        <Run FontStyle="Italic"
        Text="aaa" /><Run 
        Text="bbb" />
    </TextBlock>
    

    尽管像这样跨行拆分标签有点尴尬,但如果您为 XAML 文本编辑器选择了Visual Studio 选项“在属性之间保留新行和空格”,则自动重新格式化不会更改此格式:

    【讨论】:

      【解决方案4】:

      我编写了一个附加属性来“绕过”这种行为。

      public class TextBlockExtension
      {
      
          public static bool GetRemoveEmptyRuns(DependencyObject obj)
          {
              return (bool)obj.GetValue(RemoveEmptyRunsProperty);
          }
      
          public static void SetRemoveEmptyRuns(DependencyObject obj, bool value)
          {
              obj.SetValue(RemoveEmptyRunsProperty, value);
      
              if (value)
              {
                  var tb = obj as TextBlock;
                  if (tb != null)
                  {
                      tb.Loaded += Tb_Loaded;
                  }
                  else
                  {
                      throw new NotSupportedException();
                  }
              }
          }
      
          public static readonly DependencyProperty RemoveEmptyRunsProperty =
              DependencyProperty.RegisterAttached("RemoveEmptyRuns", typeof(bool), 
                  typeof(TextBlock), new PropertyMetadata(false));
      
          public static bool GetPreserveSpace(DependencyObject obj)
          {
              return (bool)obj.GetValue(PreserveSpaceProperty);
          }
      
          public static void SetPreserveSpace(DependencyObject obj, bool value)
          {
              obj.SetValue(PreserveSpaceProperty, value);
          }
      
          public static readonly DependencyProperty PreserveSpaceProperty =
              DependencyProperty.RegisterAttached("PreserveSpace", typeof(bool), 
                  typeof(Run), new PropertyMetadata(false));
      
      
          private static void Tb_Loaded(object sender, RoutedEventArgs e)
          {
              var tb = sender as TextBlock;
              tb.Loaded -= Tb_Loaded;
      
             var spaces = tb.Inlines.Where(a => a is Run 
                  && string.IsNullOrWhiteSpace(((Run)a).Text) 
                  && !GetPreserveSpace(a)).ToList();
              spaces.ForEach(s => tb.Inlines.Remove(s));
          }
      }
      

      整个源代码及其解释都可以在here找到。 通过使用这个附加属性,您可以按照您想要的方式保持您的 XAML 格式,但您不会在呈现的 XAML 中获得这些空格。

      【讨论】:

        【解决方案5】:

        我的解决方案是使默认字体大小几乎不可见(FontSize="1"),然后将每个&lt;Run 的字体大小设置为所需的大小:

        <TextBlock HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="1"
                   FontFamily="Arial"
                   Foreground="#414141">        
        
            <Run FontSize="10" Text="{Binding LoadsCount}" />        
            <Run FontSize="10" Text="+" />        
            <Run FontSize="10" Text="{Binding BrokerLoadsCount}" />
        </TextBlock>
        

        您最好在 Code Behind 中执行此操作。我已经尝试过以前的解决方案,但在某些情况下,VS 只是格式化了仔细缩进的代码。

        【讨论】:

          【解决方案6】:

          我将 Pieter 的附加属性移植到 WPF(我认为它是用于 UWP)。

          例子:

          <StackPanel>
              <TextBlock Text="Before:" FontWeight="SemiBold"/>
              <TextBlock>
                  Foo
                  <Run Text="Bar"/>
                  <Run>Baz</Run>
              </TextBlock>
              <TextBlock Text="After:" FontWeight="SemiBold" Margin="0,10,0,0"/>
              <TextBlock local:TextBlockHelper.TrimRuns="True">
                  Foo
                  <Run Text="Bar"/>
                  <Run>Baz</Run>
              </TextBlock>
              <TextBlock Text="Use two spaces if you want one:" FontWeight="SemiBold" Margin="0,10,0,0"/>
              <TextBlock local:TextBlockHelper.TrimRuns="True">
                  Foo
                  <Run Text="  Bar"/>
                  <Run>Baz</Run>
              </TextBlock>
          </StackPanel>
          

          using System;
          using System.Linq;
          using System.Text.RegularExpressions;
          using System.Windows;
          using System.Windows.Controls;
          using System.Windows.Documents;
          
          public class TextBlockHelper
          {
              public static bool GetTrimRuns(TextBlock textBlock) => (bool)textBlock.GetValue(TrimRunsProperty);
              public static void SetTrimRuns(TextBlock textBlock, bool value) => textBlock.SetValue(TrimRunsProperty, value);
          
              public static readonly DependencyProperty TrimRunsProperty =
                  DependencyProperty.RegisterAttached("TrimRuns", typeof(bool), typeof(TextBlockHelper),
                      new PropertyMetadata(false, OnTrimRunsChanged));
          
              private static void OnTrimRunsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  var textBlock = d as TextBlock;
                  textBlock.Loaded += OnTextBlockLoaded;
              }
          
              static void OnTextBlockLoaded(object sender, EventArgs args)
              {
                  var textBlock = sender as TextBlock;
                  textBlock.Loaded -= OnTextBlockLoaded;
          
                  var runs = textBlock.Inlines.OfType<Run>().ToList();
                  foreach (var run in runs)
                      run.Text = TrimOne(run.Text);
              }
          
              private static string TrimOne(string text)
              {
                  if (text.FirstOrDefault() == ' ')
                      text = text.Substring(1);
                  if (text.LastOrDefault() == ' ')
                      text = text.Substring(0, text.Length - 1);
          
                  return text;
              }
          }
          

          【讨论】:

          • 您的解决方案的另一个更新。摆脱TrimOne 方法并在OnTextBlockLoaded 中使用这个foreach:foreach (var run in runs.Where(run =&gt; string.IsNullOrWhiteSpace(run.Text))) run.Text = string.Empty; 它只会刷新空白运行并且不会删除您自己的空格。 :)
          • @Genotypek,这似乎并没有删除“Foo”之后不需要的空间。如果所有文本都在 &lt;Run&gt;s 中,它应该可以工作。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多