【问题标题】:FlowDocumentPageViewer SearchText and HighlightingFlowDocumentPageViewer 搜索文本和突出显示
【发布时间】:2023-04-08 04:10:02
【问题描述】:

我正在创建搜索并使用FlowDocumentPageViewer 突出显示文本,类似于给出的链接。

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

最好的,

巴拉。

【问题讨论】:

    标签: wpf flowdocument


    【解决方案1】:

    似乎一个简单的解决方案是在搜索时实际编辑文档以进行突出显示,记住在清除搜索结果或将未编辑的文档用于其他目的(例如保存它)时将其编辑回来)。

    您可以使用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,然后要删除加亮,只需在每个加亮时调用 TextBoxBase.Undo() 一次。

    【讨论】:

    • 嗨,Ray,感谢您的帮助,根据您的 cmets,我可以突出显示选定的文本,一切正常。但是我使用矩形(在 ItemsControl 模板中)突出显示所选文本的原因是,当我单击突出显示的文本时,我需要触发一个事件,如果我使用矩形,我可以在 MouseDown 事件中轻松完成它,但是根据您的方法,一旦我单击突出显示的文本,就可以触发一些事件。如果您需要进一步的要求,请告诉我。最好的,巴拉。
    【解决方案2】:
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 1970-01-01
    • 2012-06-25
    • 2018-06-01
    相关资源
    最近更新 更多