C# WPF CheckBox Command not Binded - c#

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>

Related

Enable disable button based on WPF datagrid selected rows following MVVM c#

In my WPF app I have a datagrid
<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Bills}"
SelectionMode="Extended"
Name="myGrid"...
Now I want to enable a button if any rows of the datagrid are selected, if no rows are selected the button should be disabled, pretty simple stuff.
My button xaml is like
<Button
Command="{Binding PreviewButtonClicked}"
CommandParameter="{Binding SelItm, ElementName=myGrid}"
I've create a standard RelayCommand class
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);
}
}
and my viewmodel class contains:
public class myVM()
{
private string _SelItm;
public string SelItm
{
get { return _SelItm; }
set
{
SetValue(ref _SelItm, value);
}
}
public RelayCommand PreviewButtonClicked { get; set; }
public myVM()
{
PreviewButtonClicked = new RelayCommand(ShowPDF, CanShowPDF);
}
public void ShowPDF(object param)
{
//do stuff
}
public bool CanShowPDF(object param)
{
if (SelItm.Any())
{
return true;
}
return false;
}
}
But when I run the app I get the below error on line if (SelItm.Any())
System.ArgumentNullException: Value cannot be null.
What am I doing incorrectly ?
There are at least three options.
Option 1 - Codebehind
Add an event handler to the SelectionChanged event on the DataGrid.
<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Bills}"
SelectionMode="Extended"
SelectionChanged="DataGridSelectionChanged"
Name="myGrid"...
The SelectionChanged event will be invoked when there's a full row selected. (You can set the SelectionUnit as "Rows" on the DataGrid explicitly) Otherwise use the SelectedCellsChanged event.
In the handler of the SelectionChanged event:
private void DataGridSelectionChanged(object sender, SelectionChangedEventArgs args)
{
myButton.IsEnabled = myGrid.SelectedItems.Count > 0;
}
Option 2
You can bind the SelectedItem property on the DataGrid, to your viewmodel.
<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Bills}"
SelectionMode="Extended"
SelectedItem="{Binding SelectedItemInDataGrid}"
Name="myGrid"...
In the viewmodel, simplified and also assuming your vm implements INotifyPropertyChanged:
private object _selectedItemInDg;
public object SelectedItemInDataGrid
{
set {
_selectedItemInDg = value;
OnPropertyChanged(nameof(SelectedItemInDataGrid ));
OnPropertyChanged(nameof(IsButtonEnabled));
PreviewButtonClicked?.UpdateCanExecute();
}
get => _selectedItemInDg;
}
public bool IsButtonEnabled => _selectedItemInDg != null;
private void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
And your button:
<Button IsEnabled="{Binding IsButtonEnabled}" Command="..." CommandParameter="...">Content of button</Button>
Option 3 - In CanExecute
You've got a method in your viewmodel, CanShowPDF(object param), which determines for the RelayCommand wether the command can be executed or not. WPF enables/disables the button automatically based on the CanExecute method of an ICommand instance.
I'd add a method to the RelayCommand class:
public void UpdateCanExecute() {
CanExecuteChanged?.Invoke();
}
Call the UpdateCanExecute method on your RelayCommand instance every time something changes that would affect the result of CanShowPDF.
The exception
You ask about an exception you get:
System.ArgumentNullException: Value cannot be null.
Probably you are calling a method on something that is null at that moment.
But it's hard to figure out the cause with the given information in the question and I don't actually know what SelItm is, although seen the name we could guess.

How do I handle events in a custom control?

I have a custom ItemsControl just like that
public class MyControl : ItemsControl { }
My Template would something like
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding Text}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now I want to act when either when ENTER or SPACE got pressed so I'd do something like this
<TextBox Text="{Binding Text}">
<TextBox.InputBindings>
<KeyBinding Key="Space"
Command="{Binding KeyPressedCommand}"
CommandParameter="{x:Static Key.Space}"/>
</TextBox.InputBindings>
</TextBox>
But how do I bind it to my Control?
One of ways to solve it is using MVVM pattern and RelayCommand class from tutorial.
RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly 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)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
}
Then you should set DataContext of your Window (or UserControl), it requred for resolving bindings in xaml.
One of ways doing it:
MainWindow.xaml
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
Setup Text Property and KeyPressedCommand from "something like this" example in the MainViewModel class.
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
private bool _text;
public bool Text
{
get => _text;
set
{
_text = value;
OnPropertyChanged(nameof(Text));
// Text changed!
}
}
public ICommand KeyPressedCommand => new RelayCommand(obj =>
{
if (obj is Key key) {
// do something here with the 'key' provided by CommandParameter
}
});
public MainViewModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

How CommandParameter works in MVVM?

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.

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}" />

Categories

Resources