Simple WPF MVVM Commanding Issue - What's Wrong With This Code? - wpf

I'm trying to set up a command on a button in my UI using MVVM. The command doesn't execute when I click the button, though. The code is based off of Jason Dolinger's example (link in 3rd paragraph).
It seems like it should be pretty simple, so I'm sure I'll feel silly once I find out what's wrong.
Relevant code bits follow. The command is as follows (very simple):
public class NavigateCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
MessageBox.Show("Executed.");
}
}
The XAML looks like:
<Button x:Name="btn_ProjectManager" Command="{Binding Navigate}" Content="Test Button">
The ViewModel looks like:
public class HomeScreenViewModel : DependencyObject
{
public ICommand Navigate;
public HomeScreenViewModel()
{
this.Navigate = new NavigateCommand();
}
}

Navigate should be a property. Binding works only with properties

Related

WPF Attach a command to a textbox on return key in NET 3.5

I am trying to attach a command and a commandparameter to a textbox on return key but without success. The parameter is the current text in the same textbox.
<TextBox x:Name="txtSearch">
<TextBox.InputBindings>
<KeyBinding Command="{Binding SearchCommand}"
CommandParameter="{Binding Path=Text, ElementName=txtSearch}" Key="Return" />
</TextBox.InputBindings>
</TextBox>
Basically I want to execute the command when user clicks on return/enter key and pass as a parameter the current text in the textbox.
I have found this link where it is said that in .NET 3.5 command parameter for keybinding is not accepting bindings. So a solution is proposed by code in code-behind but how can I pass a parameter to the command from the code?
First, you'll need to add the KeyBinding to your TextBox and set its Command on code-behind. Just add this in the constructor of your View:
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
KeyBinding kb = new KeyBinding();
kb.Command = (DataContext as MyViewModel).SearchCommand;
kb.Key = Key.Enter;
txtSearch.InputBindings.Add(kb);
}
Then, you can bind the Text property of the TextBox named txtSearch to a property of your ViewModel. This way you don't need to pass a parameter as you can use the value of that property in your ViewModel inside the code that executes your Command.
Your ViewModel should look like this:
public class MyViewModel : ObservableObject
{
private string _txtSearch;
public string TxtSearch
{
get { return _txtSearch; }
set
{
if (value != _txtSearch)
{
_txtSearch = value;
OnPropertyChanged("TxtSearch");
}
}
}
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
{
_searchCommand = new RelayCommand(p => canSearch(), p => search());
}
return _searchCommand;
}
}
private bool canSearch()
{
//implement canExecute logic.
}
private void search()
{
string text = TxtSearch; //here you'll have the string that represents the text of the TextBox txtSearch
//DoSomething
}
}
If you have access to C# 6 (Visual Studio 2015 and later versions), you can alter the call to the OnPropertyChanged to: OnPropertyChanged(nameof(TxtSearch));. This way you get rid of the "magic string" and eventual renaming of the property won't cause any problem for you.
And then your XAML should look like this: (Notice that you need to specify that te UpdateSourceTrigger must be PropertyChanged, so that your TxtSearch property of your ViewModel stays up to date when you hit the Enter key on your TextBox.
<TextBox Text="{Binding TxtSearch, UpdateSourceTrigger=PropertyChanged}" x:Name="txtSearch"/>
Your ViewModel needs to implement INotifyPropertyChanged and you need a proper ICommand implementation. Here I'll use the RelayCommand.
Those implementations are shown below.
Since your framework is .NET 3.5, implement it like this:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This is a implementation of the RelayCommand:
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}

ICommand help to replace button events

I am just after some information regarding ICommands,
On my WPF application, I have onClick events that adds to a ObservableCollection. So (ObservableCollection.Add()
However, I have 2 similar events to add to a collection also. So I hear I could use the ICommand interface to "execute" for adding/Editing/Removing etc, so I don't need these separate events.
Can someone provide me a example of how I can do this in MVVM. (All adds are in my ViewModel)
Thanks
You might want to look into the "RelayCommand" - it's a common implementation of an ICommand that will simplify your view-model code, allowing you to specify delegates for the ICommand's "Execute" and "CanExecute" methods. You'll find plenty of implementations on the web, but this is the one I use:
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this._execute = execute;
this._canExecute = canExecute;
}
public virtual bool CanExecute(object parameter)
{
return this._canExecute == null || this._canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public virtual void Execute(object parameter)
{
this._execute(parameter);
}
}
In your VM, expose a command like this:-
public ICommand FooCommand
{
get
{
if (_fooCommand == null)
{
_fooCommand = new RelayCommand(ExecuteFooCommand, CanDoFooCommand);
}
return _fooCommand;
}
}
private void ExecuteFooCommand(object commandParameter)
{
// Code to execute the command.
}
private bool CanDoFooCommand()
{
// Code that indicates whether the command can be executed.
// This will manifest itself in the view by enabling/disabling the button.
}
As the RelayCommand ctr parameters are delegates, you can of course do stuff like this:-
new RelayCommand(o => { // do something }, o => true);
Finally, bind your command to your view button:-
<Button Content="Click me" Command="{Binding FooCommand}" ... />
You can also pass parameters to the command delegate:-
<Button Content="Click me" Command="{Binding FooCommand}" CommandParamter="123" />
(Written out from memory so may not be 100% syntactically correct!)
Going one step further...
To simplify things even more, I use a dynamic property to expose VM commands to the view. In my VM base class I have the following property:-
public dynamic Commands
{
get
{
return _commands;
}
}
Then in a VM's constructor I can create all of its commands like this:-
Commands.FooCommand = new RelayCommand(.....
Commands.BarCommand = ..etc..
In my XAML I bind commands like this:- Command={Binding Commands.FooCommand}.
It's a timesaver as it just means I can hang as many commands off of a single property as I want, rather than expose each one as a separate property as in my earlier example.

Calling a method of a UserControl somewhere in MVVM

I have the following scenario:
I have a user control, let's say UserControl.xaml
In the code behind of this control I have the method DoSomething()
I have viewmodel for this control UserControlViewModel.cs
I need to call usercontrol's DoSomething() method somewhere. Any ideas how to accomplish this?
Thanks!
If I really had to do this, then using the DataContextChanged event may help.
Here's a solution with hopefully minimal coupling between the view and the view-model.
public partial class MainWindow : IMainWindow
{
public MainWindow()
{
this.DataContextChanged += this.MainWindowDataContextChanged;
this.InitializeComponent();
}
private void MainWindowDataContextChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
var vm = this.DataContext as IMainWindowViewModel;
if (vm != null)
{
vm.View = this;
}
}
public void DoSomething()
{
Debug.WriteLine("Do something in the view");
}
}
public interface IMainWindow
{
void DoSomething();
}
public class MainWindowViewModel : IMainWindowViewModel
{
public MainWindowViewModel()
{
this.DoSomethingCommand = new RelayCommand(this.DoSomething);
}
public ICommand DoSomethingCommand { get; set; }
private void DoSomething()
{
Debug.WriteLine("Do something in the view model");
var view = this.View;
if (view != null)
{
view.DoSomething();
}
}
public IMainWindow View { get; set; }
}
public interface IMainWindowViewModel
{
IMainWindow View { get; set; }
}
You really should be using an MVVM framework if you're doing MVVM. A framework would provide a mechanism from which you can invoke a verb (method) on your view model from your view. Caliburn.Micro for example provides Actions.
It sounds as though your application is incorrectly structured.
What does
DoSomething()
do, that isn't reacting to a change in a bound property of the ViewModel?
If you really need to trigger something in the code behind of the View from the ViewModel, use a messaging handler such as the one in the Galasoft MVVMLight framework.

Command Binding to Routed Event in WPF User-control

I want to bind Viewmodel command to Usercontrol's Routed Event.
Here is the detailed explanation of what I have.
I have a User Control which have one Image (which shows image) and one Button at bottom (Button to remove Image). I am using a Usercontrol in a ListView.
In my Usercontrol's Code behind I have a RoutedEventHandler to remove the Image:
public event RoutedEventHandler RemoveImage;
In the window where I use this Usercontrol, I have put:
<uc:ucImageListItem x:Name="ImageListItem" RemoveImage="ImageListItem_RemoveImage" />
This code works correctly if My code to remove image is in code behind. but I want to Bind command of Viewmodel to RemoveImage RoutedEvent.
Probably like (not correct)
<uc:ucImageListItem x:Name="ImageListItem" RemoveImage="{binding CommandtoRemove}" />
How to achieve this?
I found something related to RoutedCommand or DependancyProperty, but could not find any proper way, How to use them.
Let me know if I need to further clear my question.
Thanks in anticipation.
Hi this piece of code shows how to call command:
Command handler
public class CommandHandler : ICommand
{
public CommandHandler(Action<object> action,Func<object,bool> canexecute)
{
_action = action;
_canExecute = canexecute;
}
Action<object> _action;
Func<object, bool> _canExecute;
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}
ViewModel
public class MainViewModel
{
private CommandHandler _buttonCommand;
public CommandHandler ButtonCommand
{
get
{
return _buttonCommand ?? (_buttonCommand = new CommandHandler((param) => OnButtonCommand(param),(param)=>true));
}
}
private void OnButtonCommand(object obj)
{
//DO things here whatever you want to do on Button click
}
}
View
<Button Command="{Binding ButtonCommand}" Content="ok"/>
you need to pass two parameters to CommandHandler Constructor one is Action that you want to fire on Command and second param is func that must return bool. If func evaluates to true only then the Action of Command is fired.And the param in action and func is what you will bind to the CommandParameter in my case above it will be null as I havent binded the CommandParameter.I hope this will help.

WPF ViewModel Commands CanExecute issue

I'm having some difficulty with Context Menu commands on my View Model.
I'm implementing the ICommand interface for each command within the View Model, then creating a ContextMenu within the resources of the View (MainWindow), and using a CommandReference from the MVVMToolkit to access the current DataContext (ViewModel) Commands.
When I debug the application, it appears that the CanExecute method on the command is not being called except at the creation of the window, therefore my Context MenuItems are not being enabled or disabled as I would have expected.
I've cooked up a simple sample (attached here) which is indicative of my actual application and summarised below. Any help would be greatly appreciated!
This is the ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
The DisplayValueCommand is such:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
And finally, the view is defined in Xaml:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
To complete Will's answer, here's a "standard" implementation of the CanExecuteChanged event :
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(from Josh Smith's RelayCommand class)
By the way, you should probably consider using RelayCommand or DelegateCommand : you'll quickly get tired of creating new command classes for each and every command of you ViewModels...
You have to keep track of when the status of CanExecute has changed and fire the ICommand.CanExecuteChanged event.
Also, you might find that it doesn't always work, and in these cases a call to CommandManager.InvalidateRequerySuggested() is required to kick the command manager in the ass.
If you find that this takes too long, check out the answer to this question.
Thank you for the speedy replies. This approach does work if you are binding the commands to a standard Button in the Window (which has access to the View Model via its DataContext), for example; CanExecute is shown to be called quite frequently when using the CommandManager as you suggest on ICommand implementing classes or by using RelayCommand and DelegateCommand.
However, binding the same commands via a CommandReference in the ContextMenu
do not act in the same way.
In order for the same behaviour, I must also include the EventHandler from Josh Smith's RelayCommand, within CommandReference, but in doing so I must comment out some code from within the OnCommandChanged Method. I'm not entirely sure why it is there, perhaps it is preventing event memory leaks (at a guess!)?
public class CommandReference : Freezable, ICommand
{
public CommandReference()
{
// Blank
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
//if (oldCommand != null)
//{
// oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
//}
//if (newCommand != null)
//{
// newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
//}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}
However, binding the same commands via a CommandReference in the
ContextMenu do not act in the same way.
That's a bug in CommandReference implementation. It follows from these two points:
It is recommended that the implementers of ICommand.CanExecuteChanged hold only weak references to the handlers (see this answer).
Consumers of ICommand.CanExecuteChanged should expect (1) and hence should hold strong references to the handlers they register with ICommand.CanExecuteChanged
The common implementations of RelayCommand and DelegateCommand abide by (1). The CommandReference implementation doesn't abide by (2) when it subscribes to newCommand.CanExecuteChanged. So the handler object is collected and after that CommandReference no longer gets any notifications that it was counting on.
The fix is to hold a strong ref to the handler in CommandReference:
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
In order for the same behaviour, I must also include the EventHandler
from Josh Smith's RelayCommand, within CommandReference, but in doing
so I must comment out some code from within the OnCommandChanged
Method. I'm not entirely sure why it is there, perhaps it is
preventing event memory leaks (at a guess!)?
Note that your approach of forwarding subscription to CommandManager.RequerySuggested also eliminates the bug (there's no more unreferenced handler to begin with), but it handicaps the CommandReference functionality. The command with which CommandReference is associated is free to raise CanExecuteChanged directly (instead of relying on CommandManager to issue a requery request), but this event would be swallowed and never reach the command source bound to the CommandReference. This should also answer your question as to why CommandReference is implemented by subscribing to newCommand.CanExecuteChanged.
UPDATE: submitted an issue on CodePlex
An easier solution for me, was to set the CommandTarget on the MenuItem.
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
More info: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

Resources