Binding commands without PRISM - c#

Since I started using MVVM, I've always used PRISM's DelegateCommand class for binding commands in my view models to button commands in my views. I believe Telerik also has an equivalent of DelegateCommand.
My question is, is there a built in alternative to using 3rd party frameworks such as prism and telerik. If I'm throwing together a quick throwaway application I may not want the hassle of installing packages from NuGet. Is there a way to achieve the same thing using a Func or an Action or a delegate?

No, you still need a Command class that implements ICommand. However, you can write your own DelegateCommand really easily (citation, I wrote this off the top of my head in less than a minute):
public class DelegateCommand : ICommand
{
private Action<object> execute;
public DelegateCommand(Action<object> executeMethod)
{
execute = executeMethod;
}
public bool CanExecute(object param)
{
return true;
}
public void Execute(object param)
{
if (execute != null)
execute(param);
}
}
Use and enjoy! You could take an additional Func<bool, object> parameter if you wanted custom CanExecute behavior instead of returning true.
Note, if you really don't like null as the function, and want it to throw if you try it, just use this constructor instead:
public DelegateCommand(Action<object> executeMethod)
{
if (executeMethod == null)
throw new ArgumentNullException("executeMethod");
execute = executeMethod;
}

Related

Is a command a sort of event handler or why does my button execute the command?

I'm learning about wpf, delegates, event and I have some clue on what does what, but I'm a little lost when it comes to implementing ICommand
I have a class the implements the ICommand interface like this
class RelayCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this (execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
{
this._execute = execute;
this._canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
//throw new NotImplementedException();
return true;
}
public void Execute(object parameter)
{
//throw new NotImplementedException();
this._execute(parameter);
}
public void OnCanExecute()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
And then my ViewModel that uses i.
class PersonViewModel
{
public ICommand ICommandPresenter { get; set; }
public PersonModel PM;
private string _btnString = "";
#region Propertys
public string ButtonString
{
get {return _btnString; }
set
{
if (_btnString.Equals(value))
{
return;
}
_btnString = value;
}
}
public string Name { get { return PM.Name; } set
{
PM.Name = value;
}
}
#endregion Propertys
public PersonViewModel()
{
PM = new PersonModel();
ICommandPresenter = new RelayCommand(ChangeName);
}
public void ChangeName(object a)
{
string s = a as string;
MessageBox.Show("Hello from PersonViewModel Commander: " + s);
}
}
This is where it gets confusing for me. In the RelayCommand class I have an event CanExecuteChanged but that event is never executed/fired. From what I've understood from my reading on events is that you don't "need" to have subscribers to an event, but if you're going to have an event you should atleast have somewhere in the code that executes it. But I don't have that in my code but for some reason my button still does what I command it to do. I still understand that I've clicked the button but I don't have anything in my code that is subscribed to that button.
Why is my button able to execute my code even though I don't have an event connected to it?
Since I don't have any subscribers connected to CanExecuteChanged event does it becomes useless?
Is the command acting like an event? if so, please describe the whole process from clicking the button to executing the command.
CanExecuteChanged is member of ICommand class and, simplifying the things, is used by wpf framework to enable/disable your button depending on result of CanExecute() method. CanExecute is not tight to the code you want to execute when you click the button, but to the condition, when it's legal to be done.
Command executes your code, because you send pointer to your method(ChangeName method) here:
ICommandPresenter = new RelayCommand(ChangeName);
So you are not using CanExecuteChange at all, because you are invoking this constructor:
public RelayCommand(Action<object> execute).
To have CanExecute you have to invoke overloaded constructor that accepts CanExecute predicate:
public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
And to invoke it just pass some function that returns a bool as second parameter:
ICommandPresenter = new RelayCommand(ChangeName, ()=>MyCustomLogicWhenButtonIsActive());
Based on what I saw in .Net's source code, the command assigned to the button's Command property is executed in the following way:
Clicking the button calls the button's OnClick() method (located in the button's ButtonBase base class).
The OnClick() method (see source here) calls the Commands.CommandHelpers.ExecuteCommandSource() method, passing this as the command source parameter.
The ExecuteCommandSource() (see source here) further calls the CriticalExecuteCommandSource() method, passing the same command source as a parameter.
Finally, the CriticalExecuteCommandSource() method (see source here) accesses the command source's Command member, and:
checks if the command's CanExecute() method returns true,
if it's true, it calls the command's Execute() method.
(If you use a RelayCommand implementation, then obviously, the RelayCommand class relays the call to the specific method you passed to its constructor when you instantiated it.)
So, to answer your question whether commands are fired through an event:
The source indicates that the OnClick() method executes the command directly, not through event handling. As you can see in the source, this method does raise the Click event, but it does so separately from the command execution.
Though, it remains unanswered how the OnClick() method is being called in the first place by the framework; I have no idea about that.

RelayCommand best practice

I'm working with RelayCommands (they are in a separate class) for about a month now and I got the feeling they're kind of clunky when declaring them. Below I have 3 ways I can think of how I can declare a RelayCommand.
In a first case I declare my ICommand and then when the ViewModel is loading I construct my RelayCommand that points to a method in my code.
public class MyViewModel
{
public ICommand MyCommand { get; private set; }
public MyViewModel()
{
MyCommand = new RelayCommand(MyMethod, CanMyMethod);
}
private void MyMethod()
{
// Do something here ...
}
private bool CanMyMethod()
{
return string.IsNullOrEmpty(MyString) ? false : true;
}
}
A second method is to do everything at once.
public ICommand MyCommand
{
get
{
return new RelayCommand(
() =>
{
// Do something here ...
},
() =>
string.IsNullOrEmpty(MyString) ? false : true);
}
}
Now, I'm planning to write an application with quite some Commands in a certain ViewModel. I also can't split the ViewModel in smaller ViewModels because all the controls have to work together.
So my questions are:
What is the best approach to declaring and constructing ICommands? Is it one of my approaches or is there an easier way?
How hard is it to maintain the overview with each approach considering there are over 50 ICommands in a single ViewModel.
I'm hoping to release my application on both Windows 7, 8 and 10 in the future. Are there any limitations to RelayCommands I have to take in account if I'm only using .NET4.5?
Besides RelayCommands I also found this project: Caliburn-Micro. It allows you to do something like the code below. Does anyone have an idea how good this works performance wise in comparison to RelayCommands? This is just an extra question and not required to be answered to have a post marked as an answer.
Xaml (View)
<Button x:Name="Login" Content="Log in" />
ViewModel
public bool CanLogin(string username, string password)
{
return !String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password);
}
public string Login(string username, string password)
{
...
}
Refer below answers.
What is the best approach to declaring and constructing ICommands?
Is it one of my approaches or is there an easier way? Ans:You can
take combined approach. If your execute method is very minimal, you
can use RelayCommnd otherwise you can implement your own ICommand in
a separate class. This will improve readability of your viewmodel as
well as modularity of your code.
How hard is it to maintain the overview with each approach considering there are over 50 ICommands in a single ViewModel.
Ans: Cover in Ans 1
I'm hoping to release my application on both Windows 7, 8 and 10 in the future. Are there any limitations to RelayCommands I have to take in account if I'm only using .NET4.5?
Ans: I am not seeing any limitation in Windows 7 or 8 but not sure about Windows 10.
Besides RelayCommands I also found this project: Caliburn-Micro. It allows you to do something like the code below. Does anyone have an idea how good this works performance wise in comparison to RelayCommands? This is just an extra question and not required to be answered to have a post marked as an answer.
Ans: I am not sure about source code of RelayCommands in Caliburn-Micro. But if it using CommandManager to achieve the CanExecute functionality. Then command manager will get triggered for all the user inputs change and hence that will cause performance issues if have some heavy logic in the CanExecute method. Refer Are there any performance implications with CanExecuteCommand?
Here's the pattern that I prefer, which is basically a variation of method 1:
public class MyViewModel
{
private readonly RelayCommand myCommand;
public MyViewModel()
{
this.myCommand = new RelayCommand(this.DoStuff, () => ...);
}
public ICommand MyCommand
{
get { return this.myCommand; }
}
private void DoStuff() { ... }
}
This has the advantage of keeping extra methods on your RelayCommand implementation (like RaiseCanExecuteChanged) around for use from your view model, but exposes only an ICommand instance to consumers.
I agree with Krowi, the first approach is much easier to read. But I would put your relay command into its own class so you can re-use it:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
[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);
}
#endregion
}

What is my mistake in implementing an asynchronous RelayCommand?

I am learning WPF and MVVM at the moment and I faced a problem when i tried to write unit tests for a viewmodel, whose commands invoke async methods. That problem is well-described in this question. That question also has a solution: to write a new Command class with an additional awaitable method that can be awaited in unit tests. But since i use MvvmLight, i decided not to write a new class, but to inherit from the built-in RelayCommand class instead. However, i don't seem to understand how to do it properly. Below is a simplified example that illustrates my problem:
AsyncRelayCommand:
public class AsyncRelayCommand : RelayCommand
{
private readonly Func<Task> _asyncExecute;
public AsyncRelayCommand(Func<Task> asyncExecute)
: base(() => asyncExecute())
{
_asyncExecute = asyncExecute;
}
public AsyncRelayCommand(Func<Task> asyncExecute, Action execute)
: base(execute)
{
_asyncExecute = asyncExecute;
}
public Task ExecuteAsync()
{
return _asyncExecute();
}
//Overriding Execute like this fixes my problem, but the question remains unanswered.
//public override void Execute(object parameter)
//{
// _asyncExecute();
//}
}
My ViewModel (based on the default MvvmLight MainViewModel):
public class MainViewModel : ViewModelBase
{
private string _welcomeTitle = "Welcome!";
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
_welcomeTitle = value;
RaisePropertyChanged("WelcomeTitle");
}
}
public AsyncRelayCommand Command { get; private set; }
public MainViewModel(IDataService dataService)
{
Command = new AsyncRelayCommand(CommandExecute); //First variant
Command = new AsyncRelayCommand(CommandExecute, () => CommandExecute()); //Second variant
}
private async Task CommandExecute()
{
WelcomeTitle = "Command in progress";
await Task.Delay(1500);
WelcomeTitle = "Command completed";
}
}
As far as i understand it, both First and Second variants should invoke different constructors, but lead to the same result. However, only the second variant works the way i expect it to. The first one behaves strangely, for example, if i press the button, that is binded to Command once, it works ok, but if i try to press it a second time a few seconds later, it simply does nothing.
My understanding of async and await is far from complete. Please explain me why the two variants of instantiating the Command property behave so differently.
P.S.: this behavior is noticeable only when i inherit from RelayCommand. A newly created class that implements ICommand and has the same two constructors works as expected.
OK, I think I found the problem. RelayCommand uses a WeakAction to allow the owner (target) of the Action to be garbage collected. I'm not sure why they made this design decision.
So, in the working example where the () => CommandExecute() is in the view model constructor, the compiler is generating a private method on your constructor that looks like this:
[CompilerGenerated]
private void <.ctor>b__0()
{
this.CommandExecute();
}
Which works fine because the view model is not eligible for garbage collection.
However, in the odd-behavior example where the () => asyncExecute() is in the constructor, the lambda closes over the asyncExecute variable, causing a separate type to be created for that closure:
[CompilerGenerated]
private sealed class <>c__DisplayClass2
{
public Func<Task> asyncExecute;
public void <.ctor>b__0()
{
this.asyncExecute();
}
}
This time, the actual target of the Action is an instance of <>c__DisplayClass2, which is never saved anywhere. Since WeakAction only saves a weak reference, the instance of that type is eligible for garbage collection, and that's why it stops working.
If this analysis is correct, then you should always either pass a local method to RelayCommand (i.e., do not create lambda closures), or capture a (strong) reference to the resulting Action yourself:
private readonly Func<Task> _asyncExecute;
private readonly Action _execute;
public AsyncRelayCommand(Func<Task> asyncExecute)
: this(asyncExecute, () => asyncExecute())
{
}
private AsyncRelayCommand(Func<Task> asyncExecute, Action execute)
: base(execute)
{
_asyncExecute = asyncExecute;
_execute = execute;
}
Note that this actually has nothing to do with async; it's purely a question of lambda closures. I suspect it's the same underlying issue as this one regarding lambda closures with Messenger.

Relay/ICommand vs DelegateCommand -- Differences

As far as I can tell the below code can be changed from Relay/ICommand Command to Delegate command and still bind the commands the same way! If I am wrong what are the differences and uses of each.
private DelegateCommand something;
public DelegateCommand Something
Here is the full implementation
private RelayCommand something;
public ICommand Something
{
get
{
if (something == null)
something = new RelayCommand(SomethingMethod, CanSomething);
return something;
}
}
private bool CanSomething(object parameter)
{
//just for readability return true
return true;
}
private void SomethingMethod(object parameter)
{
using (DatabaseContext context = new DatabaseContext())
{
try { }
catch(Exception ex)
{
throw new ApplicationException(string.Format("Something {0} to {1}", file, directory), ex);
}
}
}
Neither DelegateCommand nor RelayCommand exist in the framework itself. They are provided by third party libraries.
Both are an implementation of ICommand which works by accepting a delegate and using that to provide the ICommand implementation. As such, both classes have the same intent, and work in basically the same manner.
As for differences - there may be some subtle diffferences, depending on which framework you're using. For example, Prism's DelegateCommand<T> also has a concept of IActiveAware, which is used for building composite commands.

ICommand CanExecute not triggering after PropertyChanged?

I got a WPF application that shows a button bound to a command like that:
<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">
The command is defined like that:
public ICommand TestrunStartCommand
{
get { return new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress); }
}
public bool IsTestrunInProgress
{
get{
return _isTestrunInProgress;
}
set{
_isTestrunInProgress = value;
RaisePropertyChanged(IsTestrunInProgressPropertyName);
}
}
The problem is, the button won't be enabled immediately after I set IsTestrunInProgress to false, but only after I click inside the application window.
Could you help me understand this behaviour and show me how to fix this?
Further reading:
wpf command pattern - when does it query canexecute
The ICommand interface exposes an event ICommand.CanExecuteChanged which is used to inform the UI when to re-determine the IsEnabled state of command driven UI components.
Depending upon the implementation of the RelayCommand you are using, you may need to raise this event; Many implementations expose a method such as RelayCommand.RaiseCanExecuteChanged() which you can invoke to force the UI to refresh.
Some implementations of the RelayCommand make use of CommandManager.RequerySuggested, in which case you will need to call CommandManager.InvalidateRequerySuggested() to force the UI to refresh.
Long story short, you will need to call one of these methods from your property setter.
Update
As the state of the button is being determined when the active focus is changing, I believe the CommandManager is being used. So in the setter of your property, after assigning the backing field, invoke CommandManager.InvalidateRequerySuggested().
Update 2
The RelayCommand implementation is from the MVVM light toolkit. When consumed from WPF/.NET, the implementation wraps the methods and events exposed from the CommandManager. This will mean that these commands work automagically in the majority of situations (where the UI is altered, or the focused element is changed). But in a few cases, such as this one, you will need to manually force the command to re-query. The proper way to do this using this library would be to call the RaiseCanExecuteChanged() method on the RelayCommand.
This is so important and easy to miss, I am repeating what #Samir said in a comment. Mr Laurent Bugnion wrote in his blog:
In WPF 4 and WPF 4.5, however, there is a catch: The CommandManager will stop working after you upgrade MVVM Light to V5. What you will observe is that your UI elements (buttons, etc) will stop getting disabled/enabled when the RelayCommand’s CanExecute delegate returns false.
If you are in a hurry, here is the fix: In any class that uses the RelayCommand, replace the line saying:
using GalaSoft.MvvmLight.Command;
with:
using GalaSoft.MvvmLight.CommandWpf;
You can try with CommandManager.InvalidateRequerySuggested.
Anyway this did not help me sometimes in the past. For me the best solution turned out to be to bind the boolean property to the Button.IsEnabled dependency property.
In your case something like
IsEnabled={Binding IsTestrunInProgress}
The issue is, the ICommand Property TestrunStartCommand is always returning a new command object whenever it is accessed.
A simple fix is to create the ICommand object once and use it again and again.
private ICommand _testRunCommand = null;
public ICommand TestrunStartCommand
{
get
{
return _testRunCommand ?? (_testRunCommand = new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress));
}
}
This was quite a simple fix and it worked for me.
Addition to Riegardt Steyn's answer above: https://stackoverflow.com/a/33503341/1964969
If you don't want to change Command to CommandWpf usage (as that two RelayCommand versions are not compatible inbetween), another workaround could be to not instantiate a command at the declaration place. Use constructor code instead:
public class SomeVMClass
{
// CanExecute won't work:
// Declaration and instantiation same place
public RelayCommand MyCommand1 => new RelayCommand(MyBusinessLogic, MyCanExecuteValidator);
// CanExecute will work
// Declaration only
public RelayCommand MyCommand2 { get; private set; }
public SomeVMClass()
{
// Let's instantiate our declared command
MyCommand2 = new RelayCommand(MyBusinessLogic, MyCanExecuteValidator);
...
Blockquote
In your Command class change CanExcutedChanged to this
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
This is example of my command class
public class SaveConfigCommand : ICommand
{
public MyViewModel VM { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public SaveConfigCommand(MyViewModel vm)
{
VM = vm;
}
public bool CanExecute(object? parameter)
{
MyObjectModel model = parameter as MyObjectModel;
if (model == null)
return false;
// Validate others properties here
return true;
}
public void Execute(object? parameter)
{
VM.MyMethodInViewModel();
}
}

Categories

Resources