在WPF textBlock中使用缩进包装文本

时间:2015-06-10 09:28:23

标签: wpf xaml word-wrap textblock

我使用列表框显示带有数据模板的撤消重做列表:

<ListBox x:Name="actionList"
           Height="150"
           HorizontalAlignment="Stretch"
           VerticalAlignment="Stretch"
           MouseMove="ListBoxMouseMove"
           ScrollViewer.VerticalScrollBarVisibility="Visible"
           SelectionMode="Extended"
           Style="{StaticResource CustomListBoxStyle}">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <TextBlock Width="235"
                   HorizontalAlignment="Stretch"
                   VerticalAlignment="Stretch"
                   FontSize="11"
                   Text="{Binding DisplayText}"
                   TextWrapping="Wrap" />
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>

它按预期工作,但是对于长撤消重做字符串,会发生换行但它与行的第一个字符对齐。我们希望它缩进一点,以清楚地识别两个列表项。说明如下:

Word wrap desired functionality

我们怎样才能达到同样目的。

2 个答案:

答案 0 :(得分:2)

我找到了使用转换器的解决方法:

<ListBox x:Name="actionList"
               Height="150"
               HorizontalAlignment="Stretch"
               VerticalAlignment="Stretch"
               MouseMove="ListBoxMouseMove"
               ScrollViewer.VerticalScrollBarVisibility="Visible"
               SelectionMode="Extended"
               Style="{StaticResource CustomListBoxStyle}">
        <ListBox.ItemTemplate>
          <DataTemplate>
            <TextBlock x:Name="textBlock"
                       Width="235"
                       HorizontalAlignment="Stretch"
                       VerticalAlignment="Stretch"
                       FontSize="11"
                       Text="{Binding DisplayText,
                                      Converter={StaticResource indentWrapConverter},
                                      ConverterParameter=235}"
                       TextWrapping="Wrap"/>
          </DataTemplate>
        </ListBox.ItemTemplate>
      </ListBox>

转换器类:

namespace XXXX.Converters
{
  using System;
  using System.Text;
  using System.Globalization;
  using System.Windows.Data;

  /// <summary>
  /// Wrap converter for text block to indent wrapped line a bit to identify 
  /// it correctly
  /// </summary>
  public class IndentWrapConverter : IValueConverter
  {
    #region Implementation of IValueConverter

    /// <summary>
    /// Wrap the passed-in text as per passed-in textblock width, after warpping it
    /// adds whitepsaces to indent the next lines
    /// </summary>
    /// <returns>
    /// A converted value. If the method returns null, the valid null value is used.
    /// </returns>
    /// <param name="value">The value produced by the binding source.</param><param name="targetType">The type of the binding target property.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
    public object Convert(object value,
                          Type targetType,
                          object parameter,
                          CultureInfo culture)
    {
      // get the string of textblock
      var stringData = value as string;

      // if string cant be parsed retrurn null
      if (stringData == null)
      {
        return null;
      }

      // get textblock width
      var width = 235.0;
      double.TryParse(parameter.ToString(),
                      out width);

      // get actual length by using text block width divide by font size
      int length = (int)Math.Round((2 * width / 11));

      // if text length is less than calulated length then return string as it is
      // else wrap the text
      return  stringData.Length > length 
             ? WordWrap(stringData,
                        length)
             : stringData;
    }



    /// <summary>
    /// Word wraps the given text to fit within the specified width.
    /// </summary>
    /// <param name="text">Text to be word wrapped</param>
    /// <param name="width">Width, in characters, to which the text
    /// should be word wrapped</param>
    /// <returns>The modified text</returns>
    private static string WordWrap(string text,
                                  int width)
    {
      int pos, next;
      StringBuilder sb = new StringBuilder();

      // Lucidity check
      if (width < 1)
        return text;

      // Parse each line of text
      for (pos = 0; pos < text.Length; pos = next)
      {
        // Find end of line
        int eol = text.IndexOf(Environment.NewLine,
                               pos,
                               StringComparison.Ordinal);
        if (eol == -1)
          next = eol = text.Length;
        else
          next = eol + Environment.NewLine.Length;

        // Copy this line of text, breaking into smaller lines as needed
        if (eol > pos)
        {
          do
          {
            int len = eol - pos;
            if (len > width)
              len = BreakLine(text, pos, width);
            sb.Append(text, pos, len);
            if (pos < width)
            {
              sb.Append(Environment.NewLine);
              sb.Append("   ");
            }

            // Trim whitespace following break
            pos += len;
            while (pos < eol && Char.IsWhiteSpace(text[pos]))
              pos++;
          } while (eol > pos);
        }
      }
      return sb.ToString();
    }

    /// <summary>
    /// Locates position to break the given line so as to avoid
    /// breaking words.
    /// </summary>
    /// <param name="text">String that contains line of text</param>
    /// <param name="pos">Index where line of text starts</param>
    /// <param name="max">Maximum line length</param>
    /// <returns>The modified line length</returns>
    private static int BreakLine(string text, int pos, int max)
    {
      // Find last whitespace in line
      int i = max;
      while (i >= 0 && !Char.IsWhiteSpace(text[pos + i]))
        i--;

      // If no whitespace found, break at maximum length
      if (i < 0)
        return max;

      // Find start of whitespace
      while (i >= 0 && Char.IsWhiteSpace(text[pos + i]))
        i--;

      // Return length of text before whitespace
      return i + 1;
    }


    /// <summary>
    /// Converts a value. 
    /// </summary>
    /// <returns>
    /// A converted value. If the method returns null, the valid null value is used.
    /// </returns>
    /// <param name="value">The value that is produced by the binding target.</param><param name="targetType">The type to convert to.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
    public object ConvertBack(object value,
                              Type targetType,
                              object parameter,
                              CultureInfo culture)
    {
      return null;
    }

    #endregion
  }
}

根据需要使用列表框样式和选择。

答案 1 :(得分:1)

他们“更容易”地执行此操作可能需要使用段落对象来表示文本。

段落本身支持使用 TextIndent 等属性进行缩进(控制第一行缩进,您可以将其设置为负值)或保证金(为整体设置边距)段落,但尊重第一行缩进)。

<ListBox.ItemTemplate>
    <DataTemplate>
        // IsHitTestVisible is set to false to avoid FlowDocument's built-in text selection
        //     from disrupting the regular ListBox mouse selection behavior
        <Grid IsHitTestVisible="False">
            <FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
                                      ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                 <FlowDocument FontSize="12"
                               FontFamily="Calibri" 
                               Foreground="Black" 
                               PagePadding="0">
                     <Paragraph TextIndent="-10" 
                                Margin="10,0,0,0">
                         <Run Text="{Binding ., Mode=OneWay}" />
                     </Paragraph>
                 </FlowDocument>
            </FlowDocumentScrollViewer>
        </Grid>
    </DataTemplate>
</ListBox.ItemTemplate>