选择文本是否附加时TextBox ScrollToEnd毛刺

时间:2012-10-23 18:43:45

标签: c# .net-4.0 wpf-controls

我正在创建一个文本框,以便在添加文本时自动滚动到结尾。但是,当鼠标悬停在文本框上时,我希望选项不滚动文本框。我已经完成了所有这些,但是当用户选择文本并且文本框收到更新文本的事件时,一切都变得混乱。

以下是我正在使用的内容:


<TextBox Text="{Binding ConsoleContents, Mode=OneWay}" TextWrapping="Wrap" 
    IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Visible" 
    TextChanged="TextBox_TextChanged" MouseEnter="TextBox_MouseEnterLeave"
    MouseLeave="TextBox_MouseEnterLeave" AllowDrop="False" Focusable="True" 
    IsUndoEnabled="False"></TextBox>

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextBox textBox = sender as TextBox;
        if (textBox == null) return;

        // ensure we can scroll
        if (_canScroll)
        {
            textBox.Select(textBox.Text.Length, 0); //This was an attempt to fix the issue
            textBox.ScrollToEnd();
        }
    }

private void TextBox_MouseEnterLeave(object sender, MouseEventArgs e)
    {
        TextBox textBox = sender as TextBox;

        // Don't scroll if the mouse is in the box
        if (e.RoutedEvent.Name == "MouseEnter")
        {
            _canScroll = false;
        }
        else if (e.RoutedEvent.Name == "MouseLeave") 
        {
            _canScroll = true;
        }
    }

为了进一步解释haywire的含义,当文本框接收和propertychanged事件时,如果鼠标没有悬停在文本框上,它会设置文本并向下滚动到最后。如果鼠标悬停在它上面则不会滚动。但是,如果我选择文本并且文本框重新显示propertychanged事件,则内容会更新,但该框不会向下滚动。这是预料之中的。问题是我的选择然后从光标当前的位置到文本的顶部。如果我从框中移除光标,它会继续正常但是一旦光标返回,框就会卡在顶部而不能向下滚动。我以为它可能是光标所以我试图将它移动到最后但是什么都解决了。

有什么想法吗?!

我一直在撕开我的头发!谢谢!

2 个答案:

答案 0 :(得分:0)

您需要处理PropertyChanged事件的额外情况。由于文本已更改,因此控件已更新。因为它更新它会重置某些值,如光标位置和选定的文本等。

您可以尝试暂时保存这些设置,例如CaretIndexSelectionStartSelectionLength。虽然我没有这方面的经验,所以你必须自己找出你想要保留的价值。

当为TextBox触发PropertyChanged事件时,您也可以应用相同的_canScroll检查。这允许您使用文本。但是,如果鼠标离开文本框,则必须在显示最新文本之前等待新事件。

您可能还想查看使用IsFocused属性。在我看来,它提供了比MouseEnter和MouseLeave事件更好的解决方案。但这当然取决于你。

答案 1 :(得分:0)

整个上午花了很多时间,但这是怎么做的:

<TextBox Text="{Binding ConsoleContents, Mode=OneWay}" 
         TextWrapping="Wrap" IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Visible" TextChanged="TextBox_TextChanged" 
         MouseEnter="TextBox_MouseEnterLeave" MouseLeave="TextBox_MouseEnterLeave" SelectionChanged="TextBox_SelectionChanged" AllowDrop="False" Focusable="True" 
         IsUndoEnabled="False"></TextBox>

public partial class ConsoleView : UserControl
{
    private bool _canScroll;

    // saves
    private int _selectionStart;
    private int _selectionLength;
    private string _selectedText;

    public ConsoleView(ConsoleViewModel vm)
    {
        InitializeComponent();
        this.DataContext = vm;

        _canScroll = true;
    }


    private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextBox textBox = sender as TextBox;
        if (textBox == null) return;

        // ensure we can scroll
        if (_canScroll)
        {
            // set the cursor to the end and scroll down
            textBox.Select(textBox.Text.Length, 0);
            textBox.ScrollToEnd();

            // save these so the box doesn't jump around if the user goes back in
            _selectionLength = textBox.SelectionLength;
            _selectionStart = textBox.SelectionStart;
        }
        else if (!_canScroll)
        {

            // move the cursor to where the mouse is if we're not selecting anything (for if we are selecting something the cursor has already moved to where it needs to be)
            if (string.IsNullOrEmpty(_selectedText))
            //if (textBox.SelectionLength > 0)
            {
                textBox.CaretIndex = textBox.GetCharacterIndexFromPoint(Mouse.GetPosition(textBox), true);
            }
            else
            {
                textBox.Select(_selectionStart, _selectionLength); // restore what was saved
            }

        }
    }


    private void TextBox_MouseEnterLeave(object sender, MouseEventArgs e)
    {
        TextBox textBox = sender as TextBox;
        if (textBox == null) return;

        // Don't scroll if the mouse is in the box
        if (e.RoutedEvent.Name == "MouseEnter")
        {
            _canScroll = false;
        }
        else if (e.RoutedEvent.Name == "MouseLeave")
        {
            _canScroll = true;
        }

    }


    private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
    {
        TextBox textBox = sender as TextBox;
        if (textBox == null) return;

        // save all of the things
        _selectionLength = textBox.SelectionLength;
        _selectionStart = textBox.SelectionStart;
        _selectedText = textBox.SelectedText; // save the selected text because it gets destroyed on text update before the TexChanged event.

    }

}