The current step of learning MVVM is RelayCommand for me.
So i came up with this RelayCommand class:
Relay Command class
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute ?? (x => true);
}
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;
}
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
View Code-Behind
To test if CanExecute is true or false, I created a Click Event which is calling the Command if CanExecute == true or Show an Error Message when CanExecute == false.
if (sender is Button button)
{
if (_viewModel.MyCommand.CanExecute(button.Tag)) // Also testet to set this parameter `null`
_viewModel.MyCommand.Execute(button.Tag);
else
ErrorMessage.Error("CanExecute = false");
}
ViewModel
In my ViewModel I created the Command and added a Thread.Sleep() to have time that canExecute can show me the ErrorMessage from the Code-Behind.
public ICommand MyCommand { get; set; }
public ViewModel()
{
MyCommand = new RelayCommand(MyCommandMethod);
}
public async void MyCommandMethod(object obj)
{
await Task.Run(() =>
{
Thread.Sleep(5000);
ErrorMessage.Error(obj as string);
});
}
The Problem now is, that if I click the Button 5 times for example, that MyCommandMetod() is used 5 times. So CanExecute will never change.
But why isn't it changing?
I understand RelayCommand as this:
1st - Button is clicked
2nd - canExecute = false (wait till process is finished)
3rd - canExecute = true
4th - Button can be executed again.
So that u can't spam Button clicks and crash the application if for example someone use SpeedClicker and clicks 1.000.000 times a seconds or so.
You have to pass some can-execute-logic to the command when creating it:
public ViewModel()
{
MyCommand = new RelayCommand(MyCommandMethod, MyCanExecutePredicate);
}
private bool MyCanExecutePredicate( object commandParameter )
{
// TODO: decide whether or not MyCommandMethod is allowed to execute right now
}
Example: if you want to allow only one command execution at a time, you could come up with something along these lines:
public async void MyCommandMethod(object obj)
{
_myCanExecute = false;
MyCommand.Refresh();
await Task.Run(() =>
{
Thread.Sleep(5000);
ErrorMessage.Error(obj as string);
});
_myCanExecute = true;
MyCommand.Refresh();
}
private bool MyCanExecutePredicate( object commandParameter )
{
return _myCanExecute;
}
Related
I have two methods that do almost the two things:
public static void ShowThing()
{
// code..
}
and
public static bool TryShowThing()
{
if(condition)
{
// same code above..
return true;
}
return false;
}
At the moment I'm binding a button's Command to the void method and it does what it should.
Problem is that now I'm cleaning up the code and to avoid coupling I wanted to bind the button to the bool method and that won't work.
Is Command={Binding BooleandReturningMedhod} even allowed in xaml?
Apparently nobody on the internet has ever had this problem before so I think I'm missing something here...
You cannot bind directly to a method.
What i think what you really wanna achieve is something like this
Code:
ShowThingCommand { get; } = new RelayCommand((o) => ShowThing(),(o) => condition)
RelayCommand:
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
XAML:
<Button Command={Binding ShowThingCommand } />
Important part is the CanExecute method, when it returns false your Button gets disabled
Continuing with my struggle with Static variables and XAML, I can't work around the command bindings greying out the button.
The code in View Model:
public static ICommand CancelCalender => _cancelCalender
?? (_cancelCalender = new CommandHandler(CancelCalender_Button, _canExecute));
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
The reference,
xmlns:viewModels="clr-namespace:StaffShiftManager.ViewModels"
And these are the ways I tried to bind the command variable:
Command="{x:Static viewModels:ViewModelBase.CancelCalender}"
And
Command="viewModels:ViewModelBase.CancelCalender"
And
Command="{Binding Source={x:Static viewModels:ViewModelBase.CancelCalender}}"
Is there something that I am missing? Any help would be greatly appreciated.
Basically in the ICommand CanExecute() method, this returns wether or not the Command is enabled \ can execute.
Returning false from the CanExecute() will disable (gray out) the button.
Now I have modified your code slightly to provide a Func<bool> as the CanExecute() handler. What will happen here is every time the Command Execution is re-queried it will execute your canExecute method.
public class CommandHandler : ICommand
{
public CommandHandler(Action execute)
:this(execute, null)
{
}
public CommandHandler(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException(nameof(execute));
_executeHandler = execute;
_canExecuteHandler = canExecute ?? (() => true);
}
Func<bool> _canExecuteHandler = () => true;
Action _executeHandler;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecuteHandler();
}
public void Execute(object parameter)
{
_executeHandler?.Invoke();
}
}
Not that the default implementation for the canExecute method is to return true. Even passing a null Func to the constructor will still result in true.
Just to add one more thing one of my favorite command binders (much more advanced than above) is using the DelegateCommand. I dont remeber where I found the original source (as I did not write it) but is much more advanced.
In a Silverlight app that is written with MVVM I want to enable/disable my view based on some stuff.
In the constructor of the View class in code behind I can say something like this and it disables the form:
public MyForm1View()
{
InitializeComponent();
if(this.DataContext == null)
{
this.IsEnabled = False;
}
}
The issue is when there is no data to load, I am showing a gray overlay screen on top of my form to the user with a link on that gray overlay that says "Create a New Record"....now the problem is that if I disable my form like that above then How can I re-enable it when they click that CreateNewRecord link?
But how can I reenable it again from the view-model? Maybe I should have an Action on my ViewModel and when it's called on the ViewModel, it calls a method that's wired up in the code behind of the View ? But how to code this idea?
I would suggest few things:
simple wrapper for ICommand Interface:
public class DelegateCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public DelegateCommand(Action execute, Func<bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (this.canExecute != null)
{
return this.canExecute();
}
else
{
return true;
}
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
execute();
}
public void RaiseExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, new EventArgs());
}
}
}
ViewModel:
public class ViewModel : INotifyPropertyChanged {
public void ViewModel() {
SwitchCommand = new DelegateCommand(() => this.IsEnabled = true, () => true);
}
public DelegateCommand SwitchCommand {get;set;}
private bool isEnabled;
public bool IsEnabled {
get {
return isEnabled;
}
set {
isEnabled = value;
NotifyPropertyChanged("IsEnabled");
}
// here, InotifyPropertyChanged implementation, dozens of sample available
}
Xaml:
as example:
<Button Command={Binding SwitchCommand} /> bind command to click.
So, what's left is to set ViewModel to View, via view constructor, of IoC if you use it.
hope that help.
i want to start a task when a relay command is called, however i want to disable the button as long as that task is running
take this example
private ICommand update;
public ICommand Update
{
get
{
if (update == null)
{
update = new RelayCommand(
param => Task.Factory.StartNew(()=> StartUpdate()),
param => true); //true means the button will always be enabled
}
return update;
}
}
what is the best way to check if that task is running?
here is my solution but not sure if its the best way
class Vm : ObservableObject
{
Task T;
public Vm()
{
T = new Task(() => doStuff());
}
private ICommand myCommand;
public ICommand MyCommand
{
get { return myCommand ?? (myCommand = new RelayCommand( p => { T = new Task(() => doStuff()); T.Start(); }, p => T.Status != TaskStatus.Running)); }
}
private void doStuff()
{
System.Threading.Thread.Sleep(5000);
}
}
Update : Every answer here works fine, but still they dont agree with each other, and i just reached a 100 reputation , i start a bounty whenever i reach 100, so what i am looking for is an implementation for an optimal non memory leaking asynchronous RelayCommand that executes within a task in .net 4.0
I strongly recommend that you avoid new Task as well as Task.Factory.StartNew. The proper way to start an asynchronous task on a background thread is Task.Run.
You can create an asynchronous RelayCommand easily using this pattern:
private bool updateInProgress;
private ICommand update;
public ICommand Update
{
get
{
if (update == null)
{
update = new RelayCommand(
async () =>
{
updateInProgress = true;
Update.RaiseCanExecuteChanged();
await Task.Run(() => StartUpdate());
updateInProgress = false;
Update.RaiseCanExecuteChanged();
},
() => !updateInProgress);
}
return update;
}
}
I think, you can use this implementation of AsyncCommand.
public class AsyncCommand : ICommand, IDisposable
{
private readonly BackgroundWorker _backgroundWorker = new BackgroundWorker {WorkerSupportsCancellation = true};
private readonly Func<bool> _canExecute;
public AsyncCommand(Action action, Func<bool> canExecute = null, Action<object> completed = null,
Action<Exception> error = null)
{
_backgroundWorker.DoWork += (s, e) =>
{
CommandManager.InvalidateRequerySuggested();
action();
};
_backgroundWorker.RunWorkerCompleted += (s, e) =>
{
if (completed != null && e.Error == null)
completed(e.Result);
if (error != null && e.Error != null)
error(e.Error);
CommandManager.InvalidateRequerySuggested();
};
_canExecute = canExecute;
}
public void Cancel()
{
if (_backgroundWorker.IsBusy)
_backgroundWorker.CancelAsync();
}
public bool CanExecute(object parameter)
{
return _canExecute == null
? !_backgroundWorker.IsBusy
: !_backgroundWorker.IsBusy && _canExecute();
}
public void Execute(object parameter)
{
_backgroundWorker.RunWorkerAsync();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_backgroundWorker != null)
_backgroundWorker.Dispose();
}
}
}
So your solution to use RelayCommand almost works. The problem is that the UI won't immediately update after the task finishes running. This is because something needs to trigger the ICommand's CanExecuteChanged event in order for the UI to properly update.
One way to solve this problem is by creating a new kind of ICommand. For example:
class AsyncRelayCommand : ICommand
{
private Func<object, Task> _action;
private Task _task;
public AsyncRelayCommand(Func<object,Task> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return _task == null || _task.IsCompleted;
}
public event EventHandler CanExecuteChanged;
public async void Execute(object parameter)
{
_task = _action(parameter);
OnCanExecuteChanged();
await _task;
OnCanExecuteChanged();
}
private void OnCanExecuteChanged()
{
var handler = this.CanExecuteChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
Now your view model can do something like the following
private ICommand myCommand;
public ICommand MyCommand
{
get { return myCommand ?? (myCommand = new AsyncRelayCommand(p => Task.Factory.StartNew(doStuff))); }
}
private void doStuff()
{
System.Threading.Thread.Sleep(5000);
}
Or you could make your doStuff function an "async" function like so
private ICommand myCommand2;
public ICommand MyCommand2
{
get { return myCommand2 ?? (myCommand2 = new AsyncRelayCommand(p => doStuff2())); }
}
private async Task doStuff2()
{
await Task.Delay(5000);
}
You could have a static variable IsRunning, which you can set to True when your task starts, to false when it finishes, and just bind that enabled button to the state of the IsRunning
I am trying to avoid Prism library to keep my control simple as possible from point of view of mount of reference assemblies and I ended up with this solution
_cmd = new RelayCommand(async delegate
{
await Task.Run(() => <YourMethod>());
}, delegate { return !IsInProgress; }) );
Seems to be working well. (if you don't need to pass commandParameter in). Unfortunately this is still a problem.
RelayCommand class inherits from ICommand
public class RelayCommand : ICommand
{
private Action<object> _execute;
private Predicate<object> _canExecute;
private event EventHandler CanExecuteChangedInternal;
public RelayCommand(Action<object> execute)
: this(execute, DefaultCanExecute)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
if (canExecute == null)
{
throw new ArgumentNullException("canExecute");
}
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
CanExecuteChangedInternal -= value;
}
}
public bool CanExecute(object parameter)
{
return _canExecute != null && _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void OnCanExecuteChanged()
{
EventHandler handler = CanExecuteChangedInternal;
if (handler != null)
{
//DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty));
handler.Invoke(this, EventArgs.Empty);
}
}
public void Destroy()
{
_canExecute = _ => false;
_execute = _ => { return; };
}
private static bool DefaultCanExecute(object parameter)
{
return true;
}
}
I am new to MVVM and I would like to create simple command button that will be enabled if any of the items in list is selected and that will add listitem selected to the favorite list.
Here is my AddCommand implementation:
class AddFavCommand : ICommand
{
private readonly Action _favAction;
private readonly bool _canExecute;
public AddFavCommand()
{
}
public AddFavCommand(Action favAction, bool canExecute)
{
_favAction = favAction;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_favAction();
}
}
In my View Model I have public property AddFavCommand in order to do binding with my view:
private AddFavCommand _addFavCommand;
private bool _canAddFavExecute;
public ICommand AddFavCommand
{
get
{
if (_addFavCommand == null)
{
_addFavCommand = new AddFavCommand(AddFav, _canAddFavExecute);
}
return _addFavCommand;
}
}
and for now I have simple function just to check if command will work:
private void AddFav()
{
MessageBox.Show("Add");
}
so this part works perfectly without implementing canExecute property.
But now I want button to be disabled when list item in my list is not selected. I have a property:
CurrentItem
that is binded to List Item and it will be null if item is not selected. My question is how to trigger button to be disabled when item is not selected. I tried to adding:
private void AddFav()
{
MessageBox.Show("Add");
_canAddFavExecute = CurrentItem != null; // to my function, but my button always stays disabled.
}
Thanks
If you have created your own delegate command then you will have to provide the CanExecute delegate like below and you can register this with your CanExecuteChanged event in command.
_addFavCommand = new AddFavCommand(AddFav, CanExecuteCommand);
public bool CanExecuteCommand(object parameter)
{
return CurrentCommand != null;
}
and from the setter of the CurrentCommand you will have to raise the canexecutechanged event for your command. In your case you have provided the bool once to the command and hence it is always disabled.