WPF TextBox 更改文本的背景颜色而不是整个 TextBox

WPF TextBox change background color of text and not whole TextBox

我想知道是否有办法更改 TextBox 内而不是整个 TextBox 内文本的背景颜色。 就像你 highlight/select 文本一样。

TextBox 通常不支持彩色文本或富文本格式。根据您的情况,您必须使用 TextBlockRichtextBox.

文本块

您可以直接处理文本元素:

<TextBlock>
  <Run Text="This is some" />
  <Run Text="red"
       Background="Red" />
  <Run Text="text." />
</TextBlock>

或者如果您需要查找特定文本,请处理文本指针:

<TextBlock x:Name="ColouredTextBlock" />
private void OnLoaded(object sender, EventArgs e)
{
  var text = "This is some red text.";
  var highlightText = "red";

  this.ColouredTextBlock.Text = text;

  int highlightTextIndex = this.ColouredTextbox.Text.IndexOf(highlightText);
  TextPointer textStartPointer = this.ColouredTextbox.ContentStart.DocumentStart.GetInsertionPosition(LogicalDirection.Forward);
  var highlightTextRange = new TextRange(textStartPointer.GetPositionAtOffset(highlightTextIndex), textStartPointer.GetPositionAtOffset(highlightTextIndex + highlightText.Length));
  highlightTextRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Red);
}

要使突出显示动态化,您可以使用 MultiBinding 通过 text-to-Inline 转换器创建内联元素。由于我们不能直接绑定到 TextBlock.Inlines 属性,我们使用 TextBlock.Text 属性 作为虚拟绑定目标:

HighlightInfo.cs

public struct HighlightInfo
{
  /// <summary>
  ///  Set Range parameter: inclusive start index and exclusive end index
  /// </summary>
  /// <param name="highlightRange">inclusive start index and exclusive end index</param>
  public HighlightInfo(Range highlightRange, Color foreground, Color background)
  {
    HighlightRange = highlightRange;
    Foreground = foreground;
    Background = background;
  }

  public System.Range HighlightRange { get; }
  public int HighlightRangeLength => this.HighlightRange.End.Value - this.HighlightRange.Start.Value;
  public Color Foreground { get; }
  public Color Background { get; }
}

TextToInlineConverter.cs

public class TextToInlineConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    string sourceText = values.OfType<string>().First();
    if (string.IsNullOrWhiteSpace(sourceText))
    {
      return Binding.DoNothing;
    }

    TextBlock textBlock = values.OfType<TextBlock>().First();

    IEnumerable<HighlightInfo> highlightInfos = values.OfType<IEnumerable<HighlightInfo>>().First();
    List<HighlightInfo> sortedHighlightInfos = highlightInfos
      .OrderBy(highlightInfo => highlightInfo.HighlightRange.Start.Value)
      .ToList();

    int highlightStartIndex = sortedHighlightInfos.FirstOrDefault().HighlightRange.Start.Value;
    bool hasPreccedingNonHighlightText = highlightStartIndex > 0;
    if (hasPreccedingNonHighlightText)
    {
      string preceedingText = sourceText.Substring(0, highlightStartIndex);
      textBlock.Inlines.Add(preceedingText);
    }

    CreateHighlightTextElements(sourceText, textBlock, sortedHighlightInfos);

    int highlightEndIndex = sortedHighlightInfos.LastOrDefault().HighlightRange.End.Value;
    bool hasTrailingNonHighlightText = highlightEndIndex < sourceText.Length;
    if (hasTrailingNonHighlightText)
    {
      string trailingText = sourceText.Substring(highlightEndIndex);
      textBlock.Inlines.Add(trailingText);
    }

    // We are binding to the 'TextBlock.Text' property as a dummy target, so we don't want to set it.
    // We have already modified the 'TextBlcok.Inlines' collection.
    return Binding.DoNothing;
  }

  private void CreateHighlightTextElements(string sourceText, TextBlock textBlock, List<HighlightInfo> sortedHighlightInfos)
  {
    for (int index = 0; index < sortedHighlightInfos.Count; index++)
    {
      HighlightInfo highlightInfo = sortedHighlightInfos[index];
      var highlightText = new Run(sourceText.Substring(highlightInfo.HighlightRange.Start.Value, highlightInfo.HighlightRangeLength))
      {
        Foreground = new SolidColorBrush(highlightInfo.Foreground),
        Background = new SolidColorBrush(highlightInfo.Background),
      };
      textBlock.Inlines.Add(highlightText);
      if (index + 1 < sortedHighlightInfos.Count)
      {
        HighlightInfo nextHighlightInfo = sortedHighlightInfos[index + 1];
        var nonHighlightTextRangeLength = nextHighlightInfo.HighlightRange.Start.Value - highlightInfo.HighlightRange.End.Value;
        bool hasEnclosedNonHighlightText = nonHighlightTextRangeLength > 0;
        if (hasEnclosedNonHighlightText)
        {
          highlightText = new Run(sourceText.Substring(highlightInfo.HighlightRange.End.Value, nonHighlightTextRangeLength));
          textBlock.Inlines.Add(highlightText);
        }
      }
    }
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    => throw new NotSupportedException();
}

MainWindow.xaml.cs

partial class MainWIndow : Window
{
  public ObservableCollection<HighlightInfo> HighlightInfos { get; }
  public string TextValue { get; }

  public MainWindow()
  {
    InitializeComponent();

    this.TextValue = "This is some red and orange and bluegreen text.";

    this.HighlightInfos = new ObservableCollection<HighlightInfo>()
    {
      new HighlightInfo(new Range(
        this.TextValue.IndexOf("red"), 
        this.TextValue.IndexOf("red") + "red".Length), 
        Colors.Black, 
        Colors.Red),
      new HighlightInfo(new Range(
        this.TextValue.IndexOf("orange"), 
        this.TextValue.IndexOf("orange") + "orange".Length), 
        Colors.Black, 
        Colors.Orange),
      new HighlightInfo(new Range(
        this.TextValue.IndexOf("blue"), 
        this.TextValue.IndexOf("blue") + "blue".Length), 
        Colors.Black, 
        Colors.Blue),
      new HighlightInfo(new Range(
        this.TextValue.IndexOf("green"), 
        this.TextValue.IndexOf("green") + "green".Length),
        Colors.Black, 
        Colors.Green),
    };
  }
}

MainWindow.xaml

<Window>
  <Window.Resources>
    <local:TextToInlineConverter x:Key="TextToInlineConverter" />
  </Window.Resources>

  <TextBlock>
    <TextBlock.Text>
      <MultiBinding  Converter="{StaticResource TextToInlineConverter}">
        <Binding Path="TextValue" />
        <Binding Path="HighlightInfos" />
        <Binding RelativeSource="{RelativeSource Self}" />
      </MultiBinding>
    </TextBlock.Text>
  </TextBlock>
</Window>

富文本框

如果您需要允许用户输入,则必须使用 RichTextBox:

<RichTextBlock x:Name="ColouredRichTextBox" />
private void OnLoaded(object sender, EventArgs e)
{
  var text = "This is some red text.";
  var highlightText = "red";

  this.ColouredRichTextBox.Document = new FlowDocument(new Paragraph(new Run(text)));

  TextPointer textStartPointer = this.ColouredRichTextBox.Document.ContentStart.DocumentStart.GetInsertionPosition(LogicalDirection.Forward);
  var highlightTextRange = new TextRange(textStartPointer.GetPositionAtOffset(highlightTextIndex), textStartPointer.GetPositionAtOffset(highlightTextIndex + highlightText.Length));
  highlightTextRange .ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Red);
}