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.
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" />
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
}
Only Set the AcceptsReturn property to true
XMAL
<TextBox AcceptsReturn="True" />
Related
I have a list of user control and each user control have two buttons, and when I click on them, something must happen, but I want to handle this event not inside the user control, I want to handle the events inside the main page
So, How can I catch the events that fired by the selected item user control of list view?
user control code behind:
public sealed partial class TestingUerControl : UserControl
{
public TestingUerControl()
{
this.InitializeComponent();
}
public event EventHandler FirstButtonEvent;
public event EventHandler SecondButtonEvent;
private void firstButton_Click(object sender, RoutedEventArgs e)
{
//Some stuff of code
FirstButtonEvent?.Invoke(this, new EventArgs());
}
private void secondButton_Click(object sender, RoutedEventArgs e)
{
//Some stuff of code
SecondButtonEvent?.Invoke(this, new EventArgs());
}
}
main page xaml markup:
<ListView x:Name="listUserControl"
Width="100"
Header="400">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:MyModel">
<userControl:TestingUerControl/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I used this statement:
((TestingUerControl)listUserControl.SelectedItem).FirstButtonEvent += OnFirstButtonEvent;
but this doesn't work I can cast the SelectedItem to MyModel class only
So How can I reach to "FirstButtonEvent" and "SecondButtonEvent" of selected user control of list view
The way with commands and MVVM is preferable, but you can also work with custom RoutedEvent instead of Event:
public sealed partial class TestingUerControl : UserControl
{
public TestingUerControl()
{
this.InitializeComponent();
}
public static readonly RoutedEvent FirstButtonEvent = EventManager.RegisterRoutedEvent(
nameof(FirstButton), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TestingUerControl));
public event RoutedEventHandler FirstButton
{
add { AddHandler(FirstButtonEvent, value); }
remove { RemoveHandler(FirstButtonEvent, value); }
}
private void firstButton_Click(object sender, RoutedEventArgs e)
{
//Some stuff of code
RaiseEvent(new RoutedEventArgs(TestingUerControl.FirstButtonEvent));
}
private void secondButton_Click(object sender, RoutedEventArgs e)
{
//See first btn
}
}
and then in XAML just assign an event handler:
<ListView x:Name="listUserControl" Width="100" Header="400">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:MyModel">
<userControl:TestingUerControl FirstButton="OnFirstButton_Click"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Use MVVM:
public class TestCommand : ICommand
{
Action<object> _execute;
Func<object, bool> _canExecute;
public TestCommand(Action<object> execute)
: this(execute, DefaultCanExecute)
{
}
public TestCommand(Action<object> execute, Func<object, bool> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
if (canExecute == null)
{
throw new ArgumentNullException("canExecute");
}
this._execute = execute;
this._canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
return _canExecute(parameter);
}
else
{
return false;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
private static bool DefaultCanExecute(object parameter)
{
return true;
}
}
public class MyModel
{
public MyModel()
{
FirstButtonCmd = new TestCommand(OnFirstButtonCmd);
SecondButtonCmd = new TestCommand(OnSecondButtonCmd);
}
public ICommand FirstButtonCmd{get;set;}
public ICommand SecondButtonCmd{get;set;}
private void OnFirstButtonCmd()
{
//click first button
}
private void OnSecondButtonCmd()
{
//click second button
}
}
TestingUerControl.xaml
<Button Click={Binding FirstButtonCmd}></Button>
<Button Click={Binding SecondButtonCmd}></Button>
TestingUerControl.xaml.cs
public sealed partial class TestingUerControl : UserControl
{
public TestingUerControl()
{
this.InitializeComponent();
}
}
So i have WPF application with main windoes and 2 UserControls:
HomeView.xaml
OptionsView.xaml
View Model
public class ApplicationViewModel : INotifyPropertyChanged
{
#region Fields
private ICommand changePageCommand;
private ICommand addFilesCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion
public ApplicationViewModel()
{
// Add available pages
PageViewModels.Add(new HomeViewModel() { IsSelected = true });
PageViewModels.Add(new OptionsViewModel() { IsSelected = false });
// Set starting page
CurrentPageViewModel = PageViewModels[0];
}
#region Properties / Commands
public ICommand ChangePageCommand
{
get
{
if (changePageCommand == null)
{
changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return changePageCommand;
}
}
public List<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
_pageViewModels = new List<IPageViewModel>();
return _pageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
#endregion
#region Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == viewModel);
}
#endregion
}
Whan application start
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
ApplicationViewModel context = new ApplicationViewModel();
app.DataContext = context;
app.Show();
}
}
Windows respurces
<Window.Resources>
<DataTemplate DataType="{x:Type home:HomeViewModel}">
<home:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type options:OptionsViewModel}">
<options:OptionsView />
</DataTemplate>
</Window.Resources>
And inside HomeView.xaml i have simple button:
<Button Command="{Binding DataContext.AddFilesCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding}"/>
And i want to add simple Click command, something.
So i try to add this ICommand:
public ICommand AddFilesCommand
{
}
Any suggestions how to add this kind on command that will execute after each Button Click ?
This can be done a lot easier. I would create a class to implement commands easily:
using System;
using System.Windows.Input;
namespace YourNameSpace
{
public class RelayCommand : ICommand
{
private Action Action;
public Command(Action _action)
{
Action = _action;
}
public event EventHandler CanExecuteChanged = (sender, e) => { };
public bool CanExecute(object parameter) => true;
public void Execute(object parameter)
{
Action();
}
}
}
Then create the command (you don't need private ICommand for this):
public ICommand AddFileCommand { get; set; }
And use it like this (in the constructor):
AddFileCommand = new RelayCommand(()=>
{
MethodToExecute();
});
XAML:
<Button Command="{Binding AddFileCommand}"/>
This way your code will be easier to see trough.
I'm using MVVM and have the following problem. My TextBox.Text is bound with UpdateSourceTrigger=LostFocus (thats what the user want). I have a Button with a SaveCommand CommandBinding - this works. Now i have a KeyBinding with Strg+S wich also execute the SaveCommand. And here is the problem: when i m in the Textbox and press Strg+s, the changes are not in the viewmodel.
is there any way to get MVVM Commands with KeyBinding and TextBox UpdateSourceTrigger=LostFocus working together?
some code to check out the problem
<Window>
<Window.InputBindings>
<KeyBinding Key="S" Modifiers="Control" Command="{Binding SaveCommand}"></KeyBinding>
</Window.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding MyText1, UpdateSourceTrigger=LostFocus}" Width="100"></TextBox>
<Button Grid.Row="1" Content="_Save" Command="{Binding SaveCommand}" IsDefault="True"></Button>
</Grid>
</Window>
public partial class MainWindow : Window
{
private Viewmodel _data;
public MainWindow()
{
_data = new Viewmodel();
InitializeComponent();
this.DataContext = _data;
}
}
public class Viewmodel : INPCBase
{
private string _myText1;
private Lazy<DelegateCommand> _save;
public Viewmodel()
{
this._save = new Lazy<DelegateCommand>(()=> new DelegateCommand(this.SaveCommandExecute));
}
private void SaveCommandExecute()
{
MessageBox.Show(MyText1);
}
public string MyText1
{
get { return _myText1; }
set { _myText1 = value; this.NotifyPropertyChanged(()=>MyText1);}
}
public ICommand SaveCommand
{
get { return _save.Value; }
}
}
at the moment i came up with the following workaround. within the usercontrol/views where i define my KeyBindings, i also listen to the PreviewKeyDown event and set the focus to the next element when eg. Strg+S is pressed.
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
var fe = Keyboard.FocusedElement as UIElement;
if (fe != null)
{
fe.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
I have the same problem and end up with attached property for TextBox.
public static bool GetCommitOnSave(DependencyObject obj)
{
return (bool)obj.GetValue(CommitOnSaveProperty);
}
public static void SetCommitOnSave(DependencyObject obj, bool value)
{
obj.SetValue(CommitOnSaveProperty, value);
}
public static readonly DependencyProperty CommitOnSaveProperty =
DependencyProperty.RegisterAttached("CommitOnSave", typeof(bool), typeof(Helper), new PropertyMetadata(false, CommitOnSavePropertyChanged));
private static void CommitOnSavePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if ((bool)e.NewValue)
{
if ((bool)e.NewValue)
{
textBox.KeyDown += TextBox_KeyDown;
}
else
{
textBox.KeyDown -= TextBox_KeyDown;
}
}
}
}
private static void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
var textBox = (TextBox)sender;
if (e.Key == Key.S && Keyboard.Modifiers == ModifierKeys.Control)
{
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
}
}
Using <TextBox Text="{Binding Name}" local:Helper.CommitOnSave="True" />
Of course you can set attached property in style for all TextBoxes in a form.
I think I find the best solution for me. I mix solution #blindmeis and my previous one with using attached property.
I create command which update binding source of actual keyboard focused element:
public class CommitValueCommand : ICommand
{
private static CommitValueCommand _instance;
public static CommitValueCommand Command => _instance ?? (_instance = new CommitValueCommand());
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (Keyboard.FocusedElement is TextBox textBox)
{
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
}
//for combobox etc.
else if (Keyboard.FocusedElement is Selector selector)
{
BindingOperations.GetBindingExpression(selector, Selector.SelectedValueProperty).UpdateSource();
}
}
}
In Execute method of command SaveCommand just at beginning invoke CommitValueCommand.Command.Execute().
I have simplified example:
XAML:
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Name="cb" />
<Button Name="button1" Click="button1_Click" />
Code behind:
public partial class MainWindow : Window
{
private ObservableCollection<MyObject> collection = new ObservableCollection<MyObject>();
public MainWindow()
{
InitializeComponent();
collection.Add(new MyObject(true));
//grid.DataContext = collection[0];
}
private void button1_Click(object sender, RoutedEventArgs e)
{
collection[0].IsSelected = false;
}
}
public class MyObject
{
public bool IsSelected { get; set; }
public MyObject(bool isSelected)
{
this.IsSelected = isSelected;
}
}
The cb.IsChecked doesn't change by button clicking though the collection[0].IsSelected is changed.
Even if I uncomment grid.DataContext = collection[0]; - nothing changed.
In real example I have the same checkbox in the item template of a listbox. So the behaviour is the same - the selection of checkboxes don't change.
You need to implement INotifyPropertyChanged on your MyObject type
Please try the following codes:
public class MyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
public MyObject(bool isSelected)
{
this.IsSelected = isSelected;
}
}
I am using MVVM pattern. I have a
Text box whose Text property is bound to ViewModel's(VM supports INotifyProperyChange) Text property
Button whose command is bound to VM's ICommand property type
You may think of this as a SearchTextBox and SearchButton
The problem I am facing is that when I enter the text in SearchTextBox and click on SearchButton then only the SearchTextBox bound set property implementation is called but the Command for SearchButton click never executes (Note: ICommand CanExecute handler always returns True)
It works fine if I either tab out of SearchTextBox using TAB key or use mouse to move focus away from SearchTextBox and then click the SearchButton. That means do two seperate actions to trigger both the events seperately. Ideally clicking on the SearchButton should result in the SearchTextBox loose focus thus calling Set property and the click on the Search button translates into the command execution.
Code is as below
XAML:
<TextBox Text="{Binding Path=SearchText,Mode=TwoWay}"/>
<Button Content="Search" Width="100" Command="{Binding MySearchCommand}"/>
C#:
public String _SearchText;
public String SearchText
{
get { return _SearchText; }
set
{
_SearchText = value;
OnPropertyChanged("SearchText");
}
}
ICommand implementation is a standard implemenetation with no fancy code and CanExecute handler always returns True
Try to isolate the issue by writing a small test project that reproduces the issue, if you can repro then please post the code. Usually when you repro the issue outside of your main project the problem and the solution become obvious.
I created a sample application to reproduce this problem.
I placed breakpoint and added a Debug.Writeline in SearchText - Set property and MySearchCommandExecute method.
When breakpoints are set, only the SearchText - Set property gets called. I observed that if I remove the breakpoint from SearchText - Set property then both the property and the command are correctly executed. Looks like some problem with VS 2008 but I may be wrong.
The relevant sample code is as below
class SearchViewModel : ViewModelBase
{
public SearchViewModel()
{
}
public String _SearchText;
public String SearchText
{
get { return _SearchText; }
set
{
System.Diagnostics.Debug.WriteLine("Set Membership called");
OnPropertyChanged("SearchText");
}
}
#region Commands
RelayCommand _SearchCommand;
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = new RelayCommand(param => this.MySearchCommandExecute(), param => this.MySearchCommandCanExecute);
}
return _SearchCommand;
}
}
public void MySearchCommandExecute()
{
System.Diagnostics.Debug.WriteLine("MySearchCommandExecute called");
// Do Search
}
public bool MySearchCommandCanExecute
{
get
{
return true;
}
}
#endregion
}
SearchView.xaml
<UserControl x:Class="WpfApplication2.SearchView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="4">
<Label Foreground="Black" FontFamily="Calibri" Width="155" Margin="4,0,4,0" Content="SearchText"/>
<TextBox Foreground="Black" FontFamily="Calibri" Width="155" Margin="4,0,4,0" Text="{Binding Path=SearchText}"/>
</StackPanel>
<Button HorizontalAlignment="Left" Content="Search" Width="100" Command="{Binding SearchCommand}" Margin="8"/>
</StackPanel>
</UserControl>
RelayCommand.cs
// Reference: MSDN sample
class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("relaycommand execute");
_execute = execute;
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
Byte,
Sorry for my late response, but I hope it will become handy anyway. I'm very busy lately so I couldn't debug your code (I'll try to do that when I have more time), but please try my sample code pasted below (It works perfectly for me). As you can see it's extremely simple. I used your xaml, but for Window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new TempViewModel();
}
}
public class TempViewModel : INotifyPropertyChanged
{
private String _searchText;
private ICommand _searchCommand;
#region Commands
protected class Search : ICommand
{
private TempViewModel _viewModel;
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
public void Execute(object parameter)
{
//MessageBox in VM is just for demonstration
MessageBox.Show("command executed with search string: " + this._viewModel._searchText);
}
public Search(TempViewModel viewModel)
{
this._viewModel = viewModel;
}
}
#endregion //Commands
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion //INotifyPropertyChanged
#region Public properties
public String SearchText
{
get
{
return this._searchText;
}
set
{
this._searchText = value;
OnPropertyChanged("SearchText");
}
}
public ICommand SearchCommand
{
get
{
return this._searchCommand;
}
set
{
this._searchCommand = value;
OnPropertyChanged("SearchCommand");
}
}
#endregion //Public properties
public TempViewModel()
{
this.SearchCommand = new Search(this);
this.SearchText = "Sample string";
}
}
Please feel free to ask if you have any further questions.
EDIT: Ah, sorry, but I changed Command="{Binding SearchCommand}" to Command="{Binding Path=SearchCommand}"