2

默认情况下,文本框不插入制表符。当您按 Tab 时,它会将焦点移至下一个元素。当您按下 Ctrl-Tab 时,它会插入一个制表符。

如何修复这种行为,使其表现得像人们通常期望文本框的行为一样,例如 Tab 键使用制表符插入缩进?此外,如何将 Ctrl-Tab 重新映射到替代功能?

对于插入制表符的第一个问题,我可以附加到 TextBox 的 KeyDown 事件,如果按下的键是 Tab,则将事件标记为已处理以防止 TextBox 失去焦点,但是如何插入 Tab 字符?

当我附加到文本框的 KeyDown 事件时,如果已按住 Ctrl,则按 Tab 键根本不会触发侦听器。如果我附加到 TextBox 的 KeyUp 事件,按下 tab 键确实会被拾起,但只有在它已经将选项卡插入到 TextBox 之后。如果没有这种不需要的行为,如何重新映射 Ctrl-Tab?

我在 C++/CX 中工作。

4

1 回答 1

2

如果其他人处于同样的情况,这是我最终开发的解决方案。在我看来,这个问题的简单程度令人震惊,而且也相当不令人满意。如果有人有更好的解决方案,我很想听听。

总体思路是重写 Tab 的 KeyDown 事件以插入 '\t' 字符。能够覆盖 Ctrl+Tab 的 KeyDown 事件会很棒,但这似乎是不可能的,因为 TextBox 上的某种硬编码会吞噬 Ctrl+Tab KeyDown 事件并且它甚至不会触发。因此,Ctrl+Tab 的 KeyUp 事件被覆盖。

KeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{
    if (e->Key == VirtualKey::Control) m_CtrlKeyPressed = true;
    else if (e->Key == VirtualKey::Tab) {
        // There is no need to test for if Ctrl is pressed here, since Ctrl-Tab appears to
        // be hardcoded into TextBox.
        // When Ctrl is pressed, a KeyDown event for Tab is never fired by TextBox.

        // Normally TextBox will try to give up focus when Tab is pressed - this prevents
        // that.
        e->Handled = true;

        // Platform::Strings support almost no operations, so we will need to cast the
        // TextBox's text into a wstring to insert.
        TextBox^ textBox = static_cast<TextBox^>(sender);
        std::wstring modifiedString(textBox->Text->Data());

        // SelectionStart works as current cursor position even when no text is selected.
        int cursorPosition = textBox->SelectionStart;

        // Unfortunately, casting into a wstring reveals Windows line-endings as \r\n,
        // which count as two characters now.
        // Therefore, every time we run into a line-ending our cursorPosition will become
        // off by one, so we need to compensate.
        int offsetDueToLineEndings = 0;
        for (int i = 0; i < cursorPosition + offsetDueToLineEndings; i++)
        {
            if (modifiedString[i] == '\r') offsetDueToLineEndings++;
        }
        modifiedString.insert(cursorPosition + offsetDueToLineEndings, 1, '\t');

        // Unfortunately, this text replacement wipes TextBox's built-in undo data.
        textBox->Text = ref new String(modifiedString.c_str());
        textBox->SelectionStart = cursorPosition + 1;
    }
}


KeyUp(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{   
    if (e->Key == VirtualKey::Control) m_CtrlKeyPressed = false;
    else if (m_CtrlKeyPressed)
    {
        if (e->Key == VirtualKey::Tab) {
            // See KeyDown for Tab for comments on this code.
            TextBox^ textBox = static_cast<TextBox^>(sender);
            std::wstring modifiedString(textBox->Text->Data());

            int cursorPosition = textBox->SelectionStart;

            int offsetDueToLineEndings = 0;
            for (int i = 0; i < cursorPosition + offsetDueToLineEndings; i++)
            {
                if (modifiedString[i] == '\r') offsetDueToLineEndings++;
            }
            modifiedString.erase(cursorPosition + offsetDueToLineEndings - 1, 1);

            textBox->Text = ref new String(modifiedString.c_str());
            textBox->SelectionStart = cursorPosition - 1;

            // Do something with Ctrl+Tab
        }
    }
}

这种实现存在许多严重的问题。

对于简单的 Tab 按:

  • 插入 Tab 会导致 TextBox 的撤消数据被擦除。

对于 Ctrl+Tab 按下:

  • 如果用户按住 Ctrl+Tab,则会插入多个选项卡,直到他释放 Tab。

  • 触发时,有一段时间可以看到选项卡正在创建和删除,看起来很粗制滥造。

  • 使用 Ctrl+Tab 会导致 TextBox 的撤消数据被擦除。

缺点可能意味着该解决方案无法使用。同样,如果有更好的解决方案,我很想听听。至少,也许看到我的方法的结果可以避免其他人自己实施和发现所有这些问题的麻烦。

于 2015-08-10T20:34:37.180 回答