How CommandParameter works in MVVM? - c#

I want to implement the CommandParameter in my class CommandProvider, which use for a command (Button, etc.) and inherit from ICommand, but I didn't understand how to implement that. Example:
XAML
<TreeViewItem Header="Playlist" ItemsSource="{Binding ItemSourceTree}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding Path=NewPlaylist}"
CommandParameter="{Binding Path=NamePlaylist}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeViewItem.ItemTemplate>
<DataTemplate DataType="{x:Type local:PlaylistDB}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=NamePlaylist}">
</TextBlock>
</StackPanel>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
The console says, NamePlaylist doesn't found.
And link a function to the Binding NewPlaylist:
public ICommand NewPlaylist { get { return new CommandProvider((obj) => DoubleClickTest(obj)); } }
Function
public void DoubleClickTest(object obj)
{
var tmp = obj as string;
Console.WriteLine(tmp);
}
So I need to modify my class CommandProvider to take parameter right? How I can do that?
CommandProvider
public class CommandProvider : ICommand
{
#region Constructors
public CommandProvider(Action<object> execute) : this(execute, null) { }
public CommandProvider(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute != null ? _canExecute(parameter) : true;
}
public void Execute(object parameter)
{
if (_execute != null)
_execute(parameter);
}
public void OnCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#endregion
private readonly Action<object> _execute = null;
private readonly Predicate<object> _canExecute = null;
}
PlaylistDB
public class PlaylistDB
{
public string NamePlaylist { get; set; }
}
I want to retrieve the NamePlaylist in my function DoubleClickTest(), and I want to pass it in CommandParameter. How can I do that?

Use The below Class for accepting commandparameters Using ICommand,
public class DelegateCommand: ICommand
{
#region Constructors
public DelegateCommand(Action<object> execute)
: this(execute, null) { }
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute != null ? _canExecute(parameter) : true;
}
public void Execute(object parameter)
{
if (_execute != null)
_execute(parameter);
}
public void OnCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#endregion
private readonly Action<object> _execute = null;
private readonly Predicate<object> _canExecute = null;
}
Usage:
public ICommand CloseCommand
{
get
{
return new DelegateCommand((obj)=>CloseMethod(obj));
}
}
obj is the command parameter passed in the above example.

Related

C# WPF CheckBox Command not Binded

View:
<DataGridTemplateColumn.Header>
<CheckBox x:Name="chk_Top" HorizontalAlignment="Center" HorizontalContentAlignment="Center"
Command="{Binding Chk_GridTop}"/>
</DataGridTemplateColumn.Header>
View Model:
public partial class ViewModel_AC: INotifyPropertyChanged
{
ICommand _chkGridTop;
public ICommand Chk_GridTop
{
get { return _chkGridTop ?? (_chkGridTop = new DelegateCommand(_chk_GridTop)); }
}
public void _chk_GridTop(object check)
{
//Empty
}
}
DelegateCommand
public class DelegateCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new NullReferenceException("execute can no null");
_execute = execute;
_canExecute = canExecute;
}
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
There are other Commands bind with buttons which working well, but checkBox command is not working
I want to fire Chk_GridTop when checkBox is checked or unchecked
Is it something wrong what i use that?
add CommandParameter
<DataGridTemplateColumn.Header>
<CheckBox x:Name="chk_Top" HorizontalAlignment="Center" HorizontalContentAlignment="Center"
Command="{Binding Chk_GridTop}" CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}"/>
</DataGridTemplateColumn.Header>

UWP ListView Button MVVM Binding

I have a ListView that, right now opens a Popup on SelectedItem.
What I want is that if the user decides to remove an Item from the List he can click the Button and it gets removed - right now the Button does fire, but how do I tell the Button in the VM what Item to delete - without "SelectedItem"? p.E..
<ListView
SelectedItem="{Binding...}"
x:Name="lv">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding...}"/>
<Button Command="{Binding ElementName=lv,Path=DataContext.RemoveXCommand}" />
</Stackpanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
VM
public void RemoveXCommand()
{
foreach(var item in pseudo)
{
if(item.Name == ?????)
pseudo.Remove(item);
}
}
Is there a way, or do I have to remove the opening of the Popup, and implement it as another Button, so I can use the SelectedItem to get the comparison?
Thank you.
EDIT1:
Thanks to Fruchtzwerg I got it working
public RelayCommand<string> RemoveXCommand{ get; private set; }
//... in then Constructor
RemoveXCommand = new RelayCommand<string>((s) => RemoveXCommandAction(s));
public void RemoveXCommand(object temp)
{
foreach(var item in pseudo)
{
if(item.Name == (string) temp)
pseudo.Remove(item);
}
}
You can pass the item you need to remove as CommandParameter
<Button Command="{Binding ElementName=lv, Path=DataContext.RemoveXCommand}"
CommandParameter="{Binding}"/>
and remove it like
public void RemoveXCommand(object itemToRemove)
{
pseudo.Remove(itemToRemove);
}
Your approach deleting the item by name is also possible. Bind the Name of the item as CommandParameter
<Button Command="{Binding ElementName=lv, Path=DataContext.RemoveXCommand}"
CommandParameter="{Binding Name}"/>
and remove it like
public void RemoveXCommand(object nameToRemove)
{
foreach(var item in pseudo)
{
if(item.Name == (string)nameToRemove)
{
pseudo.Remove(item);
}
}
}
Note that the second approach is removing all items having the name of the item you selected. The first approach removes only the item you selected because the specific instance is removed.
To allow a parameter in RelayCommand a new or modified implementeation of ICommand is required. Here is a possible solution:
public class ParameterRelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public ParameterRelayCommand(Action<object> execute)
: this(execute, null)
{ }
public ParameterRelayCommand(Action execute<object>, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}

CommandParameter usage in KeyInput Bindings Vs. Button bindings

The following two elements are firing in my implementation of ICommand differently and causing problems. When the implementation enters CanExecuteChanged(object parameter) for the TextBox, the value of parameter is null. When it enters the same method for the Button, the value of parameter is equal to the CommandParameter.
Ideally I'd like in both cases that the CommandParameter value is not sent to the CanExecuteChanged, only to Execute.
Implementation of ICommand
public event EventHandler CanExecuteChanged
{
add
{
canExecuteChanged += value;
CommandManager.RequerySuggested += value;
}
remove
{
canExecuteChanged -= value;
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
if (parameter is bool)
{
this.canExecute = (bool)parameter;
}
return this.canExecute;
}
public void Execute(object parameter)
{
this.executeAction((T)parameter);
}
internal void RaiseCanExecuteChanged()
{
this.OnCanExecuteChanged();
}
private void OnCanExecuteChanged()
{
if (this.canExecuteChanged != null)
{
this.canExecuteChanged(this, EventArgs.Empty);
}
}
TextBox
<TextBox Width="80" Margin="2,2,2,2" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" MaxLength="25">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}">
<KeyBinding.CommandParameter>
<s:Boolean>True</s:Boolean>
</KeyBinding.CommandParameter>
</KeyBinding>
</TextBox.InputBindings>
</TextBox>
Button
<Button Margin="2,2,2,2" Padding="10,0,10,0" Content="Search">
<Button.InputBindings>
<MouseBinding Command="{Binding SearchCommand }" MouseAction="LeftClick">
<MouseBinding.CommandParameter>
<s:Boolean>True</s:Boolean>
</MouseBinding.CommandParameter>
</MouseBinding>
</Button.InputBindings>
</Button>
In this case, try the implementation ICommand of #JoshSmith, for me both options worked well:
RelayCommand
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
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;
}
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);
}
}
SearchCommand
private RelayCommand _searchCommand = null;
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
{
_searchCommand = new RelayCommand(param => this.Search(param), param => true);
}
return _searchCommand;
}
}
private void Search(object param)
{
bool parameter = (bool)param;
if (parameter)
{
MessageBox.Show("Pressed the Enter Key");
}
}

How do I pass a variable as a CommandParameter

I'm trying to send a variable from the ViewModel as a parameter to a command. The command looks like this:
public class EditPersonCommand : ICommand
{
private bool _CanExecute = false;
public bool CanExecute(object parameter)
{
PersonModel p = parameter as PersonModel;
CanExecuteProperty = (p != null) && (p.Age > 0);
return CanExecuteProperty;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) { }
private bool CanExecuteProperty
{
get { return _CanExecute; }
set
{
if (_CanExecute != value)
{
_CanExecute = value;
EventHandler can_execute = CanExecuteChanged;
if (can_execute != null)
{
can_execute.Invoke(this, EventArgs.Empty);
}
}
}
}
}
The ViewModel looks like this:
public class PersonViewModel : ViewModelBase
{
private PersonModel _PersonModel;
private EditPersonCommand _EditPersonCommand;
///<remarks>
/// must use the parameterless constructor to satisfy <Window.Resources>
///</remarks>
public PersonViewModel()
: this(new PersonModel())
{
}
public PersonViewModel(PersonModel personModel)
{
_PersonModel = personModel;
}
public ICommand EditPersonCommand
{
get
{
if (_EditPersonCommand == null)
{
_EditPersonCommand = new EditPersonCommand();
}
return _EditPersonCommand;
}
}
}
The xaml looks like this:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
Command="{Binding EditPersonCommand}"
CommandParameter="{Binding _PersonModel}" />
I've tried creating a property in the ViewModel instead of using the private local variable name, but that didnt work either. The object parameter always shows null in the call to CanExecute and the button is never enabled. If I change the CommandParameter value to Hello, then I receive Hello in the call to CanExecute, so I'm not sure why the variable doesnt work. Any help would be appreciated.
Update: I've also tried making a public property to the model (which I dont really want to expose the model, but just tried it to see if it works, but it doesnt).
// Added this to the ViewModel
public PersonModel PersonModelProp
{
get
{
return _PersonModel;
}
set
{
_PersonModel = value;
OnPropertyChanged("PersonModelProp");
}
}
And changed the xaml to this:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
Command="{Binding EditPersonCommand}"
CommandParameter="{Binding PersonModelProp}" />
But still no luck. The ViewModel does implement INotifyPropertyChanged
Is the CommandParameter always null or are you only checking the first time it is being executed?
It appears that the order in which you declare your properties matters in this case since setting the Command property causes the CanExecute to fire immediately before the CommandParameter has been set.
Try moving the CommandParameter property before the Command property:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
CommandParameter="{Binding PersonModelProp}"
Command="{Binding EditPersonCommand}" />
Also, see here and here.
Edit
To ensure that your events are being raised properly you should raise the CanExecuteChanged event when the PersonModelProp value changes.
The Command:
public class EditPersonCommand : ICommand
{
public bool CanExecute(object parameter)
{
PersonModel p = parameter as PersonModel;
return p != null && p.Age > 0;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
//command implementation
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
And the view model:
public class PersonViewModel : ViewModelBase
{
private PersonModel _PersonModel;
private EditPersonCommand _EditPersonCommand;
///<remarks>
/// must use the parameterless constructor to satisfy <Window.Resources>
///</remarks>
public PersonViewModel()
: this(new PersonModel())
{
_EditPersonCommand = new EditPersonCommand();
}
public PersonViewModel(PersonModel personModel)
{
_PersonModel = personModel;
}
public ICommand EditPersonCommand
{
get
{
return _EditPersonCommand;
}
}
public PersonModel PersonModelProp
{
get
{
return _PersonModel;
}
set
{
_PersonModel = value;
OnPropertyChanged("PersonModelProp");
EditPersonCommand.RaiseCanExecuteChanged();
}
}
}
Two points to the answer:
First, as #akton mentioned, you can only bind to public properties. It doesn't have to be a DependencyProperty though.
Second, which took me some tome to figure out, is that you have to set the binding for the CommandParameter before the Command property. i.e.
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
CommandParameter="{Binding PersonModelProp}"
Command="{Binding EditPersonCommand}" />
Hope this helps :)
_PersonModel is private and so cannot be accessed. Create a public property that exposes it and bind to that in the CommandParameter. Remember to make the property a dependency property (technically not required but it helps) and the ViewModel should implement INotifyProperty changed and fire the PropertyChanged event so the binding is updated.
I think you have a problem in your EditPersonCommand (it not fired ok).I check it with relayCommand and it work!
This is the code:
ViewModel:
public class PersonViewModel : ViewModelBase
{
private PersonModel _PersonModel;
private ICommand _EditPersonCommand;
///<remarks>
/// must use the parameterless constructor to satisfy <Window.Resources>
///</remarks>
public PersonViewModel()
: this(new PersonModel())
{
}
public PersonViewModel(PersonModel personModel)
{
PersonModelProp = personModel;
}
public ICommand EditPersonCommand
{
get
{
if (_EditPersonCommand == null)
{
_EditPersonCommand = new RelayCommand(ExecuteEditPerson,CanExecuteEditPerson);
}
return _EditPersonCommand;
}
}
private bool CanExecuteEditPerson(object parameter)
{
PersonModel p = parameter as PersonModel;
return (p != null) && (p.Age > 0);
}
private void ExecuteEditPerson(object o)
{
}
public PersonModel PersonModelProp
{
get
{
return _PersonModel;
}
set
{
_PersonModel = value;
NotifyPropertyChanged("PersonModelProp");
}
}
}
And this RelayCommand (Fire events ok!)
public class RelayCommand : ICommand
{
#region Constants and Fields
private readonly Predicate<object> canExecute;
private readonly Action<object> execute;
#endregion
#region Constructors and Destructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
#endregion
#region Events
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
#endregion
#region Implemented Interfaces
#region ICommand
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
#endregion
#endregion
}
Xmal:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
CommandParameter="{Binding PersonModelProp}"
Command="{Binding EditPersonCommand}" />

How to use the CommandParameter with a RelayCommand?

I am still learning the ropes of MVVM and WPF, and at the moment I am trying to create a Mediaplayer using MVVM. After intensive googling I decided that using CommanParameter would be the best way to avoid code behind. I reckon the code and XAML looks fine, but there is no magic- AKA nothing is happening.
Is there any kind soul out there who would mind have a look at my code and give me some advice? As always, I truly value yours answers. Please ignore my plurals in RelayCommands, it was getting late :)
XAML
<MediaElement Name="MediaElement"
Source="{Binding VideoToPlay}"
Width="400" Height="180" Stretch="Fill"
LoadedBehavior="Manual" UnloadedBehavior="Manual"/>
<Slider Name="timelineSlider" Margin="5" Width="250"
HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button
Command="{Binding PlayMediaCommand}"
CommandParameter="{Binding ElementName=MediaElement, Mode=OneWay}"><<</Button>
C#
class MediaPlayerViewModel: INotifyPropertyChanged
{
private MediaElement MyMediaElement;
private Uri _videoToPlay;
public Uri VideoToPlay
{
get { return _videoToPlay; }
set
{
_videoToPlay = value;
OnPropertyChanged("VideoToPlay");
}
}
void SetMedia()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.InitialDirectory = "c:\\";
dlg.Filter = "Media files (*.wmv)|*.wmv|All Files (*.*)|*.*";
dlg.RestoreDirectory = true;
if (dlg.ShowDialog() == true)
{
VideoToPlay = new Uri(dlg.FileName);
}
}
RelayCommands _openFileDialogCommand;
public ICommand OpenFileDialogCommand
{
get
{
if (_openFileDialogCommand == null)
{
_openFileDialogCommand = new RelayCommands(p => SetMedia(),
p => true);
}
return _openFileDialogCommand;
}
}
RelayCommands _playMediaCommand;
public ICommand PlayMediaCommand
{
get
{
if (_playMediaCommand == null)
{
_playMediaCommand = new RelayCommands(p => PlayMedia(p),
p => true);
}
return _playMediaCommand;
}
}
void PlayMedia(object param)
{
var paramMediaElement = (MediaElement)param;
MyMediaElement = paramMediaElement;
MyMediaElement.Source = VideoToPlay;
MyMediaElement.Play();
}
protected void OnPropertyChanged(string propertyname)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyname));
}
public event PropertyChangedEventHandler PropertyChanged;
class RelayCommands: ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public RelayCommands(Action<object> execute)
: this(execute, null)
{}
public RelayCommands(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)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
Your example code works fine once the property VideoToPlay has been set. Are you sure you are setting this? Your XAML snippet did not include any usage of the OpenFileDialogCommand which sets this property:
<Button Content="Select File" Command="{Binding OpenFileDialogCommand}" />

Categories

Resources