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 ?
CodePudding user response:
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.