Passing a parameter to RelayCommand - wpf

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.

Related

WPF MVVM - global command with parameters

I need to open same window for displaying reports, from many different pages, on button click. But (based on page where I open It) I need to provide which report should be displayed, title of window etc.
So I'm trying to create a global command with parameters for that, in order to avoid writing same button command in each ViewModel.
I know how to create global command:
public static class Global_commands
{
private static readonly RoutedUICommand _reports = new RoutedUICommand("View report", "View_report", typeof(Global_commands));
public static RoutedUICommand View_report
{
get{return _reports;}
}
}
//in App.xaml:
public App()
{
var view_report = new CommandBinding(Global_commands.View_report, View_Report_Executed, View_Report_CanExecute);
CommandManager.RegisterClassCommandBinding(typeof(Window), view_report);
}
private void View_Report_Executed(object sender, ExecutedRoutedEventArgs e)
{
//...
}
But that doesn't allow me to pass a parameter, or at least I don't know how. So I can't say which report should be displayed.
Another option for commands I know is this:
public class Register_command : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public Register_command(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute(parameter);
}
public void Execute(object parameter)
{
if (_execute != null)
_execute(parameter);
}
}
//in ViewModel of some window:
public ICommand Open_report { get; set; }
public SomeViewModel()
{
Open_report = new Register_command(Open_report_window, null);
}
//I would a method something like that - with parameters
private void Open_report_window(object parameter)
{
Report_Window report_wind = new Report_Window();
report_wind.Owner = System.Windows.Application.Current.MainWindow;
switch (parameter)
{
case "1":
report_wind.Title= "Report number 1";
report_wind.Report_Name="Report1.rdlc";
default:
break;
}
}
In this case, my Executed method allows me to pass a parameter, but I don't know how to pass this command globally.
Can somebody show me correct approach for this ?
Sorry, too fast on posting. My solution was to use e.Parameter. so first option is the one to go for me:
public static class Global_commands
{
private static readonly RoutedUICommand _reports = new RoutedUICommand("View report", "View_report", typeof(Global_commands));
public static RoutedUICommand View_report
{
get{return _reports;}
}
}
//in App.xaml:
public App()
{
var view_report = new CommandBinding(Global_commands.View_report, View_Report_Executed, View_Report_CanExecute);
CommandManager.RegisterClassCommandBinding(typeof(Window), view_report);
}
private void View_Report_Executed(object sender, ExecutedRoutedEventArgs e)
{
Report_Window report_wind = new Report_Window();
report_wind.Owner = System.Windows.Application.Current.MainWindow;
switch (e.Parameter)
{
case "1":
report_wind.Title= "Report number 1";
report_wind.Report_Name="Report1.rdlc";
default:
break;
}
}

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();
}
}

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

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;
}
}

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