开发者

FlowDocumentPageViewer SearchText and Highlighting

开发者 https://www.devze.com 2022-12-12 07:42 出处:网络
I am creating asearch and highlighting the text using the FlowDocumentPageViewer, something similar to the link given.

I am creating a search and highlighting the text using the FlowDocumentPageViewer, something similar to the link given.

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

When I search for Tokens of string, (using a list of string) everything works fine and I get my rectangle applied appropriately. But I have two problems here,

  1. When I change the pages of the FlowDocumentPageViewer, my Rectangular highlighted area remains the same and it is not sinking with the Text.

  2. When I zoom in or zoom out of the FlowDocumentPageViewer, the text gets zoomed but the Highlight rectangle remains in the same position,

Can you please help me in resolving this problem such that the rectangle gets applied to the Text itself. I am posing my application here. Please let me know still if you need further information.

    <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; }
    }
}

Best,

Bala.


What would appear to be an easy solution is to actually edit the document to do the hilighting as you search, remembering to edit it back when you clear the search results or need the unedited document for other purposes (such as to save it).

You can search a document for text using the code I posted in this answer. In your case, instead of setting the selection to each range found, you'll want to hilight the range like this:

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;
  }
}

To complete the picture, you'll need a way to clear the hilighting when you're done. Several options:

  1. Save the ranges that were hilighted so you can just use ApplyPropertyValue to apply Brushes.Transparent background (simple, but this will erase any background that was previously set)

  2. Split each range that was found into runs and save the lists of runs each with its original background color, so it will be easy to restore

  3. Save the entire document before you muck with it and just restore it to remove the hilighting

  4. If the document is in a RichTextBox and the user won't be allowed to edit the document, you can simply use the undo mechanism. Make sure your TextBoxBase.UndoLimit is increased as you do the hilighting, then to remove the hilighting just call TextBoxBase.Undo() once per hilight.


Try to this solution http://rredcat.blogspot.com/2009/11/zoom-and-page-chahged-events-for.html

0

精彩评论

暂无评论...
验证码 换一张
取 消