in my WPF UI, I use RoutedCommands that I refer to in my xaml via the following code:
Command="viewModel:MessageListViewModel.DeleteMessagesCommand"
I don't like this static link to my ViewModel class,I think this is not as nice as creating a custom ICommand implementation and use a syntax like the following
Command="{Binding DeleteMessagesCommand}"
Having created one, I notice one major drawback of what I've done: RoutedCommands utilize the CommandManager and (in some way that is completely opaque to me) fire the CommandManager.RequerySuggested event, so that their CanExecute Method is requeried automatically. As for my custom implementation, CanExecute is only fired once at startup and never again after that.
Does anybody have an elegant solution for this?
Just implement the CanExecuteChanged event as follows:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
When you assign the command to a control, it subscribes to the CanExecuteChanged event. If you "redirect" it to the CommandManager.RequerySuggested event, the control will be notified whenever CommandManager.RequerySuggested is triggered.
I very much prefer the DelegateCommand implementation of Prism for viewmodel binding (http://msdn.microsoft.com/en-us/library/ff654132.aspx). You can invoke CanExecute() on every command invoker by calling RaiseCanExecuteChanged on it.
Simple usage example:
public class ViewModel
{
public ViewModel()
{
Command = new DelegateCommand<object>(x => CommandAction(), x => CanCommandAction());
}
bool state;
public void ChangeState(bool value)
{
state = value;
Command.RaiseCanExecuteChanged();
}
public DelegateCommand<object> Command {get; private set;}
private void CommandAction()
{
//do smthn
}
private bool CanCommandAction() { return true == state; }
}
//and binding as usual
Command="{Binding Command}"
Related
I'm using MVVM light toolkit to handle a button click. If I do:
CustomerSaveCommand = new RelayCommand(
() => CustomerSave(),
()=> true);
private void CustomerSave() {
customer.Address="My Street";
}
The function is invoked but the Address field bound in the UI is not updated.
If I put customer.Address="1234" in the ViewModel constructor, the UI IS updated. What am I doing wrong?
EDITED:
The problem is really strange: if I do viewModel.customer.City = "CITY1" in the window load it runs, if I add a button and, in the code-behind click, I add viewModel.customer.City = "CITY2" it does not work.
The customer object in your viewmodel needs to implement the INotifyPropertyChanged interface.
Then in the Address Property setter, you would invoke the PropertyChanged event.
Alternatively, your viewModel can implement the INotifyPropertyChanged interface, and could wrap the Address property and call the PropertyChanged event. You would have to update your bindings, but your model objects wouldn't have to implement any interfaces.
The reason you're seeing that the address is showing up when you modify the object in the constructor is because binding has not taken place yet. In order for the UI to be updated you need to instruct the binding engine that a property binding has changed. To do that you use the INotifyPropertyChanged interface.
try something like this:
public class AutoDelegateCommand : RelayCommand, ICommand
{
public AutoDelegateCommand(Action<object> execute)
: base(execute)
{
}
public AutoDelegateCommand(Action<object> execute, Predicate<object> canExecute)
: base(execute, canExecute)
{
}
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
i have an XamDataPresenter (XamDataGrid) bound to a collection in the ViewModel:
XAML:
<igDP:XamDataPresenter x:Name="dataPresenter" DataSource="{Binding Path=AppServers, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True">
</igDP:XamDataPresenter>
Code:
public ShellViewModel()
{
AppServers = new BindingListCollectionView(new BindingList<AppServer>(_context.GetAllAppServers()));
AppServers.CurrentChanged += new EventHandler(AppServers_CurrentChanged);
}
void AppServers_CurrentChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(() => CanSaveAppServers);
NotifyOfPropertyChange(() => CanDeleteAppServers);
}
The CanSaveAppServers property:
public bool CanSaveAppServers
{
get
{
return (_appServers.SourceCollection as BindingList<AppServer>).Any(x => x.ChangeTracker.State != ObjectState.Unchanged);
}
}
The CanSaveAppServers property should be false if an item of the collection is changed. But how is the CanSaveAppServers called? Another event? Or the wrong collection type? Shouldn't this be done automatically in some way?
Thanks in advance.
If you are letting Caliburn bind via naming conventions, then you have a public method named SaveAppServers. Caliburn creates an ICommand that is bound to the Button so that when the button is clicked, ICommand's Execute() is called. In the meantime, there is a CanExecute() method on ICommand that is used to determine whether the button is enabled or not.
When you call NotifyOfPropertyChange(() => CanSaveAppServers), this ends up making the ICommand raise its CanExecuteChanged event, which makes WPF refresh by calling CanExecute() again, which under the covers is getting CanSaveAppServers.
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
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
Does anyone know how I can force CanExecute to get called on a custom command (Josh Smith's RelayCommand)?
Typically, CanExecute is called whenever interaction occurs on the UI. If I click something, my commands are updated.
I have a situation where the condition for CanExecute is getting turned on/off by a timer behind the scenes. Because this is not driven by user interaction, CanExecute is not called until the user interacts with the UI. The end result is that my Button remains enabled/disabled until the user clicks on it. After the click, it is updated correctly. Sometimes the Button appears enabled, but when the user clicks it changes to disabled instead of firing.
How can I force an update in code when the timer changes the property that affects CanExecute? I tried firing PropertyChanged (INotifyPropertyChanged) on the property that affects CanExecute, but that did not help.
Example XAML:
<Button Content="Button" Command="{Binding Cmd}"/>
Example code behind:
private ICommand m_cmd;
public ICommand Cmd
{
if (m_cmd == null)
m_cmd = new RelayCommand(
(param) => Process(),
(param) => EnableButton);
return m_cmd;
}
// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
Calling System.Windows.Input.CommandManager.InvalidateRequerySuggested() forces the CommandManager to raise the RequerySuggested event.
Remarks: The CommandManager only pays attention to certain conditions in determining when the command target has changed, such as change in keyboard focus. In situations where the CommandManager does not sufficiently determine a change in conditions that cause a command to not be able to execute, InvalidateRequerySuggested can be called to force the CommandManager to raise the RequerySuggested event.
I was aware of CommandManager.InvalidateRequerySuggested() a long time ago, and used it, but it wasn't working for me sometimes. I finally figured out why this was the case! Even though it doesn't throw like some other actions, you HAVE to call it on the main thread.
Calling it on a background thread will appear to work, but sometimes leave the UI disabled. I really hope this helps somebody, and saves them the hours I just wasted.
A workaround for that is binding IsEnabled to a property:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
and then implement this property in your ViewModel. This also makes it a bit easier for the UnitTesting to work with the properties rather than commands to see if the command can be executed at a certain point of time.
I, personally, find it more convenient.
Probably this variant will suit you:
public interface IRelayCommand : ICommand
{
void UpdateCanExecuteState();
}
Implementation:
public class RelayCommand : IRelayCommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute = null;
readonly Action<Object> _executeAction = null;
public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
Using simple:
public IRelayCommand EditCommand { get; protected set; }
...
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted);
protected override bool CanEditCommandExecuted(object obj)
{
return SelectedItem != null ;
}
protected override void EditCommandExecuted(object obj)
{
// Do something
}
...
public TEntity SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
//Refresh can execute
EditCommand.UpdateCanExecuteState();
RaisePropertyChanged(() => SelectedItem);
}
}
XAML:
<Button Content="Edit" Command="{Binding EditCommand}"/>
Thanks guys for the tips. Here's a bit of code on how to marshal that call from a BG thread to the UI thread:
private SynchronizationContext syncCtx; // member variable
In the constructor:
syncCtx = SynchronizationContext.Current;
On the background thread, to trigger the requery:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
Hope that helps.
-- Michael
To update only a single GalaSoft.MvvmLight.CommandWpf.RelayCommand you could use
mycommand.RaiseCanExecuteChanged();
and for me i've created an Extension method:
public static class ExtensionMethods
{
public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
}