0

I am trying to develop a messaging application in WPF.. Now,what I want is when a user clicks on "Enter" the message has to send and when the user clicks "Shift+enter" it should move to a new line.

I have tried something like this,But it doesn't seems to work

if (e.Key == Key.Enter && (e.KeyboardDevice.Modifiers & ModifierKeys.Shift) != ModifierKeys.Shift)
{
  //insert newline
}
else if(e.Key==Key.Enter)
{
  //Send Message
}

I am using Textbox here.

FCin
  • 3,804
  • 4
  • 20
  • 49
Mounika
  • 137
  • 2
  • 11
  • what does "doesn't work" mean? Throws up an error, sends the message anyway, adds 2 lines, etc. – sous2817 Nov 02 '18 at 13:38
  • @sous2817 "Shift+Enter" is not inserting a newline..It is sending the message – Mounika Nov 02 '18 at 13:41
  • is "Shift" a valid modifier key? Perhaps it needs to be a LeftShift or RightShift (https://stackoverflow.com/questions/5750722/how-to-detect-modifier-key-states-in-wpf) – sous2817 Nov 02 '18 at 16:21

3 Answers3

1

Set the AcceptsReturn property to true and handle PreviewKeyDown:

private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter && Keyboard.Modifiers != ModifierKeys.Shift)
    {
        //TODO: send message...
        e.Handled = true;
    }
}

XAML:

<TextBox AcceptsReturn="True" PreviewKeyDown="TextBox_PreviewKeyDown" />
mm8
  • 163,881
  • 10
  • 57
  • 88
  • I've tried this also...but this property doesn't have any effects – Mounika Nov 02 '18 at 13:58
  • It works just fine for me so then you are doing something wrong. What happens and what do you expect to happen? – mm8 Nov 02 '18 at 14:06
  • Actually...I'm implementing it through xamarin.forms and used a renderer in native wpf to implement this .Actually,What I want is If a user presses "Enter" The message has to send and it's working fine and if he presses "shift+enter" it should insert a new line...but in both scenarios it is sending message – Mounika Nov 02 '18 at 14:21
  • Not in my example.It only enters the if block if the SHIFT key is not pressed. – mm8 Nov 02 '18 at 14:35
1

Working on a similar concept. Here is what I did. The below solution also somewhat adheres to MVVM architectural pattern if that's your thing.

You'll need the following.

Step 1: Add the following for you XAML.

 <TextBox Text="{Binding Path=MessageText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
 AcceptsReturn="False" AcceptsTab="True" TextWrapping="Wrap" SpellCheck.IsEnabled ="True">
   <TextBox.Resources>
       <Style TargetType="{x:Type Border}">
           <Setter Property="CornerRadius" Value="3"/>
       </Style>
   </TextBox.Resources>
<TextBox.InputBindings>
    <KeyBinding Key="Enter" Command="{Binding SendMessageCommand, Mode=OneWay}" />
    <KeyBinding Gesture="Shift+Return" Command="{Binding NewLineCommand, Mode=OneWay}" CommandParameter="{Binding Path=., Mode=OneWay, ElementName=chattext_field}" />
</TextBox.InputBindings>

Step 2 : Create your view model if you don't already have one. In my example, it called AppViewModel.

class AppViewModel : INotifyPropertyChanged
{
   private string messageText = string.Empty;

   public string MessageText
   {
       get { return messageText; }
       set
       {
           messageText = value;
           NotifyPropertyChanged();
       }
   }
   public ICommand SendMessageCommand { get; private set; }
   public ICommand NewLineCommand { get; private set; }

   public void Load()
   {
       NewLineCommand = new CustomCommand(p => 
        {
            System.Windows.Controls.TextBox txtB = p as System.Windows.Controls.TextBox;
            if (txtB == null)
                return;
            var caretIdx = txtB.CaretIndex;

            if (string.IsNullOrEmpty(MessageText))
                MessageText += "\r";
            else
                MessageText = MessageText.Insert(caretIdx, "\r");

            txtB.CaretIndex = caretIdx + 1;
        });
        SendMessageCommand = new CustomCommand(p => 
        {
            try
            {
                // your send message code here
            }
            catch (LogException ex)
            {
                System.Windows.MessageBox.Show($"Message sending failure.\n{ex}", "Message Center", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        });
   }

   private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
   {
       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }
}

Step 3 : When you load your user control/View using the view model. Initialize/Load the view model when the view is ready.

public partial class MyChatControl : UserControl
{
    public MyChatControl()
    {
        InitializeComponent();
        this.Loaded += MyChatControl_Loaded;
    }

    private void MyChatControl_Loaded(object sender, RoutedEventArgs e)
    {
        try
        {              
            AppViewModel model = new AppViewModel();
            model.Load();
            this.DataContext = model;
        }
        catch (LogException ex)
        {
            MessageBox.Show($"Failed control content load.\n{ex}", "Failed", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
}

Almost forgot, here is my "CustomCommand" implementation if you don't have one yet. I have a Async version called "CustomAsyncCommand" as well if you need.

// Interface 
public interface ICustomCommand : ICommand
{
    event EventHandler<object> Executed;
}

// Command Class
public class CustomCommand : ICustomCommand
{
    #region Private Fields
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;
    #endregion

    #region Constructor
    public CustomCommand(Action<object> execute) : this(execute, null)
    {
    }

    public CustomCommand(Action<object> execute, Func<object, bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute ?? (x => true);
    }
    #endregion

    #region Public Methods
    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter = null)
    {
        Refresh();
        _execute(parameter);
        Executed?.Invoke(this, parameter);
        Refresh();
    }

    public void Refresh()
    {
        CommandManager.InvalidateRequerySuggested();
    }
    #endregion

    #region Events
    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
        }
    }
    public event EventHandler<object> Executed;
    #endregion
}
B.Spangenberg
  • 340
  • 2
  • 13
0

Only Set the AcceptsReturn property to true

XMAL

<TextBox AcceptsReturn="True" />
  • Your answer incorrect. AcceptsReturn does the opposite of what OP requested. OP wants new lines on (Shift+Enter), not Enter. "AcceptsReturn - Gets or sets a value indicating whether pressing ENTER in a multiline TextBox control creates a new line of text in the control or activates the default button for the form." Please see : https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.textbox.acceptsreturn?view=netframework-4.7.2 – B.Spangenberg Nov 06 '18 at 12:53