Получить позицию прокрутки текстового поля WPF

Мне нужно добавить украшения к содержимому элемента управления WPF TextBox. В основном это работает нормально, я могу получить положение указанных индексов символов и соответствующим образом расположить другие элементы. Но все ломается при прокрутке TextBox. Мои позиции макета больше не совпадают с отображаемым текстом, потому что он переместился в другое место.

Теперь я очень удивлен, что класс TextBox не предоставляет никакой информации о своем состоянии прокрутки или каких-либо событий, когда прокрутка изменилась. Что я могу сделать сейчас?

Я использовал Snoop, чтобы узнать, есть ли какой-нибудь подэлемент прокрутки, который я мог бы спросить, но ScrollContentPresenter также не имеет доступной информации о прокрутке. Я бы очень хотел поместить свои элементы декора прямо в прокручиваемую область, чтобы прокрутка тоже могла повлиять на них, но может быть только один элемент управления содержимым, а это уже один из внутренних элементов TextBox.


person ygoe    schedule 15.02.2013    source источник
comment
Может быть, попробовать подписаться на перенаправленное событие ScrollViewer.ScrollChanged? Он предоставляет такую ​​информацию, как высота экстента, смещение прокрутки, размер области просмотра (все в горизонтальном и вертикальном размерах), возможно, это поможет. Я не совсем уверен, что полностью понимаю вашу задачу. Но попробуйте.   -  person Haspemulator    schedule 16.02.2013


Ответы (2)


Я не уверен, как зафиксировать событие, когда текстовое поле было прокручено (возможно, используйте для этого ответ Narohi), но есть простой способ узнать, какова текущая позиция прокрутки:

// Gets or sets the vertical scroll position.
textBox.VerticalOffset

(Из http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.textboxbase.verticaloffset(v=vs.100).aspx)

Я использую его, чтобы увидеть, прокручивается ли текстовое поле до конца, например:

public static bool IsScrolledToEnd(this TextBox textBox)
{
    return textBox.VerticalOffset + textBox.ViewportHeight == textBox.ExtentHeight;
}
person John Gibb    schedule 05.02.2014
comment
Джон, спасибо, я попробовал ваш IsScrolledToEnd. Обычно это работает, но не всегда. Проблема в том, что вы сравниваете двойники с оператором ==. Я думаю, что в этом сравнении должен быть допуск в 1 строку, например: bool scrollToEnd = Memo.VerticalOffset > Memo.ExtentHeight - Memo.ViewportHeight - fontHeight; - person Aleksandr; 12.12.2017
comment
@Aleksandr Где ты берешь fontHeight? Я использовал Memo.FontSize, и, похоже, он работал так, как задумал. - person Gravitate; 19.03.2019
comment
двойной fontHeight = Memo.FontSize * Memo.FontFamily.LineSpacing; - person Aleksandr; 21.03.2019

Вы можете получить ScrollViewer с помощью этого метода, передав текстовое поле в качестве аргумента и типа ScrollView. Затем вы можете подписаться на событие ScrollChanged.

public static T FindDescendant<T>(DependencyObject obj) where T : DependencyObject
{
    if (obj == null) return default(T);
    int numberChildren = VisualTreeHelper.GetChildrenCount(obj);
    if (numberChildren == 0) return default(T);

    for (int i = 0; i < numberChildren; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child is T)
        {
            return (T)(object)child;
        }
    }

    for (int i = 0; i < numberChildren; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        var potentialMatch = FindDescendant<T>(child);
        if (potentialMatch != default(T))
        {
            return potentialMatch;
        }
    }

    return default(T);
}

Пример:

public MainWindow()
{
    InitializeComponent();
    Loaded += new RoutedEventHandler(MainWindow_Loaded);
}

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    ScrollViewer s = FindDescendant<ScrollViewer>(txtYourTextBox);
    s.ScrollChanged += new ScrollChangedEventHandler(s_ScrollChanged);
}

void s_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    // check event args for information needed
}
person Nathan Hillyer    schedule 15.02.2013