I want to remove all hyperlinks located in a RichTextBox but not the runs inside the hyperlinks.
My plan is for each hyperlink:
gather all runs inside it
remove the hyperlink
re-insert the runs
While extracting the runs I face this problem:
If the hyperlink consists of one unformatted run,
I don't get the run but also the surrounding hyperlink.
Please try this code:
Xaml:
<RichTextBox Name="rtb" Grid.Column="0" Grid.Row="0" IsDocumentEnabled="True">
<FlowDocument>
<Paragraph>
<Hyperlink>
<Run>HyperlinkUnformatted</Run>
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink>
<Run>Hyper</Run><Run Background="Yellow">link</Run><Run>Formatted</Run>
</Hyperlink>
</Paragraph>
</FlowDocument>
</RichTextBox>
C#:
// All runs inside the RichTextBox
List<Run> runs = runsGet(rtb.Document.ContentStart, rtb.Document.ContentEnd);
foreach (Run run in runs)
{
TextRange rangeOfRun = new TextRange(run.ContentStart, run.ContentEnd);
string runAsString = rangeToString(rangeOfRun, DataFormats.Xaml);
MessageBox.Show(runAsString);
}
/// <summary>
/// Returns all runs between startPos and endPos.
/// </summary>
private List<Run> runsGet(TextPointer startPos, TextPointer endPos)
{
List<Run> foundRuns = null;
TextPointer currentPos = startPos;
while (currentPos != null && currentPos.CompareTo(endPos) <= 0)
{
Run nextRun = runNextGet(currentPos);
if (nextRun == null) break;
if (nextRun.ContentStart.CompareTo(endPos) <= 0)
{
if (foundRuns == null) foundRuns = new List<Run>();
foundRuns.Add(nextRun);
currentPos = nextRun.ContentEnd.GetNextInsertionPosition(LogicalDirection.Forward);
}
else
{
break;
}
}
return foundRuns;
}
/// <summary>
/// Returns first run located at startPos or behind.
/// </summary>
private Run runNextGet(TextPointer startPos)
{
TextPointer currentPos = startPos;
while (currentPos != null)
{
if (currentPos.Parent is Run)
{
return currentPos.Parent as Run;
}
else
{
currentPos = currentPos.GetNextInsertionPosition(LogicalDirection.Forward);
}
}
return null;
}
/// <summary>
/// Returns a text area as Xaml string (if dataFormat is DataFormats.Xaml).
/// </summary>
private string rangeToString(TextRange range, string dataFormat)
{
using (MemoryStream memStream = new MemoryStream())
{
using (StreamWriter streamWriter = new StreamWriter(memStream))
{
range.Save(memStream, dataFormat);
memStream.Flush();
memStream.Position = 0;
StreamReader streamReader = new StreamReader(memStream);
return streamReader.ReadToEnd();
}
}
}
What you want to do is, for each Hyperlink, copy the Inlines of the Hyperlink into a separate XamlPackage, then paste them back into the FlowDocument, overwriting the Hyperlink itself. Unfortunately, a naive implementation of this will fail because TextRange.Save() "helpfully" expands the selected range to the nearest text insertion boundaries. Since the space between a hyperlink and the beginning of its first inline, being of size zero, is not a valid caret position and thus not a valid insertion position, the text range gets expanded to include the Hyperlink. It is thereby included in the package and gets recreated when pasted back in.
To work around this behavior, you can insert some additional dummy text before and after the existing Inlines:
public static class FlowDocumentHelper
{
public static IEnumerable<DependencyObject> WalkTreeDown(this DependencyObject root)
{
if (root == null)
throw new ArgumentNullException();
yield return root;
foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
foreach (var descendent in child.WalkTreeDown())
yield return descendent;
}
public static IEnumerable<DependencyObject> WalkTreeUp(this DependencyObject child)
{
for (; child != null; child = LogicalTreeHelper.GetParent(child))
yield return child;
}
public static void RemoveHyperlinks(this FlowDocument doc)
{
var allLinks = doc.WalkTreeDown().OfType<Hyperlink>().ToArray();
for (int iLink = allLinks.Length - 1; iLink >= 0; iLink--)
{
var link = allLinks[iLink];
var range = new TextRange(link.ContentStart.GetInsertionPosition(LogicalDirection.Backward), link.ContentEnd.GetInsertionPosition(LogicalDirection.Forward));
var first = link.Inlines.FirstInline;
var last = link.Inlines.LastInline;
link.Inlines.Add(new Run(" "));
link.Inlines.InsertBefore(first, new Run(" "));
var childRange = new TextRange(first.ContentStart, last.ContentEnd);
using (MemoryStream ms = new MemoryStream())
{
string format = DataFormats.XamlPackage;
childRange.Save(ms, format, true);
ms.Seek(0, SeekOrigin.Begin);
range.Load(ms, format);
}
}
}
}
I've tested this with your case and also with a Hyperlink containing an embedded image.
Of course, the underlining and formatting from the Hyperlink itself are removed. You could add them back afterward if you want.
Related
I need to programmatically search pdf with pdfSearchManager and highlight the text. The code can make the search but with no highlight.
Plese see my code and correct me.
PdfSearchManager a = new PdfSearchManager(pdfRenderer1);
a.Reset();
a.MatchWholeWord = true;
a.HighlightAllMatches = true;
MessageBox.Show(a.Search(textBox1.Text).ToString());
As we can see on GitHub (if that is the right PdfSearchManager you're using)
Search() is a boolean so its only return true or false so it won't show you any text with .ToString()
Search() :
/// <summary>
/// Searches for the specified text.
/// </summary>
/// <param name="text">The text to search.</param>
/// <returns>Whether any matches were found.</returns>
It returns: Whether any matches were found
and not : Matches that were found
If you're trying to highlight the matches and not display them as I thought at the beginning then you should try using a.UpdateHighlights() ( I never used PdfSearchManager before but it might work)
From PdfSearchManager source code :
public bool Search(string text)
{
Renderer.Markers.Clear();
if (String.IsNullOrEmpty(text))
{
_matches = null;
_bounds = null;
}
else
{
_matches = Renderer.Document.Search(text, MatchCase, MatchWholeWord);
_bounds = GetAllBounds();
}
_offset = -1;
UpdateHighlights();
return _matches != null && _matches.Items.Count > 0;
}
there is private method UpdateHighlights and AddMatch :
private void UpdateHighlights()
{
Renderer.Markers.Clear();
if (_matches == null)
return;
if (_highlightAllMatches)
{
for (int i = 0; i < _matches.Items.Count; i++)
{
AddMatch(i, i == _offset);
}
}
else if (_offset != -1)
{
AddMatch(_offset, true);
}
}
private void AddMatch(int index, bool current)
{
foreach (var pdfBounds in _bounds[index])
{
var bounds = new RectangleF(
pdfBounds.Bounds.Left - 1,
pdfBounds.Bounds.Top + 1,
pdfBounds.Bounds.Width + 2,
pdfBounds.Bounds.Height - 2
);
var marker = new PdfMarker(
pdfBounds.Page,
bounds,
current ? CurrentMatchColor : MatchColor,
current ? CurrentMatchBorderColor : MatchBorderColor,
current ? CurrentMatchBorderWidth : MatchBorderWidth
);
Renderer.Markers.Add(marker);
}
}
I think the Search method had add all the marker from searched text.
Now, how to make it visible in pdfiumViewer.
Turn out that i am referenced to wrong renderer.
my previous code was :
PdfSearchManager a = new PdfSearchManager(pdfRenderer1);
where pdfRenderer1 is the name of component which i put on the Form
The code should be :
PdfSearchManager a = new PdfSearchManager(pdfViewer1.Renderer);
The highlight searched text is run as expected.
In C# WPF, I have a function to retrieve text from flowdocumentreader:
static string GetText(TextPointer textStart, TextPointer textEnd)
{
StringBuilder output = new StringBuilder();
TextPointer tp = textStart;
while (tp != null && tp.CompareTo(textEnd) < 0)
{
if (tp.GetPointerContext(LogicalDirection.Forward) ==
TextPointerContext.Text)
{
output.Append(tp.GetTextInRun(LogicalDirection.Forward));
}
tp = tp.GetNextContextPosition(LogicalDirection.Forward);
}
return output.ToString();
}
Then I use the function as follow:
string test = GetText(rtb.Document.ContentStart, rtb.Document.ContentEnd);
However, the string "test" ignores all the line breaks, which means "\r\n". It does keep the tab character, "\t".
My question is how to keep all the line breaks? I want to automatically highlight the first sentence of each paragraph, so I need to detect the line break characters, "\r\n".
Thanks in advance for your time.
Update:
I load the .rtf document into flowdocumentreader like this:
if (dlg.FileName.LastIndexOf(".rtf") != -1)
{
paraBodyText.Inlines.Clear();
string temp = File.ReadAllText(dlg.FileName, Encoding.UTF8);
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(temp));
TextRange textRange = new TextRange(flow.ContentStart, flow.ContentEnd);
textRange.Load(stream, DataFormats.Rtf);
myDocumentReader.Document = flow;
stream.Close();
}
Edited
assuming that each paragraph has at least one sentence that ends with a dot, you can use the following code to make the first sentence bold:
List<TextRange> ranges = new List<TextRange>();
foreach (Paragraph p in rtb.Document.Blocks.OfType<Paragraph>())
{
TextPointer pointer = null;
foreach (Run r in p.Inlines.OfType<Run>())
{
int index = r.Text.IndexOf(".");
if (index != -1)
{
pointer = r.ContentStart.GetPositionAtOffset(index);
}
}
if (pointer == null)
continue;
var firsSentence = new TextRange(p.ContentStart, pointer);
ranges.Add(firsSentence);
}
foreach (var r in ranges)
{
r.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
}
I'm currently using the following code as a LineTransformer with an AvalonEdit TextEditor. I want to be able to highlight the current single search result with a selection, however the selection is barely visible because the formatting of the DocumentColorizingTransformer has precedence over showing highlighted text. How do I get the highlighted selection to show instead of or before the formatting?
public class ColorizeSearchResults : DocumentColorizingTransformer {
public ColorizeSearchResults() : base() {
SearchTerm = "";
MatchCase = false;
}
public string SearchTerm { get; set; }
public bool MatchCase { get; set; }
protected override void ColorizeLine(DocumentLine line) {
if (SearchTerm.Length == 0)
return;
int lineStartOffset = line.Offset;
string text = CurrentContext.Document.GetText(line);
int count = 0;
int start = 0;
int index;
while ((index = text.IndexOf(SearchTerm, start, MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) >= 0) {
base.ChangeLinePart(
lineStartOffset + index,
lineStartOffset + index + SearchTerm.Length,
(VisualLineElement element) => {
element.TextRunProperties.SetForegroundBrush(Brushes.White);
element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
});
start = index + 1;
count++;
}
}
}
Example of formatting showing over selection
See http://avalonedit.net/documentation/html/c06e9832-9ef0-4d65-ac2e-11f7ce9c7774.htm for AvalonEdit render flow.
The selection layer is rendered before text. So if the text has background, it overrides the selection background. Fortunately we can set the background to Brush.Transparent (or a mix of the Selection.Brush and your own color).
Solution: I've modified the SelectionColorizer code to reset selection background to transparent:
class SelectionColorizerWithBackground : ColorizingTransformer
{
ICSharpCode.AvalonEdit.Editing.TextArea _textArea;
public SelectionColorizerWithBackground(
ICSharpCode.AvalonEdit.Editing.TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this._textArea = textArea;
}
protected override void Colorize(ITextRunConstructionContext context)
{
int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
int lineEndOffset = context.VisualLine.LastDocumentLine.Offset +
context.VisualLine.LastDocumentLine.TotalLength;
foreach (var segment in _textArea.Selection.Segments)
{
int segmentStart = segment.StartOffset;
if (segmentStart >= lineEndOffset)
continue;
int segmentEnd = segment.EndOffset;
if (segmentEnd <= lineStartOffset)
continue;
int startColumn;
if (segmentStart < lineStartOffset)
startColumn = 0;
else
startColumn = context.VisualLine.ValidateVisualColumn(
segment.StartOffset, segment.StartVisualColumn,
_textArea.Selection.EnableVirtualSpace);
int endColumn;
if (segmentEnd > lineEndOffset)
endColumn =
_textArea.Selection.EnableVirtualSpace
? int.MaxValue
: context.VisualLine
.VisualLengthWithEndOfLineMarker;
else
endColumn = context.VisualLine.ValidateVisualColumn(
segment.EndOffset, segment.EndVisualColumn,
_textArea.Selection.EnableVirtualSpace);
ChangeVisualElements(
startColumn, endColumn,
element => {
element.TextRunProperties.SetBackgroundBrush(
System.Windows.Media.Brushes.Transparent);
if (_textArea.SelectionForeground != null)
{
element.TextRunProperties.SetForegroundBrush(
_textArea.SelectionForeground);
}
});
}
}
}
To use the code you are supposed to do the following:
var lineTransformers = textEditor.TextArea.TextView.LineTransformers;
// Remove the original SelectionColorizer.
// Note: if you have syntax highlighting you need to do something else
// to avoid clearing other colorizers. If too complicated you can skip
// this step but to suffer a 2x performance penalty.
lineTransformers.Clear();
lineTransformers.Add(new ColorizeSearchResults());
lineTransformers.Add(
new SelectionColorizerWithBackground(textEditor.TextArea));
After I've tried my solutions extensively, I'd like to add a few points:
While my other solution above appears to work, you'll have some subpixel artefacts when the rectangles are supposed to be tiled. If that is unacceptable you can implement an IBackgroundRenderer. (That happens to be my chosen solution.) If you want some code you may request here, but I doubt whether it will be useful.
BTW, since your question is about search result, most likely you can use https://github.com/icsharpcode/AvalonEdit/blob/697ff0d38c95c9e5a536fbc05ae2307ec9ef2a63/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs unmodified (or modify it if you don't want the rounded borders).
You may use element.BackgroundBrush = Brushes.Magenta; instead of element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);. AvalonEdit appears to draw the background with a rectangle with 3px radius.
There is also a RichTextColorizer starting from AvalonEdit 5.01. I don't know how to use it though because it is not referenced in other files. And the (most likely unwanted) rounded rectangles in the previous paragraph are likely to be present.
So here's my final product based almost entirely off of the existing AvalonEdit SearchResultBackgroundRenderer.
This works a little different than my post's colorizer as you have to modify the search results manually instead of it doing it for you. But that may also save some computation time.
If your search doesn't use Regex, then you can easily modify SearchResult to instead just pass in a start offset and length for the constructor.
/// <summary>A search result storing a match and text segment.</summary>
public class SearchResult : TextSegment {
/// <summary>The regex match for the search result.</summary>
public Match Match { get; }
/// <summary>Constructs the search result from the match.</summary>
public SearchResult(Match match) {
this.StartOffset = match.Index;
this.Length = match.Length;
this.Match = match;
}
}
/// <summary>Colorizes search results behind the selection.</summary>
public class ColorizeSearchResultsBackgroundRenderer : IBackgroundRenderer {
/// <summary>The search results to be modified.</summary>
TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
/// <summary>Constructs the search result colorizer.</summary>
public ColorizeSearchResultsBackgroundRenderer() {
Background = new SolidColorBrush(Color.FromRgb(246, 185, 77));
Background.Freeze();
}
/// <summary>Gets the layer on which this background renderer should draw.</summary>
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
/// <summary>Causes the background renderer to draw.</summary>
public void Draw(TextView textView, DrawingContext drawingContext) {
if (textView == null)
throw new ArgumentNullException("textView");
if (drawingContext == null)
throw new ArgumentNullException("drawingContext");
if (currentResults == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (SearchResult result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.BorderThickness = 0;
geoBuilder.CornerRadius = 0;
geoBuilder.AddSegment(textView, result);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
drawingContext.DrawGeometry(Background, null, geometry);
}
}
}
/// <summary>Gets the search results for modification.</summary>
public TextSegmentCollection<SearchResult> CurrentResults {
get { return currentResults; }
}
/// <summary>Gets or sets the background brush for the search results.</summary>
public Brush Background { get; set; }
}
In order to make use of the background renderer:
var searchColorizor = new ColorizeSearchResultsBackgroundRenderer();
textEditor.TextArea.TextView.BackgroundRenderers.Add(searchColorizor);
I want to select all the words within richtextbox which has blue color. How can I do this?
In richtextbox, there are some keywords of blue color. I want to get all these keywords as a collection.
By "select", I assume you mean "find". I don't believe you can actually select multiple discontinuous ranges of text in a RichTextBox.
Assuming my understanding is correct, here's some moderately-tested code I've been working on recently. Let me know how it works for you.
Be aware that all the textual content in a RichTextBox is actually stored in a FlowDocument, accessed via the Document property. To iterate through the strings you need to walk hierarchy of TextElement classes in theFlowDocument. The following does so, returning each string and a stack representing the hierarchy, possibly transformed by a selector method:
public static IEnumerable<KeyValuePair<Stack<T>, string>> WalkTextElements<T>(FlowDocument doc, Func<DependencyObject, Stack<T>, T> selector)
{
// Inspiration: http://www.bryanewert.net/journal/2010/5/26/how-to-explore-the-contents-of-a-flowdocument.html
if (doc != null)
{
var stack = new Stack<T>();
// Start with a TextPointer to FlowDocument.ContentStart
TextPointer t = doc.ContentStart;
// Keep a TextPointer for FlowDocument.ContentEnd handy, so we know when we're done.
TextPointer e = doc.ContentEnd;
// Keep going until the TextPointer is equal to or greater than ContentEnd.
while ((t != null) && (t.CompareTo(e) < 0))
{
// Identify the type of content immediately adjacent to the text pointer.
TextPointerContext context = t.GetPointerContext(LogicalDirection.Forward);
// ElementStart is an "opening tag" which defines the structure of the document, e.g. a paragraph declaration.
if (context == TextPointerContext.ElementStart)
{
stack.Push(selector(t.Parent, stack));
}
// An EmbeddedElement, e.g. a UIContainer.
else if (context == TextPointerContext.EmbeddedElement)
{
; // Do nothing.
}
// The document's text content.
else if (context == TextPointerContext.Text)
{
stack.Push(selector(t.Parent, stack));
yield return new KeyValuePair<Stack<T>, string>(stack, t.GetTextInRun(LogicalDirection.Forward));
stack.Pop();
}
// ElementEnd is a "closing tag".
else if (context == TextPointerContext.ElementEnd)
{
stack.Pop();
}
else
{
throw new System.Exception("Unhandled TextPointerContext " + context.ToString());
}
// Advance to the next ContentElement in the FlowDocument.
t = t.GetNextContextPosition(LogicalDirection.Forward);
}
}
}
With this, we can enumerate the strings with background color explicitly overridden:
/// <summary>
/// Enumerate all the strings in a given flow document that are have an explicit background color.
/// </summary>
/// <param name="doc"></param>
/// <param name="includeFlowDocumentColor">true to consider overrides on the entire FlowDocument itself, else false.</param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<Brush, string>> WalkBackgroundColoredTexts(FlowDocument doc, bool includeFlowDocumentColor)
{
foreach (var pair in WalkTextElements<Brush>(doc, (d, s) => SelectTextBackgroundBrush(d, s, includeFlowDocumentColor)))
{
var brush = pair.Key.Peek();
if (brush != null)
{
yield return new KeyValuePair<Brush, string>(brush, pair.Value);
}
}
}
static Brush SelectTextBackgroundBrush(DependencyObject element, Stack<Brush> brushes, bool includeFlowDocumentColor)
{
//http://blogs.msdn.com/b/prajakta/archive/2006/10/11/flowdocument-content-model.aspx
//http://msdn.microsoft.com/en-us/library/aa970786%28v=vs.110%29.aspx
var textElement = element as TextElement;
if (textElement != null)
{
var brush = textElement.Background;
if (brush != null)
return brush;
return PeekOrDefault(brushes);
}
var tableColumn = element as TableColumn;
if (tableColumn != null)
{
var brush = tableColumn.Background;
if (brush != null)
return brush;
return PeekOrDefault(brushes);
}
if (includeFlowDocumentColor)
{
var doc = element as FlowDocument;
if (doc != null)
{
var brush = doc.Background;
if (brush != null)
return brush;
return PeekOrDefault(brushes);
}
}
return null;
}
static T PeekOrDefault<T>(Stack<T> stack)
{
return (stack.Count == 0 ? default(T) : stack.Peek());
}
You probably want to ignore background colors set on the flow document itself and get only specific text runs with background color set, which is why I added the argument.
Given the strings, you may still need to tokenize them into words.
I have a windows forms (using c#) application. It displays a webpage and has a textbox/botton combination which can be used to search for text displayed to the user. Currently I search the inner text of all elements for the text in the textbox. And then I weed out the elements that are redundant (for example a word could be in a 'p' and 'b' element where the 'b' is a child element of 'p' so the element returned should be 'b'). Finally I run the ScrollIntoView(true) method on the found element.
I'd now like to add a function that highlights the text (like if you search for a term in a real webbrowser). My first thought was to just inject html and or javascript code around the text but that seems like a messy solution.
Any ideas on how I should do this? Thanks for your help!
I know that this topic is quite old but I encountered the same issue than the topic author and during all the search I made to solve this, I finally found my answer. So I'm taking the opportunity to share it here.
Here are the methods that I'm using to perform my search in my WebBrowser and to highlight the word found:
private bool FindFirst(string text)
{
var doc = (IHTMLDocument2)WebBrowser.Document.DomDocument;
var sel = (IHTMLSelectionObject)doc.selection;
sel.empty(); // get an empty selection, so we start from the beginning
var rng = sel.createRange() as IHTMLTxtRange;
if (rng != null && rng.findText(text, 1000000000, 0))
{
rng.select();
rng.scrollIntoView(false);
return true;
}
return false;
}
/// <summary>
///
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public bool FindNext(string text)
{
var doc = (IHTMLDocument2)WebBrowser.Document.DomDocument;
var sel = (IHTMLSelectionObject)doc.selection;
var rng = sel.createRange() as IHTMLTxtRange;
if (rng != null)
{
rng.collapse(false); // collapse the current selection so we start from the end of the previous range
if (rng.findText(text, 1000000000, 0))
{
rng.select();
rng.scrollIntoView(false);
return true;
}
else
return FindFirst(text);
}
return false;
}
/// <summary>
///
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public bool FindPrevious(string text)
{
var doc = (IHTMLDocument2)WebBrowser.Document.DomDocument;
var sel = (IHTMLSelectionObject)doc.selection;
var rng = sel.createRange() as IHTMLTxtRange;
if (rng != null)
{
rng.collapse(true); // it should be true,so it goes to start of the document
var found = rng.findText(text, -1, 0); // Range count value should give negative value
if(!found)
{
rng.moveEnd("textedit");
found = rng.findText(text, -1, 0);
}
if (found)
{
rng.select();
rng.scrollIntoView(false);
return true;
}
}
return false;
}
The only "problem" with this piece of code is that only one occurrence of the found words is selected at a time.
Just take input from the user in a textbox [here txtNoteSearch] and then follow the following code to implement the search. Following code demonstrates the search and highlight.
private void WebBrowser_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
mshtml.IHTMLDocument2 doc2 = WebBrowser.Document.DomDocument;
string ReplacementTag = "<span style='background-color: rgb(255, 255, 0);'>";
StringBuilder strBuilder = new StringBuilder(doc2.body.outerHTML);
string HTMLString = strBuilder.ToString();
if (this.m_NoteType == ExtractionNoteType.SearchResult)
{
List<string> SearchWords = new List<string>();
SearchWords.AddRange(this.txtNoteSearch.Text.Trim.Split(" "));
foreach (string item in SearchWords)
{
int index = HTMLString.IndexOf(item, 0, StringComparison.InvariantCultureIgnoreCase);
// 'If index > 0 Then
while ((index > 0 && index < HTMLString.Length))
{
HTMLString = HTMLString.Insert(index, ReplacementTag);
HTMLString = HTMLString.Insert(index + item.Length + ReplacementTag.Length, "</span>");
index = HTMLString.IndexOf(item, index + item.Length + ReplacementTag.Length + 7, StringComparison.InvariantCultureIgnoreCase);
}
}
}
else
{
}
doc2.body.innerHTML = HTMLString;
}