2009-04-15 154 views
11

我有TextBlock,它具有動態添加到它的Inline(基本上一堆斜體或粗體的Run對象)。WPF TextBlock根據搜索條件突出顯示某些部分

在我的應用程序中我有搜索功能。

我希望能夠突出顯示正在搜索的TextBlock文本。

通過突出顯示我的意思是更改TextBlock文本顏色的某些部分(請記住,它可能一次突出顯示幾個不同的Run對象)。

我已經試過了這個例子http://blogs.microsoft.co.il/blogs/tamir/archive/2008/05/12/search-and-highlight-any-text-on-wpf-rendered-page.aspx

但它的接縫非常不穩定:(

有沒有簡單的方法來解決這個問題?

回答

0

最後寫下面的代碼

在此刻有一些錯誤,但是解決問題

if (Main.IsFullTextSearch) 
{ 
    for (int i = 0; i < runs.Count; i++) 
    { 
     if (runs[i] is Run) 
     { 
      Run originalRun = (Run)runs[i]; 

      if (Main.SearchCondition != null && originalRun.Text.ToLower() 
       .Contains(Main.SearchCondition.ToLower())) 
      { 
       int pos = originalRun.Text.ToLower() 
          .IndexOf(Main.SearchCondition.ToLower()); 

       if (pos > 0) 
       { 
        Run preRun = CloneRun(originalRun); 
        Run postRun = CloneRun(originalRun); 

        preRun.Text = originalRun.Text.Substring(0, pos); 
        postRun.Text = originalRun.Text 
         .Substring(pos + Main.SearchCondition.Length); 

        runs.Insert(i - 1 < 0 ? 0 : i - 1, preRun); 
        runs.Insert(i + 1, new Run(" ")); 
        runs.Insert(i + 2, postRun); 

        originalRun.Text = originalRun.Text 
         .Substring(pos, Main.SearchCondition.Length); 

        SolidColorBrush brush = new SolidColorBrush(Colors.Yellow); 
        originalRun.Background = brush; 

        i += 3; 
       } 
      } 
     } 
    } 
} 
+0

確實,您可以發佈完整的代碼或鏈接? – 2010-12-18 22:45:40

2

我有一個類似的問題 - 試圖實施過主持人,基本上代表了報告的負載文本搜索。該報告最初寫入一個字符串,我們利用FlowDocumentViewer的內置ctrl-F - 它不是很好,並有一些更奇怪的選擇,但足夠。

如果你只是想這樣的事情,你可以做到以下幾點:

 <FlowDocumentScrollViewer> 
      <FlowDocument> 
       <Paragraph FontFamily="Lucida Console" FontSize="12"> 
        <Run Text="{Binding Content, Mode=OneWay}"/> 
       </Paragraph> 
      </FlowDocument> 
     </FlowDocumentScrollViewer> 

我們決定去一個重寫的報告保持同步與程序的其餘部分,基本上每個編輯更改它每次都不得不重新創建整個報告意味着這很慢。我們希望通過轉向更新所需要的模型來改進這一點,但需要使視圖模型(而不僅僅是一個字符串)能夠以一種理智的方式來實現!我們希望在交換報告之前保留搜索功能,並更好地突出顯示一種顏色的「當前」搜索位置和另一種顏色的其他搜索位置。

這裏是我的解決方案的簡化版本;一個派生自TextBlock的類,它添加了Type HighlightingInformation的依賴項屬性。我沒有包含命名空間和用途,因爲它們很敏感。

public class HighlightingTextBlock : TextBlock 
{ 
    public static readonly DependencyProperty HighlightingProperty = 
     DependencyProperty.Register("Highlighting", typeof (HighlightingInformation), typeof (HighlightingTextBlock)); 

    public HighlightingInformation Highlighting 
    { 
     get { return (HighlightingInformation)GetValue(HighlightingProperty); } 
     set { SetValue(HighlightingProperty, value); } 
    } 

    public HighlightingTextBlock() 
    { 
     AddValueChangedCallBackTo(HighlightingProperty, UpdateText); 
    } 

    private void AddValueChangedCallBackTo(DependencyProperty property, Action updateAction) 
    { 
     var descriptor = DescriptorFor(property); 
     descriptor.AddValueChanged(this, (src, args) => updateAction()); 
    } 

    private DependencyPropertyDescriptor DescriptorFor(DependencyProperty property) 
    { 
     return DependencyPropertyDescriptor.FromProperty(property, GetType()); 
    } 

    private void UpdateText() 
    { 
     var highlighting = Highlighting; 
     if (highlighting == null) 
      return; 
     highlighting.SetUpdateMethod(UpdateText); 

     var runs = highlighting.Runs; 
     Inlines.Clear(); 
     Inlines.AddRange(runs); 
    } 
} 

類型這個類可以綁定到使用update方法時,它的文本和亮點列表更改爲更新運行的列表。亮點自己是這個樣子:

public class Highlight 
{ 
    private readonly int _length; 
    private readonly Brush _colour; 

    public int Start { get; private set; } 

    public Highlight(int start, int length,Brush colour) 
    { 
     Start = start; 
     _length = length; 
     _colour = colour; 
    } 

    private string TextFrom(string currentText) 
    { 
     return currentText.Substring(Start, _length); 
    } 

    public Run RunFrom(string currentText) 
    { 
     return new Run(TextFrom(currentText)){Background = _colour}; 
    } 
} 

要產生亮點的正確收集是一個單獨的問題,我基本上是處理主持人的樹,你遞歸搜索內容的集合解決 - 葉節點那些有內容和其他節點的孩子只有孩子。如果您搜索深度優先,您會得到您所期望的訂單。然後,您可以基本上在結果​​列表中編寫一個包裝來跟蹤位置。我不會爲此發佈所有代碼 - 我的迴應是在這裏記錄如何讓WPF以MVP風格進行多色突出顯示。

我沒有在這裏使用INotifyPropertyChanged或CollectionChanged,因爲我們不需要這些更改是多播(例如一個演示者有多個視圖)。最初,我嘗試通過爲文本添加事件更改通知併爲列表添加一個通知(您還必須手動訂閱INotifyCollectionChanged事件)。然而,我對事件描述中的內存泄漏表示擔憂,而且文本和亮點的更新並不是同時發生,這使得它成爲問題。

這種方法的一個缺點是人們不應該綁定到該控件的Text屬性。在真實版本中,我添加了一些檢查+異常拋出來阻止人們這樣做,但爲了清晰起見,從示例中省略了它!

5

我拿了dthrasers answer,並沒有使用XML解析器。他在解釋his blog中的每一部分都做得很好,但是這並不要求我添加任何額外的庫,下面是我如何做到的。

步驟一,做一個轉換器類:

class StringToXamlConverter : IValueConverter 
    { 

     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      string input = value as string; 
      if (input != null) 
      { 
       var textBlock = new TextBlock(); 
       textBlock.TextWrapping = TextWrapping.Wrap; 
       string escapedXml = SecurityElement.Escape(input); 

       while (escapedXml.IndexOf("|~S~|") != -1) { 
       //up to |~S~| is normal 
       textBlock.Inlines.Add(new Run(escapedXml.Substring(0, escapedXml.IndexOf("|~S~|")))); 
       //between |~S~| and |~E~| is highlighted 
       textBlock.Inlines.Add(new Run(escapedXml.Substring(escapedXml.IndexOf("|~S~|") + 5, 
              escapedXml.IndexOf("|~E~|") - (escapedXml.IndexOf("|~S~|") + 5))) 
              { FontWeight = FontWeights.Bold, Background= Brushes.Yellow }); 
       //the rest of the string (after the |~E~|) 
       escapedXml = escapedXml.Substring(escapedXml.IndexOf("|~E~|") + 5); 
       } 

       if (escapedXml.Length > 0) 
       { 
        textBlock.Inlines.Add(new Run(escapedXml));      
       } 
       return textBlock; 
      } 

      return null; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      throw new NotImplementedException("This converter cannot be used in two-way binding."); 
     } 

    } 

第二步: 相反TextBlock的使用內文。通過在字符串中(你的使用你的文本塊)的內容塊,像這樣:

<ContentControl 
       Margin="7,0,0,0" 
       HorizontalAlignment="Left" 
       VerticalAlignment="Center" 
       Content="{Binding Description, Converter={StaticResource CONVERTERS_StringToXaml}, Mode=OneTime}"> 
</ContentControl> 

第三步: 確保你通過測試標記化與|~S~||~E~|。讓突出顯示開始吧!

注:
你可以改變風格在運行,以確定什麼和如何你的文本被高亮
確保你把你的轉換器類,以您的命名空間和資源。這可能也需要重建才能正常工作。

0

如果你正在處理ContainerContentChanging您ListViewBase,你可以採取以下方法:TextBlock highlighting for WinRT/ContainerContentChanging

請注意,此代碼是Windows RT。 WPF語法稍有不同。另請注意,如果您使用綁定來填充TextBlock.Text屬性,則我的方法生成的文本將被覆蓋。我使用ContainerContentChanging來填充目標字段,因爲與正常綁定相比,性能大大提高並且內存使用量有所提高。我只使用綁定來管理源數據,而不是數據視圖。

2

奇怪的巧合,我最近寫了一篇文章,解決了同樣的問題。這是一個自定義控件,它具有與TextBlock相同的屬性(因此,無論您需要它,您都可以交換TextBlock),並且它還有一個可以綁定到的名稱爲HighLightText的附加屬性,以及HighLightText的值爲發現主要Text屬性(不區分大小寫),它突出顯示。

這是一個相當直接的控制,以創建,你可以在這裏找到相應的文章:

WPF TextBlock With Search String Matching

而且完整的代碼如下解決方案:

HighlightSearchMatchTextBlock (GitHub)

1

以下是我通過構建現有的TextBlock並添加一個名爲SearchText的新依賴項屬性:

public class SearchHightlightTextBlock : TextBlock 
{ 
    public SearchHightlightTextBlock() : base() { } 

    public String SearchText { get { return (String)GetValue(SearchTextProperty); } 
           set { SetValue(SearchTextProperty, value); } }  

    private static void OnDataChanged(DependencyObject source, 
             DependencyPropertyChangedEventArgs e) 
    { 
     TextBlock tb = (TextBlock)source; 

     if (tb.Text.Length == 0) 
      return; 

     string textUpper = tb.Text.ToUpper(); 
     String toFind = ((String) e.NewValue).ToUpper(); 
     int firstIndex = textUpper.IndexOf(toFind); 
     String firstStr = tb.Text.Substring(0, firstIndex); 
     String foundStr = tb.Text.Substring(firstIndex, toFind.Length); 
     String endStr = tb.Text.Substring(firstIndex + toFind.Length, 
             tb.Text.Length - (firstIndex + toFind.Length)); 

     tb.Inlines.Clear(); 
     var run = new Run(); 
     run.Text = firstStr; 
     tb.Inlines.Add(run); 
     run = new Run(); 
     run.Background = Brushes.Yellow; 
     run.Text = foundStr; 
     tb.Inlines.Add(run); 
     run = new Run(); 
     run.Text = endStr; 

     tb.Inlines.Add(run); 
    } 

    public static readonly DependencyProperty SearchTextProperty = 
     DependencyProperty.Register("SearchText", 
            typeof(String), 
            typeof(SearchHightlightTextBlock), 
            new FrameworkPropertyMetadata(null, OnDataChanged)); 
} 

而且在您看來,這樣的:

<view:SearchHightlightTextBlock SearchText="{Binding TextPropertyContainingTextToSearch}" 
           Text="{Binding YourTextProperty}"/> 
1

在這裏,我提出了高亮顯示文本的另一種方法。我有一個用例,我需要在WPF中裝飾一堆C#代碼,但是我不想使用textBlock.Inlines.Add類型的語法,而是想動態生成突出顯示的XAML,然後動態添加它到WPF中的Canvas或其他容器。

所以假設你要上色下面的一段代碼,也突出了它的一部分:

public static void TestLoop(int count) 
{ 
    for(int i=0;i<count;i++) 
    Console.WriteLine(i); 
} 

假設上面的代碼在一個名爲Test.txt的文件中找到。 假設您想用藍色着色所有C#關鍵字(public,static,void等)和簡單類型(int,string),Console.WriteLine用黃色突出顯示。

步驟0。創建新的WPF應用程序和包括在名爲Test.txt的

步驟1.創建代碼熒光筆類文件類似於上述一些示例代碼:

using System.IO; 
using System.Text; 

public enum HighLightType 
{ 
    Type = 0, 
    Keyword = 1, 
    CustomTerm = 2 
} 

public class CodeHighlighter 
{ 
    public static string[] KeyWords = { "public", "static", "void", "return", "while", "for", "if" }; 
    public static string[] Types = { "string", "int", "double", "long" }; 

    private string FormatCodeInXaml(string code, bool withLineBreak) 
    { 
     string[] mapAr = { "<","&lt;" , //Replace less than sign 
          ">","&gt;" }; //Replace greater than sign 
     StringBuilder sb = new StringBuilder(); 

     using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code)))) 
     { 
      while (!sr.EndOfStream) 
      { 
       string line = sr.ReadLine(); 

       line = line.Replace("\t", "&#160;&#160;&#160;&#160;"); //Replace tabs 
       line = line.Replace(" ", "&#160;"); //Replace spaces 

       for (int i = 0; i < mapAr.Length; i += 2) 
        line = line.Replace(mapAr[i], mapAr[i + 1]); 

       if (withLineBreak) 
        sb.AppendLine(line + "<LineBreak/>"); //Replace line breaks 
       else 
        sb.AppendLine(line); 
      } 

     } 
     return sb.ToString(); 
    } 


    private string BuildForegroundTag(string highlightText, string color) 
    { 
     return "<Span Foreground=\"" + color + "\">" + highlightText + "</Span>"; 
    } 

    private string BuildBackgroundTag(string highlightText, string color) 
    { 
     return "<Span Background=\"" + color + "\">" + highlightText + "</Span>"; 
    } 

    private string HighlightTerm(HighLightType type, string term, string line) 
    { 
     if (term == string.Empty) 
      return line; 

     string keywordColor = "Blue"; 
     string typeColor = "Blue"; 
     string statementColor = "Yellow"; 

     if (type == HighLightType.Type) 
      return line.Replace(term, BuildForegroundTag(term, typeColor)); 
     if (type == HighLightType.Keyword) 
      return line.Replace(term, BuildForegroundTag(term, keywordColor)); 
     if (type == HighLightType.CustomTerm) 
      return line.Replace(term, BuildBackgroundTag(term, statementColor)); 

     return line; 
    } 

    public string ApplyHighlights(string code, string customTerm) 
    { 
     code = FormatCodeInXaml(code, true); 
     customTerm = FormatCodeInXaml(customTerm, false).Trim(); 

     StringBuilder sb = new StringBuilder(); 
     using (StreamReader sr = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(code)))) 
     { 
      while (!sr.EndOfStream) 
      { 
       string line = sr.ReadLine(); 

       line = HighlightTerm(HighLightType.CustomTerm, customTerm, line); 

       foreach (string keyWord in KeyWords) 
        line = HighlightTerm(HighLightType.Keyword, keyWord, line); 

       foreach (string type in Types) 
        line = HighlightTerm(HighLightType.Type, type, line); 

       sb.AppendLine(line); 
      } 
     } 

     return sb.ToString(); 

    } 
} 

步驟2。畫布XAML標記添加到您的MainWindow.xaml

<Window x:Class="TestCodeVisualizer.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:TestCodeVisualizer" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 

    <Canvas Name="canvas" /> 
</Window> 

第3步:在WPF應用程序中添加以下代碼:(確保test.txt的是在正確的位置):

using System.Text; 
using System.IO; 
using System.Windows; 
using System.Windows.Markup; 

namespace TestCodeVisualizer 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      string testText = File.ReadAllText("Test.txt"); 
      FrameworkElement fe = GenerateHighlightedTextBlock(testText, "Console.WriteLine"); 
      this.canvas.Children.Add(fe); 
     } 


     private FrameworkElement GenerateHighlightedTextBlock(string code, string term) 
     { 
      CodeHighlighter ch = new CodeHighlighter(); 
      string uc = "<UserControl xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>[CONTENT]</UserControl>"; 

      string content = "<TextBlock>" + ch.ApplyHighlights(code, term) + "</TextBlock>"; 
      uc = uc.Replace("[CONTENT]", content); 

      FrameworkElement fe = XamlReader.Load(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(uc))) as FrameworkElement; 
      return fe; 
     } 

    } 
}