2009-11-18 1 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; 
    } 
} 

Чтобы завершить картину, вам нужен способ, чтобы очистить hilighting когда вы» сделанный. Несколько вариантов:

  1. Сохранить диапазоны, которые были hilighted, так что вы можете просто использовать ApplyPropertyValue применять Brushes.Transparent фон (простой, но это сотрет любой фон, который ранее был установлен)

  2. Разделить каждый диапазон который был найден в прогонов и сохранить списки пробегов каждый со своим оригинальным цветом фона, так что это будет легко восстановить

  3. сохранить весь документ, прежде чем сбросить с ним и просто восстановить его, чтобы удалить hilighting

  4. Если документ находится в RichTextBox, и пользователю не разрешается редактировать документ, вы можете просто использовать механизм отмены. Удостоверьтесь, что ваш TextBoxBase.UndoLimit увеличен по мере того, как вы делаете hilighting, а затем, чтобы удалить hilighting, просто вызовите TextBoxBase.Undo() один раз за hilight.

+0

Привет Ray, Спасибо за помощь, в соответствии с вашими комментариями, я могу выделить выделенный текст и всё отлично работает. Но причина, по которой я использую Rectangle (в шаблоне ItemsControl) для выделения выделенного текста, мне нужно запустить событие, когда я нажимаю выделенный текст. Если я использую Rectangle, я могу легко сделать это в MouseDown событие, но в соответствии с вашим подходом можно вызвать некоторые события, как только я нажму на выделенный текст. Пожалуйста, дайте мне знать, если вам нужны дополнительные требования. Лучший, Bala. –

 Смежные вопросы

  • Нет связанных вопросов^_^