Search code examples
c#.netstringtextboxuwp

UWP TextBox - Focus on selection


I have a .NET UWP TextBox with a good deal of text, and I want to search for a word in it. When I click on the button to start my search, it will find the first occurrence of this word. When I click again, it will find the second, like ctrl+f in Notepad).

I want to get focus on the found world, but when is text is long enough that there is a scrollbar in, it will not bring the found word into view.

This is a screengrab of the screen in this state, showing how I must resize the window to see the found word.

enter image description here

Here is my code for searching (textarea is of type TextBox):

private void Find(string text)
    {
        textarea.Focus(FocusState.Programmatic);
        var start = textarea.SelectionStart + textarea.SelectionLength;
        var found =  (bool)checkboxFindCaseSensitive.IsChecked ? textarea.Text.IndexOf(text, start) : textarea.Text.IndexOf(text, start, StringComparison.CurrentCultureIgnoreCase);
        if (found == -1)
        {
            textarea.SelectionStart = 0;
            found = (bool)checkboxFindCaseSensitive.IsChecked ? textarea.Text.IndexOf(text, start) : textarea.Text.IndexOf(text, start, StringComparison.CurrentCultureIgnoreCase);
            if (found == -1) return;
        }
        textarea.SelectionStart = found;
        textarea.SelectionLength = text.Length;
    }

I have already tried to put textarea.Focus(FocusState.Programmatic); at the end of method as well as textarea.Focus(FocusState.Pointer);, but neither helped.

UPDATE:

I've found that it's focusing correctly, but to the last found word (to position, where is the cursor before find next word), not to the currently found word.

enter image description here

So I need to update focus to current SelectionStart, not to the last. Any ideas? I have already tried to change SelectionStart again, replace text and update layout - nothing helps.


Solution

  • What you can do is to measure the height of your text until the index, and resize the textbox accordingly.

    private static float GetTextHeightUntilIndex(TextBox textBox, int index)
        {
            var height = 0;
            var textBuffer = textBox.Text;
    
            // Remove everything after `index` in order to measure its size
            textBox.Text = textBuffer.Substring(0, index);
            textBox.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            var height = textBox.DesiredSize().Height;
    
            // Put the full text back
            textBox.Text = textBuffer;
    
            return height;
        }
    
    private void Find(string text)
        {
            textarea.Focus(FocusState.Programmatic);
            var start = textarea.SelectionStart + textarea.SelectionLength;
            var found =  (bool)checkboxFindCaseSensitive.IsChecked ? textarea.Text.IndexOf(text, start) : textarea.Text.IndexOf(text, start, StringComparison.CurrentCultureIgnoreCase);
            if (found == -1)
            {
                textarea.SelectionStart = 0;
                found = (bool)checkboxFindCaseSensitive.IsChecked ? textarea.Text.IndexOf(text, start) : textarea.Text.IndexOf(text, start, StringComparison.CurrentCultureIgnoreCase);
                if (found == -1) return;
            }
            textarea.SelectionStart = found;
            textarea.SelectionLength = text.Length;
    
            // -------------------
    
            var cursorPosInPx = GetTextHeightUntilIndex(textarea, found);
    
            // First method: resize your textbox to the selected word
            textarea.Height = cursorPosInPx; 
    
            // Second method: scroll the textbox
            var grid = (Grid)VisualTreeHelper.GetChild(textarea, 0);
            for (var i = 0; i <= VisualTreeHelper.GetChildrenCount(grid) - 1; i++)
            {
                object obj = VisualTreeHelper.GetChild(grid, i);
                if (obj is ScrollViewer)
                    ((ScrollViewer)obj).ChangeView(null, cursorPosInPx, null, true);
            }
        }
    

    Be careful however, for the first method, depending on whatlayout your textbox is, resizing the control may have an unwanted effect or no effect at all.