2009-11-18 61 views
0

我正在創建一個搜索並使用FlowDocumentPageViewer突出顯示文本,類似於給出的鏈接。FlowDocumentPageViewer SearchText和突出顯示

http://kentb.blogspot.com/2009/06/search-and-highlight-text-in-arbitrary.html

當我搜索字符串的令牌,(使用字符串列表)一切工作正常,我得到適當應用我的矩形。不過,我這裏有兩個問題,

  1. 當我改變FlowDocumentPageViewer的頁面,我的矩形高亮區域保持不變,它不是用文字下沉。

  2. 當我放大或縮小FlowDocumentPageViewer的,文本被放大,但突出的矩形保持在相同的位置,

能否請你幫我解決這個問題,即矩形被應用到文本本身。我在這裏提出我的申請。如果您需要更多信息,請讓我知道。

<StackPanel Grid.Row="0" Orientation="Horizontal"> 
     <TextBox x:Name="_searchTextBox" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" Width="200" Height="20" Margin="2"/> 
     <Button x:Name="_searchButton" Height="{Binding Height, ElementName=_searchTextBox}" Width="50" Content="GO"> 
      </Button> 
      <Button x:Name="_listItems" Height="{Binding Height, ElementName=_searchTextBox}" Width="50" Content="List"/> 
     </StackPanel> 

    <Grid Grid.Row="1"> 
     <FlowDocumentPageViewer> 
      <FlowDocument Foreground="Black" FontFamily="Arial"> 
       <Paragraph FontSize="11"> 
        The following details have been details from Amazon to match your initial query.Some of the returned values may have been empty, so have been ommitted from theresults shown here.Also where there have been more than one value returned viathe Amazon Details, these to have beenomitted for the sake of keeping things simplefor this small demo application. Simple is good,when trying to show how something works 
       </Paragraph> 
      </FlowDocument> 
     </FlowDocumentPageViewer> 
    </Grid> 

    <ItemsControl ItemsSource="{Binding SearchRectangles}"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <Canvas/> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
        <Rectangle Fill="#99FFFF00" Width="{Binding Width}" Height="{Binding Height}" Tag="{Binding Text}" MouseDown="Rectangle_MouseDown"> 
         <Rectangle.Style> 
          <Style TargetType="{x:Type Rectangle}"> 
           <Style.Triggers> 
            <Trigger Property="IsMouseOver" Value="True"> 
             <Setter Property="BitmapEffect"> 
              <Setter.Value> 
               <OuterGlowBitmapEffect GlowColor="BurlyWood" GlowSize="7"/> 
              </Setter.Value> 
             </Setter> 
            </Trigger> 
           </Style.Triggers> 
          </Style> 
         </Rectangle.Style> 

        </Rectangle> 
       </DataTemplate> 
     </ItemsControl.ItemTemplate> 
     <ItemsControl.ItemContainerStyle> 
      <Style> 
       <Setter Property="Canvas.Top" Value="{Binding Top}"/> 
       <Setter Property="Canvas.Left" Value="{Binding Left}"/> 
      </Style> 
     </ItemsControl.ItemContainerStyle> 
    </ItemsControl> 

</Grid> 
</Grid> 

public partial class Window1 : Window 
{ 
    public static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register("SearchText", 
              typeof(string), 
              typeof(Window1)); 

    public static readonly DependencyProperty SearchRectanglesProperty = DependencyProperty.Register("SearchRectangles", 
        typeof(ICollection<SearchRectangle>), 
        typeof(Window1)); 



    public IList<string> SearchTokens { get; set; } 


    public Window1() 
    { 
     InitializeComponent(); 

     SearchRectangles = new ObservableCollection<SearchRectangle>(); 

     _searchButton.Click += delegate 
     { 
      DoSearch(); 
     }; 

     _listItems.Click += delegate 
     { 
      SearchTokens = new List<string>(); 
      SearchTokens.Add("been"); 
      SearchTokens.Add("Amazon"); 
      SearchTokens.Add("following");    
      DoSearch(SearchTokens); 

     }; 

     _searchTextBox.KeyDown += delegate(object sender, KeyEventArgs e) 
     { 
      if (e.Key == Key.Enter) 
      { 
       DoSearch(); 
      } 
     }; 
    } 


    public void DoSearch(IList<string> searchTokens) 
    { 

     SearchRectangles.Clear(); 

     if (searchTokens == null) 
      return; 

     foreach (string token in searchTokens) 
     { 
      SearchText = token; 
      DoSearch(); 
     } 
    } 

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

    public ICollection<SearchRectangle> SearchRectangles 
    { 
     get { return GetValue(SearchRectanglesProperty) as ICollection<SearchRectangle>; } 
     set { SetValue(SearchRectanglesProperty, value); } 
    } 

    private void DoSearch() 
    { 
     DoSearch(false); 
    } 

    private void DoSearch(bool clearExisting) 
    { 
     if(clearExisting == true) 
      SearchRectangles.Clear(); 
     if (SearchText.Length == 0) 
     { 
      return; 
     } 

     DoSearch(this); 
    } 

    private void DoSearch(DependencyObject searchIn) 
    { 
     if (searchIn == null) 
     { 
      return; 
     } 

     var contentHost = searchIn as IContentHost; 

     if (contentHost != null) 
     { 
      DoSearch(contentHost as UIElement, contentHost); 
     } 
     else 
     { 
      var documentViewerBase = searchIn as DocumentViewerBase; 

      if (documentViewerBase != null) 
      { 
       //extract the content hosts from the document viewer 
       foreach (var pageView in documentViewerBase.PageViews) 
       { 
        contentHost = pageView.DocumentPage as IContentHost; 

        if (contentHost != null) 
        { 
         DoSearch(documentViewerBase, contentHost); 
        } 
       } 
      } 
     } 

     //recurse through children 
     var childCount = VisualTreeHelper.GetChildrenCount(searchIn); 

     for (var i = 0; i < childCount; ++i) 
     { 
      DoSearch(VisualTreeHelper.GetChild(searchIn, i)); 
     } 
    } 

    private void DoSearch(UIElement uiHost, IContentHost contentHost) 
    { 
     if (uiHost == null) 
     { 
      return; 
     } 

     var textBlock = contentHost as TextBlock; 

     if (textBlock != null) 
     { 
      //this has the side affect of converting any plain string content in the TextBlock into a hosted Run element 
      //that's bad in that it is unexpected, but good in that it allows us to access the hosted elements in a 
      //consistent fashion below, rather than special-casing TextBlocks with text only content 
      var contentStart = textBlock.ContentStart; 
     } 

     var hostedElements = contentHost.HostedElements; 

     while (hostedElements.MoveNext()) 
     { 
      var run = hostedElements.Current as Run; 

      if (run != null && !string.IsNullOrEmpty(run.Text)) 
      { 
       ApplyHighlighting(run.Text, delegate(int start, int length) 
       { 
        var textPointer = run.ContentStart; 
        textPointer = textPointer.GetPositionAtOffset(start, LogicalDirection.Forward); 
        var leftRectangle = textPointer.GetCharacterRect(LogicalDirection.Forward); 
        textPointer = textPointer.GetPositionAtOffset(length, LogicalDirection.Forward); 
        var rightRectangle = textPointer.GetCharacterRect(LogicalDirection.Backward); 
        var rect = new Rect(leftRectangle.TopLeft, rightRectangle.BottomRight); 
        var translatedPoint = uiHost.TranslatePoint(new Point(0, 0), null); 
        rect.Offset(translatedPoint.X, translatedPoint.Y); 
        return rect; 
       }); 

      } 
     } 
    } 

    private void ApplyHighlighting(string text, Func<int, int, Rect> getRectHandler) 
    { 
     var currentIndex = 0; 

     while (true) 
     { 
      var index = text.IndexOf(SearchText, currentIndex, StringComparison.CurrentCultureIgnoreCase); 

      if (index == -1) 
      { 
       return; 
      } 

      var rect = getRectHandler(index, SearchText.Length); 

      if (rect != Rect.Empty) 
      { 
       SearchRectangles.Add(new SearchRectangle(rect.Top, rect.Left, rect.Width, rect.Height,SearchText)); 
      } 

      currentIndex = index + SearchText.Length; 
     } 
    } 

    private void Rectangle_MouseDown(object sender, MouseEventArgs e) 
    { 
     Rectangle r = sender as Rectangle; 
     MessageBox.Show(r.Tag.ToString()); 
    } 

    private void FlowDocumentPageViewer_PageViewsChanged(object sender, EventArgs e) 
    { 

    } 

    private void Window_SizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     DoSearch(SearchTokens); 
    } 

} 

public class SearchRectangle 
{ 
    private readonly double _top; 
    private readonly double _left; 
    private readonly double _width; 
    private readonly double _height; 
    private readonly string _text; 

    public SearchRectangle(double top, double left, double width, double height,string text) 
    { 
     _top = top; 
     _left = left; 
     _width = width; 
     _height = height; 
     _text = text; 

    } 

    public string Text 
    { 
     get { return _text; } 
    } 

    public double Top 
    { 
     get { return _top; } 
    } 

    public double Left 
    { 
     get { return _left; } 
    } 

    public double Width 
    { 
     get { return _width; } 
    } 

    public double Height 
    { 
     get { return _height; } 
    } 
} 

最佳,

巴拉。

回答

0

看起來很簡單的解決方案是,在搜索時實際編輯文檔以進行高亮處理,記住在清除搜索結果或將未經編輯的文檔用於其他目的(例如保存它)。

您可以使用code I posted in this answer在文檔中搜索文本。在你的情況,而不是設置的選擇,以發現每個範圍,你要高亮顯示這樣的範圍內:

public void HilightMatches(TextRange searchRange, string searchText) 
{ 
    var start = searchRange.Start; 
    while(start != searchRange.End) 
    { 
    var foundRange = FindTextInRange(
     new TextRange(start, searchRange.End), 
     searchText); 

    if(foundRange==null) return; 
    // Here is the meat... 
    foundRange.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Yellow); 
    start = foundRange.End; 
    } 
} 

要完成的圖片,你需要一種方式,當你」清除高亮顯示了重做。有幾個選項:

  1. 保存被加亮後的範圍,所以你可以只使用ApplyPropertyValue申請Brushes.Transparent背景(簡單,但是這將消除先前設置的任何背景)

  2. 拆分每個範圍被發現到運行和節省運行名單各有其原來的背景顏色,所以這將是很容易恢復

  3. 保存整個文件你用它渣土之前,只是恢復它刪除高亮顯示了

  4. 如果文檔位於RichTextBox中,並且用戶將不被允許編輯文檔,則可以簡單地使用撤消機制。確保你的TextBoxBase.UndoLimit隨着你的光線增加而增加,然後去掉hilighting,每hilight調用一次TextBoxBase.Undo()。

+0

喜雷, 感謝您的幫助,按你的意見,我可以突出顯示選定的文本,一切工作正常。 但是我使用矩形(在ItemsControl模板中)突出顯示所選文本的原因是,我需要在突出顯示的文本上觸發一個事件,如果我使用矩形,我可以在MouseDown中輕鬆完成它事件,但按照您的方法,只要點擊突出顯示的文本,就可以觸發某些事件。 如果您需要進一步的要求,請讓我知道。最好的, 巴拉。 –