第二个TextBox显示与第一个相同的文本选择

时间:2013-04-10 20:39:09

标签: c# xaml textbox windows-runtime selection

长时间听众,第一次来电。我对WinRT C#/ XAML中的TextBox有一个奇怪的问题,我希望有人可以帮助我。

基本上,我正在创建一个自定义控件,它基本上需要第二个TextBox作为第一个的副本,包括显示相同的文本,并显示相同的选定文本。显然,对于Text要求,我只是在第一个TextBox上响应TextChanged事件,并将第二个TextBox的Text设置为第一个Text,这很有效。

对于Selected Text要求,我从一个类似的解决方案开始,我的代码如下:

    void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
    {
        this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
    }

最初与鼠标一起使用时似乎工作得很好:

screen cap using mouse

但是在使用触摸选择文字时遇到问题。我在TextBox中双击以创建第一个“锚点”,就像在Touch中一样,然后拖动以开始选择;但是我只能在选择停止之前选择正常的单个字符。 TextBox不会完全失去焦点,但行为类似于此;选择锚点消失,我不能继续选择任何东西,除非我重新点击以开始一个新的选择。如果我删除代码以在TextBox2中选择文本,那么Touch选择在TextBox1中表现完美。

我一直试图解决这个问题一段时间但不能,我不确定我是否可以使用WinRT TextBox获得所需的行为。有没有人有任何想法?或者也许用另一种方法来实现具有这种行为的两个TextBox的解决方案?

非常感谢。

2 个答案:

答案 0 :(得分:0)

所以这远非一个答案,但发现了一些可能会帮助您或其他人提出潜在解决方法的事情。如果这些是您已经看到并注意到的事情,请道歉。

首先,不是对TextBox2.Select()的调用本身就是问题。例如,这对我来说很好

    private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
    {
        var start =  TextBox1.SelectionStart;
        var length = TextBox1.SelectionLength;
        TextBox2.Select(3, 5);
    }

不幸的是,使用startlength与硬编码的3和5相比,即以下内容,不起作用:

    private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
    {
        var start =  TextBox1.SelectionStart;
        var length = TextBox1.SelectionLength;
        TextBox2.Select(start, length);
    }

我还发现,如果我从最后开始,我可以选择两个字符,但从一开始只有一个字符。这让我考虑调度调用来设置第二个选择:

    private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
    {
        var start =  TextBox1.SelectionStart;
        var length = TextBox1.SelectionLength;
        Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, 
            () => TextBox2.Select(start, length));
    }

现在我可以从正面选择2,从背面选择3,有时选择4。更进一步,能够选择多达六个或七个,快速滑动。

    private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
    {
        var start =  TextBox1.SelectionStart;
        var length = TextBox1.SelectionLength;
        Dispatcher.RunIdleAsync((v) => Highlight());
    }

    public void Highlight()
    {
        TextBox2.Select(TextBox1.SelectionStart, TextBox1.SelectionLength);
    }

似乎解决这个问题的诀窍就是在TextBox1 SelectionChanged事件的任何痕迹完成之前不设置TextBox2。

这可能值得在Connect上注册。

答案 1 :(得分:0)

我的也只是部分解决方案。

我做了一些调试,发现SelectionChanged事件在整个文本选择过程中被触发。换句话说,单个手指“滑动”将生成多个SelectionChanged事件。

如您所知,在文本选择手势期间调用TextBox.Select会影响手势本身。在程序化文本选择之后,Windows似乎停止了手势。

我的解决方法是尽可能延迟调用TextBox.Select方法。除了一个边缘情况外,这确实很有效。此方法失败的位置在以下情形中:

用户开始选择手势,比如选择x个字符。用户不用将手指从屏幕上移开,暂停一两秒钟。然后,用户尝试选择更多字符。

我的解决方案没有处理上一段中的最后一位。暂停后的触摸选择实际上没有选择任何内容,因为我的代码将调用TextBox.Select方法。

这是实际的代码。如上所述,在单个选择手势期间触发了多个选择更改事件。我的代码使用计时器和计数器仅在没有任何待处理触摸生成选择更改事件时进行编程选择。

int _selectCounter = 0;
const int SELECT_TIMER_LENGTH = 500;
async private void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
    // _selectCounter is the number of selection changed events that have fired.
    // If you are really paranoid, you will want to make sure that if
    // _selectCounter reaches MAX_INT, that you reset it to zero.
    int mySelectCount = ++_selectCounter;

    // start the timer and wait for it to finish
    await Task.Delay(SELECT_TIMER_LENGTH);

    // If equal (mySelectCount == _selectCounter),
    // this means that NO select change events have fired
    // during the delay call above. We only do the
    // programmatic selection when this is the case.
    // Feel free to adjust SELECT_TIMER_LENGTH to suit your needs.
    if (mySelectCount == _selectCounter)
    {
        this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
    }
}