Use SHIFT + ENTER for multiline textbox in UWP

2019-07-22 11:40发布

I would like to adjust the behavior of a TextBox, so that SHIFT + ENTER on the keyboard inserts a new line, while just ENTER executes a different command (such as changing focus or pressing "send" like in messaging apps).

By default, the AcceptsReturn property of the TextBox being set to true inserts a new line when ENTER is pressed. Changing AcceptsReturn to false seems to prevent the new line from working at all, even if I manually add the new line:

private void ContentTextBox_KeyUp(object sender, KeyRoutedEventArgs e)
{
    // NOTE - AcceptsReturn is set to false in XAML.
    if (e.Key == VirtualKey.Enter)
    {
        var keyState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift);
        if ((keyState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down)
        {
            // SHIFT is pressed, so add a new line.
            this.ContentTextBox.Text += "\r";
        }
        else
        {
            // SHIFT is not pressed, so execute my ENTER logic.
            this.Focus(FocusState.Programmatic);
        }
    }
}

Based on this post, I was able to come up with a workaround that functionally works, but has a visual side effect. I set AcceptsReturn to true, and then manually remove the new line when SHIFT is not pressed, followed by executing the code I want when just ENTER is pressed. The side effect is that the textbox expands to accommodate the new line, then immediately shrinks again, suggesting that its automatically handling the ENTER input before my handler is even run. The code for this follows:

private void ContentTextBox_KeyUp(object sender, KeyRoutedEventArgs e)
{
    // NOTE - AcceptsReturn is set to true in XAML.
    if (e.Key == VirtualKey.Enter)
    {
        // If SHIFT is pressed, this next IF is skipped over, so the
        //     default behavior of "AcceptsReturn" is used.
        var keyState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift);
        if ((keyState & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
        {
            // SHIFT is not pressed, so remove the new line.
            string textboxText = this.ContentTextBox.Text;
            textboxText = textboxText.Remove(textboxText.Length - 1);
            this.ContentTextBox.Text = textboxText;

            // Execute my ENTER logic.
            this.Focus(FocusState.Programmatic);
        }
    }
}

Is there a different way to do this, or a way to get rid of that side effect? I tried adjusting the e.IsHandled value, but that didn't work (which makes sense, if the default behavior is running before my code).

2条回答
Fickle 薄情
2楼-- · 2019-07-22 11:58

(In continuation from comment)You can use PreviewKeyDown Event as keydown event will not fire for system handled keys

  private void TextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
        {
            if (Window.Current.CoreWindow.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down)&& e.Key == Windows.System.VirtualKey.Enter)
            {
               //Add New Line
            }
            else if (e.Key == Windows.System.VirtualKey.Enter)
            {
                //This will prevent system from adding new line
                e.Handled = true;
            }
            else
            {
                e.Handled = false;
            }
        }
查看更多
你好瞎i
3楼-- · 2019-07-22 12:07

Handling the PreviewKeyDown event, and marking the event as "handled" prevents the new line from being added in the first place (and thus also prevents the side effect). The full working code follows:

private void ContentTextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
    // NOTE - AcceptsReturn is set to true in XAML.
    if (e.Key == VirtualKey.Enter)
    {
        // If SHIFT is pressed, this next IF is skipped over, so the
        //     default behavior of "AcceptsReturn" is used.
        var keyState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift);
        if ((keyState & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
        {
            // Mark the event as handled, so the default behavior of 
            //    "AcceptsReturn" is not used.
            e.Handled = true;
        }
    }
}

private void ContentTextBox_KeyUp(object sender, KeyRoutedEventArgs e)
{
    // NOTE - AcceptsReturn is set to true in XAML.
    if (e.Key == VirtualKey.Enter)
    {
        // If SHIFT is pressed, this next IF is skipped over, so the
        //     default behavior of "AcceptsReturn" is used.
        var keyState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift);
        if ((keyState & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
        {
            // SHIFT is not pressed, so execute my ENTER logic.
            this.Focus(FocusState.Programmatic);
        }
    }
}
查看更多
登录 后发表回答