locked
Как найти и переместить текст в RichTextBox-е? RRS feed

  • Общие обсуждения

  • В отличии от Windows Forms,текущая версия WPF не предоставляет на прямую функцию пойска в RichTextBox, однако TextRange и TextPointer APIs предоставляемые Фреймворком могут помочь нам сделать нашу собственную версию текстового поиска и перемещения. Пример:

    /// <summary>
    /// This class represents the possible options for search operation.
    /// </summary>
    [Flags]
    public enum FindOptions
    {
        /// <summary>
        /// Perform case-insensitive non-word search.
        /// </summary>
        None = 0x00000000,
        /// <summary>
        /// Perform case-sensitive search.
        /// </summary>
        MatchCase = 0x00000001,
        /// <summary>
        /// Perform the search against whole word.
        /// </summary>
        MatchWholeWord = 0x00000002,
    }
    
    /// <summary>
    /// This class encapsulates the find and replace operations for<see cref="FlowDocument"/>.
    /// </summary>
    public sealed class FindAndReplaceManager
    {
        private FlowDocument inputDocument;
        private TextPointer currentPosition;
    
        /// <summary>
        /// Initializes a new instance of the<see cref="FindReplaceManager"/> 
        /// class given the specified<see cref="FlowDocument"/> instance.
        /// </summary>
        /// <param name="inputDocument">the input document</param>
        public FindAndReplaceManager(FlowDocument inputDocument)
        {
            if (inputDocument == null)
            {
                throw new ArgumentNullException("documentToFind");
            }
    
            this.inputDocument = inputDocument;
            this.currentPosition = inputDocument.ContentStart;
        }
    
        /// <summary>
        /// Gets and sets the offset position for the<see cref="FindReplaceManager"/>
        /// </summary>
        public TextPointer CurrentPosition
        {
            get
            {
                return currentPosition;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                if (value.CompareTo(inputDocument.ContentStart) < 0 || 
                    value.CompareTo(inputDocument.ContentEnd) > 0)
                {
                    throw new ArgumentOutOfRangeException("value");
                }
    
                currentPosition = value;
            }
        }
    
        /// <summary>
        /// Find next match of the input string.
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="findOptions">the search options</param>
        /// <returns>The<see cref="TextRange"/> instance representing the input string.</returns>
        /// <remarks>
        /// This method will advance the<see cref="CurrentPosition"/> to next context position.
        /// </remarks>
        public TextRange FindNext(String input, FindOptions findOptions)
        {
            TextRange textRange = GetTextRangeFromPosition(ref currentPosition, input, findOptions);
            return textRange;
        }
    
        /// <summary>
        /// Within a specified input string, replaces the input string that
        /// match a regular expression pattern with a specified replacement string.
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="replacement">The replacement string.</param>
        /// <param name="findOptions"> the search options</param>
        /// <returns>The<see cref="TextRange"/> instance representing the replacement string.</returns>
        /// <remarks>
        /// This method will advance the<see cref="CurrentPosition"/> to next context position.
        /// </remarks>
        public TextRange Replace(String input, String replacement, FindOptions findOptions)
        {
            TextRange textRange = FindNext(input, findOptions);
            if (textRange != null)
            {
                textRange.Text = replacement;
            }
    
            return textRange;
        }
    
        /// <summary>
        /// Within a specified input string, replaces all the input strings that
        /// match a specified criteria with a specified replacement string.
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="replacement">The replacement string.</param>
        /// <param name="findOptions"> the search options</param>
        /// <param name="action">the action performed for each match of the input string.</param>
        /// <returns>The number of times the replacement can occur.</returns>
        /// <remarks>
        /// This method will advance the<see cref="CurrentPosition"/> to last context position.
        /// </remarks>
        public Int32 ReplaceAll(String input, String replacement, FindOptions findOptions, Action<TextRange> action)
        {
            Int32 count = 0;
            currentPosition = inputDocument.ContentStart;
            while (currentPosition.CompareTo(inputDocument.ContentEnd) < 0)
            {
                TextRange textRange = Replace(input, replacement, findOptions);
                if (textRange != null)
                {
                    count++;
                    if (action != null)
                    {
                        action(textRange);
                    }
                }
            }
    
            return count;
        }
    
        /// <summary>
        /// Finds the corresponding<see cref="TextRange"/> instance 
        /// representing the input string given a specified text pointer position.
        /// </summary>
        /// <param name="position">the current text position</param>
        /// <param name="textToFind">input text</param>
        /// <param name="findOptions">the search option</param>
        /// <returns>
        /// An<see cref="TextRange"/> instance represeneting the matching
        /// string withing the text container.
        /// </returns>
        public TextRange GetTextRangeFromPosition(ref TextPointer position, 
                                                  String input, 
                                                  FindOptions findOptions)
        {
            Boolean matchCase = (findOptions & FindOptions.MatchCase) == FindOptions.MatchCase;
            Boolean matchWholeWord = (findOptions & FindOptions.MatchWholeWord) 
                                                        == FindOptions.MatchWholeWord;
    
            TextRange textRange = null;
    
            while (position != null)
            {
                if (position.CompareTo(inputDocument.ContentEnd) == 0)
                {
                    break;
                }
    
                if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                {
                    String textRun = position.GetTextInRun(LogicalDirection.Forward);
                    StringComparison stringComparison = matchCase ? 
                        StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
                    Int32 indexInRun = textRun.IndexOf(input, stringComparison);
    
                    if (indexInRun >= 0)
                    {
                        position = position.GetPositionAtOffset(indexInRun);
                        TextPointer nextPointer = position.GetPositionAtOffset(input.Length);
                        textRange = new TextRange(position, nextPointer);
    
                        if (matchWholeWord)
                        {
                            if (IsWholeWord(textRange)) // Test if the "textRange" represents a word.
                            {
                                // If a WholeWord match is found, directly terminate the loop.
                                position = position.GetPositionAtOffset(input.Length);
                                break;
                            }
                            else
                            {
                                // If a WholeWord match is not found, go to next recursion to find it.
                                position = position.GetPositionAtOffset(input.Length);
                                return GetTextRangeFromPosition(ref position, input, findOptions);
                            }
                        }
                        else
                        {
                            // If a none-WholeWord match is found, directly terminate the loop.
                            position = position.GetPositionAtOffset(input.Length);
                            break;
                        }
                    }
                    else
                    {
                        // If a match is not found, go over to the next context position after the "textRun".
                        position = position.GetPositionAtOffset(textRun.Length);
                    }
                }
                else
                {
                    //If the current position doesn't represent a text context position, go to the next context position.
                    // This can effectively ignore the formatting or embed element symbols.
                    position = position.GetNextContextPosition(LogicalDirection.Forward);
                }
            }
    
            return textRange;
        }
    
        /// <summary>
        /// determines if the specified character is a valid word character.
        /// here only underscores, letters, and digits are considered to be valid.
        /// </summary>
        /// <param name="character">character specified</param>
        /// <returns>Boolean value didicates if the specified character is a valid word character</returns>
        private Boolean IsWordChar(Char character)
        {
            return Char.IsLetterOrDigit(character) || character == '_';
        }
    
        /// <summary>
        /// Tests if the string within the specified<see cref="TextRange"/>instance is a word.
        /// </summary>
        /// <param name="textRange"><see cref="TextRange"/>instance to test</param>
        /// <returns>test result</returns>
        private Boolean IsWholeWord(TextRange textRange)
        {
            Char[] chars = new Char[1];
    
            if (textRange.Start.CompareTo(inputDocument.ContentStart) == 0 || textRange.Start.IsAtLineStartPosition)
            {
                textRange.End.GetTextInRun(LogicalDirection.Forward, chars, 0, 1);
                return !IsWordChar(chars[0]);
            }
            else if (textRange.End.CompareTo(inputDocument.ContentEnd) == 0)
            {
                textRange.Start.GetTextInRun(LogicalDirection.Backward, chars, 0, 1);
                return !IsWordChar(chars[0]);
            }
            else
            {
                textRange.End.GetTextInRun(LogicalDirection.Forward, chars, 0, 1);
                if (!IsWordChar(chars[0]))
                {
                    textRange.Start.GetTextInRun(LogicalDirection.Backward, chars, 0, 1);
                    return !IsWordChar(chars[0]);
                }
            }
    
            return false;
        }
    }
    



    Для связи [Mail]
    • Перемещено Tagore Bandlamudi 2 октября 2010 г. 23:03 MSDN Forums consolidation (От:Разработка Windows-приложений)
    17 марта 2010 г. 12:18
    Модератор