Search code examples
c#uwptextboxkeyboard

Use SHIFT + ENTER for multiline textbox in UWP


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).


Solution

  • (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;
                }
            }