Raise context menu command's CanExcute while open the context menu - wpf

Can Execute of a ICommand while a Context menu open
With the continuation of the above query, Still its not able to achieve.
Is there any way to raise context menu command's CanExcute while open the context menu. This need to be done in the context menu itself and here am not able to access the viewmodel.
Any idea on this?
public static BaseCommand SaveCommand
{
get
{
if (saveCommand == null)
saveCommand = new BaseCommand(OnSaveCommandClicked, OnSaveCommandCanExcute);
return saveCommand;
}
}
where BaseCommand is derived from ICommand.
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
: this(method, null)
{
}
public BaseCommand(Action<object> method, Predicate<object> canExecute)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}

Inside BaseCommand, replace the CanExecuteChanged to look like the following. And it will work the way you want it.
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}

Related

Passing a parameter to RelayCommand

I'm experimenting with the ICommand and RelayCommand constructs. Looking around, I've put together the following code:
public class RelayCommand : ICommand
{
private Action<object> _executeMethod;
private Func<object, bool> _canExecuteMethod;
public RelayCommand(Action<object> ExecuteMethod, Func<object, bool> CanExecuteMethod)
{
_executeMethod = ExecuteMethod;
_canExecuteMethod = CanExecuteMethod;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod(parameter);
}
else
{
return false;
}
}
public void Execute(object parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
}
and I use all this from my main code as follows:
private bool CanExecuteMyCommand (object parameter) { /* do something here */ }
private void ExecuteMyCommand(object parameter) { /* do something here */ }
private ICommand _execMyCmd;
public ICommand ExecMyCmd
{
get { return _execMyCmd; }
set { _execMyCmd = value; }
}
ExecMyCmd = new RelayCommand(ExecuteMyCommand, CanExecuteMyCommand);
This works fine, but one thing I've not been able to find is how to pass a parameter to ExecuteMyCommand. I've tried something like:
ExecMyCmd = new RelayCommand(ExecuteMyCommand ("foo"), CanExecuteMyCommand);
but Visual Studio shows an error. Can anybody shed some light on this?
Thanks
Gianni
Command parameters are typically passed by the control which binds to the command. Here is an example:
<Button x:Name="btn"
Content="Click"
Command="{Binding ExecMyCmd}"
CommandParameter="{Binding ElementName=btn, Path=Content}"/>
This will pass the content of the button, which is "Click", as parameter. Note that this is an example, so you could pass what ever you want here.

Trying to update CanExecute for my commands

I've created a base view model that implements ICommand. The commands bind just fine, execute as expected, and even begin in the correct states, but as properties that influence whether a command can execute or not change, the CanExecute for those commands doesn't seem to be updating.
In my code below, I can click the Run button and everything works as expected EXCEPT for the fact that when the ProgramStatus changes to Running it should be disabled.
In my base view model:
public class RelayCommand : ICommand
{
#region ICommand Member Variables
private Action<object> _execute;
private Predicate<object> _canExecute;
private event EventHandler _canExecuteChangedInternal;
#endregion // ICommand Member Variables
#region Constructors
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public RelayCommand(Action<object> execute) : this(execute, DefaultCanExecute)
{
}
#endregion // Constructors
#region ICommand Members
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 eventHandler = _canExecuteChangedInternal;
if (eventHandler != null)
{
eventHandler.Invoke(this, EventArgs.Empty);
}
}
private static bool DefaultCanExecute(object parameter)
{
return true;
}
#endregion // ICommand Members
In my view model:
RelayCommand _runCommand;
public RelayCommand RunCommand
{
get
{
if (_runCommand == null)
{
_runCommand = new RelayCommand(param => Run(), param => CanRun);
}
return _runCommand;
}
}
public bool CanRun
{
get
{
bool result = false;
if (Machine.ProgramStatus != ProgramStatus.Running && Machine.ProgramStatus != ProgramStatus.TestRunning)
{
result = true;
}
return result;
}
}
In my view:
<Button Content="Run" Command="{Binding Path=RunCommand}" />
You have to invoke the CanExecuteChanged EventHandler. So when ProgramStatus changes to Running you can call the OnCanExecuteChanged helper method for the command you want to update (e.g. RunCommand.OnCanExecuteChanged();).
If CanExecute is bound to a property you can use the OnCanExecuteChanged helper method in the property's setter.
private bool _myProperty;
public bool MyProperty
{
get { return _myProperty; }
set
{
_myProperty= value;
RunCommand.OnCanExecuteChanged();
}
}

WPF Update button state from inside command

Having different buttons on a view, how could I control the IsEnabled state of the buttons when a command is being executed?
If I execute this command:
public ICommand DoSomethingCommand
{
get
{
if (doSomethingCommand == null)
{
doSomethingCommand = new RelayCommand(
(parameter) =>
{
this.IsBusy = true;
this.someService.DoSomething(
(b, m) =>
{
this.IsBusy = false;
}
);
},
(parameter) => !this.IsBusy);
}
return this.doSomethingCommand ;
}
}
I set the IsBusy property which fires the OnPropertyChanged event. All the other buttons check this property in their command's CanExecute. But the buttons do not get disabled, when the above command is executed.
How do I do this right?
CanExecuteChanged needs to fire in order for the Command Source to know to invoke CanExecute.
A simple way to do this is to invoke CommandManager.InvalidateRequerySuggested().
bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
if (value == _isBusy)
return;
_isBusy = value;
//NotifyPropertyChanged("IsBusy"); // uncomment if IsBusy is also a Dependency Property
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}
}
why not implement INotifyPropertyChanged on your own custom version of the RelayCommand class. This will conform to MVVM:
public class RelayCommand : ICommand, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly Action execute;
private readonly Func<bool> canExecute;
private bool isBusy;
public bool IsBusy
{
get { return isBusy; }
set
{
isBusy = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs("IsBusy"));
}
}
}
public event EventHandler CanExecuteChanged
{
add
{
if (this.canExecute != null)
{
CommandManager.RequerySuggested += value;
}
}
remove
{
if (this.canExecute != null)
{
CommandManager.RequerySuggested -= value;
}
}
}
public RelayCommand(Action execute) : this(execute, null)
{
}
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public void Execute(object parameter)
{
this.IsBusy = true;
this.execute();
this.IsBusy = false;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null ? true : this.canExecute();
}
}
then use the command like this:
<Button Command="{Binding Path=DoSomethingCommand}"
IsEnabled="{Binding Path=DoSomethingcommand.IsBusy,
Converter={StaticResource reverseBoolConverter}}"
Content="Do It" />

How to pass CommandParameters to the ViewModel?

I have a command that should switch the current view when it's executed. I binded this command to my buttons like this:
<Button Style="{StaticResource TextButton}" Command="{Binding ViewModel:MainViewModel.OpenItemCommand}" CommandParameter="{Binding Link}"/>
I want to pass Link (the link of the currently selected article) to my command. My command is defined like this:
public class Command : ICommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute;
readonly Action<Object> _executeAction;
public Command(Predicate<Object> canExecute, Action<object> executeAction)
{
_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();
}
}
In my ViewModel I have this:
public ICommand OpenItemCommand
{
get
{
if (_openItemCommand == null)
{
_openItemCommand = new Command.Command(
p => true,
p => OpenItem(_HOW_DO_I_GET_THE_PARAMETER?_)
);
}
return _openItemCommand;
}
set
{
if (_openItemCommand != value)
{
_openItemCommand = value;
RaisePropertyChanged("OpenItemCommand");
}
}
}
private void OpenItem(Uri link)
{
throw new NotImplementedException();
}
When I create the command I need to pass the command parameter (the link) to the Execute method. But how do I get the value of this? I defined the CommandParameter in XAML, but I don't know how to access it.
I really searched through a huge amount of websites but I can't really find the answer.
You should look at the implementation of Prism's DelegateCommand or MVVM light's RelayCommand. With these you would write code like this:
public class ViewModel
{
public ViewModel()
{
OpenItemCommand = new RelayCommand<string>(OpenItem);
}
public RelayCommand<string> OpenItemCommand { get; private set; }
private void OpenItem(string link)
{
Debug.WriteLine(link);
}
}
where string in this case is the type of the parameter.
I'm not sure where the link parameter is coming from but if it's from a control, the value of the control could be bound to a property of your view model, then you don't need a parameter, for example:
public class ViewModel
{
public ViewModel()
{
OpenItemCommand = new RelayCommand(OpenItem);
}
public RelayCommand OpenItemCommand { get; private set; }
public string Link { get; set; }
private void OpenItem()
{
Debug.WriteLine(Link);
}
}
replace
p => OpenItem(_HOW_DO_I_GET_THE_PARAMETER?_)
with
p => OpenItem(p)
that is what the p stands for: parameter

updating text with filename with path via OpenFileDialog using MVVM and WPF

I have a text box for file name with path. After user locates a file using OpenFileDialog, this text box should be populated with filename. This text should also work when user enters filename with path directly instead of selecting from file dialog box.
Since I am learning MVVM, I am getting hard time to figure out how to update text box with filename/path. I tried everything i can think of.
I was expecting onPropertyChanged(“FilenameWithPath”) should take care this issue. Can somebody show me how to deal with this issue?
See code below
FileBrowseView.xaml
<TextBox Height="23" HorizontalAlignment="Left" Margin="113,22,0,0"
Name="txtFilenameWithPath"
Text="{Binding Path=FilenameWithPath,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
VerticalAlignment="Top" Width="300" />
<Button
Content="Browse..."
Height="30"
HorizontalAlignment="Left"
Margin="433,20,0,0"
Name="btnBrowse"
VerticalAlignment="Top"
Width="142"
Command="{Binding Path=BrowseCommand}" />
FileBrowseView.xaml.cs
public partial class FileBrowseView : Window
{
public FileBrowseView()
{
InitializeComponent();
DataContext = new FileBrowseViewModel();
}
}
FileBrowseModel
public class FileBrowseModel
{
private string _filenameWithPath = string.Empty;
public string FilenameWithPath
{
get { return _filenameWithPath; }
set
{
if (value == _filenameWithPath)
return;
else
_filenameWithPath = value;
}
}
}
FileBrowseViewModel
public class FileBrowseViewModel : INotifyPropertyChanged
{
private string _filenameWithPath = string.Empty;
public string FilenameWithPath
{
get { return _filenameWithPath; }
set
{
if (value == _filenameWithPath)
return;
else
_filenameWithPath = value;
OnPropertyChanged("FilenameWithPath");
}
}
private ICommand _browseCommand;
public ICommand BrowseCommand
{
get
{
if (_browseCommand == null)
_browseCommand = new DoBrowse();
return _browseCommand;
}
set
{
_browseCommand = value;
OnPropertyChanged("FilenameWithPath");
}
}
private class DoBrowse : ICommand
{
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
var filedialog = new System.Windows.Forms.OpenFileDialog();
DialogResult fresult = filedialog.ShowDialog();
if (fresult == System.Windows.Forms.DialogResult.OK)
{
FilenameWithPath = filedialog.FileName;
//I am trying to assign value i got from OpenFileDialog to
// FilenameWithPath property
//complier says "Cannot access non static member of outer type
'MyProject.FileBrowseViewModel' via
nested type 'MyProject.FileBrowseViewModel.DoBrowse
onPropertyChanged(“FilenameWithPath”);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You just need to set FileNameWithPath in your command's Execute function. And the setter for FileNameWithPath ought to be calling OnPropertyChanged. You shouldn't have to call that from your command's Execute function.
EDIT: Make sure that you are setting your data context to be the viewmodel and not the model since both have FilenameWithPath properties. If you were doing this the bind wouldn't fail because there is still a property to bind to. Otherwise:
Make the following changes:
public string FilenameWithPath
{
get { return _filenameWithPath; }
set
{
if (value == _filenameWithPath)
return;
else
{
_filenameWithPath = value;
OnPropertyChanged("FilenameWithPath");
}
}
}
and
if (fresult == System.Windows.Forms.DialogResult.OK)
{
FilenameWithPath = filedialog.FileName;
}
This should fix your problem. Additionally, consider changing which dialog box you use since this is WPF (as suggested in my comment).
Finally I am able to resolve this issue by adding new class called RelayCommand. I have modified the get block of _browseCommand use relay command as below.
private ICommand _browseCommand;
public ICommand BrowseCommand
{
get{
if (_browseCommand == null){
_browseCommand = new RelayCommand(
a => this.DoBrowseFolder(),
p => this.CheckCondition());
}
return _browseCommand;
}
set
{ _browseCommand = value;
OnPropertyChanged("FilenameWithPath");
}
}
public bool CheckCondition () {
//Check condition here if needed
return true;
}
private void DoBrowseFolder(){
var filedialog = new System.Windows.Forms.OpenFileDialog();
DialogResult fresult = filedialog.ShowDialog();
if (fresult == System.Windows.Forms.DialogResult.OK)
{
FilenameWithPath = filedialog.FileName;
OnPropertyChanged("FilenameWithPath ");
}
}
Relay Command Class
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute, Predicate<object> canExecute){
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#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 // ICommand Members
}

Resources