2009-11-18 38 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中轻松完成它事件,但按照您的方法,只要点击突出显示的文本,就可以触发某些事件。 如果您需要进一步的要求,请让我知道。最好的, 巴拉。 –