I have a ListView with some columns. The first column is a checkbox type. Also, I have put a checkbox at ListView header row for select/unselect all ListView items at once.
This is the view (xaml):
<Grid>
<Style x:Key="alternatingStyle" TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="LightBlue" />
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightGray" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ListView Margin="10" Name="lvUsers" AlternationCount="2" ItemContainerStyle="{StaticResource alternatingStyle}" ItemsSource="{Binding Path=Items}" SelectionMode="Extended">
<ListView.View>
<GridView>
<!-- Checkbox header -->
<GridViewColumn>
<GridViewColumn.Header>
<CheckBox x:Name="CheckAll" Command="{Binding CheckAllCommand}"
CommandParameter="{Binding IsChecked, ElementName=CheckAll}" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
Sometimes (not always) when I check/uncheck the checkbox at listview header in order to select/unselect all items within the listview I get an exception of type:
Object reference not set to an instance of an object.
I have discovered that boolean parameter passed to icommand "CheckAllCommand" is null so when I try to do a conversion into boolean so it crashes, see later view model code:
Code-Behind (xaml.cs):
public partial class MainWindow: ViewBaseControl
{
public MainWindow(ViewModelSession vm):base(vm)
{
// DataContext = new myViewModel(); <-- Data context is not initialized here, it is done automatically in the ViewBaseControl class
InitializeComponent();
}
}
ViewBaseControl class:
public class ViewBaseControl : UserControl
{
[Obsolete("To use below constructor", true)]
public ViewBaseControl()
{
}
public ViewBaseControl(ViewModelSession vm)
{
DataContext = vm;
Focusable = true;
Loaded += (sender, e) =>
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
public ViewModelSession VM
{
get { return DataContext as ViewModelSession; }
}
}
View Model:
public class myViewModel : ViewModelSession, INotifyPropertyChanged
{
private DataModel _data = null;
private ObservableCollection<DataModel> items = null;
public myViewModel()
{
this.Load();
}
public void Load()
{
items = new ObservableCollection<DataModel>();
items.Add(new DataModel() { IsSelected = false, Name = "John Doe", Age = 42, Mail = "john@doe-family.com" });
items.Add(new DataModel() { IsSelected = false, Name = "Jane Doe", Age = 39, Mail = "jane@doe-family.com" });
items.Add(new DataModel() { IsSelected = false, Name = "Sammy Doe", Age = 7, Mail = "sammy.doe@gmail.com" });
}
public ObservableCollection<DataModel> Items
{
get
{
return this.items;
}
}
private RelayCommand checkAllCommand;
public ICommand CheckAllCommand
{
get
{
return checkAllCommand ??
(checkAllCommand = new RelayCommand(param => this.SelectUnselectAll(Convert.ToBoolean(param.ToString())))); // <-- this is the line that crashes when trying to convert boolean parameter "param" into boolean. Param is sometimes null, but not always.
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool IsSelected
{
get
{
if (this._data == null)
{
return false;
}
return this._data.IsSelected;
}
set
{
if (this._data != null && value != this._data.IsSelected)
{
this._data.IsSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
}
public string Name
{
get
{
if (this._data == null)
{
return string.Empty;
}
return this._data.Name;
}
set
{
if (value != this._data.Name)
{
this._data.Name = value;
NotifyPropertyChanged("Name");
}
}
}
public int Age
{
get
{
if (this._data == null)
{
return 0;
}
return this._data.Age;
}
set
{
if (value != this._data.Age)
{
this._data.Age = value;
NotifyPropertyChanged("Age");
}
}
}
public string Mail
{
get
{
if (this._data == null)
{
return string.Empty;
}
return this._data.Mail;
}
set
{
if (value != this._data.Mail)
{
this._data.Mail = value;
NotifyPropertyChanged("Mail");
}
}
}
private void SelectUnselectAll(bool isSelected)
{
for (int i = 0; i < this.items.Count; i++)
{
if (this.items[i].IsSelected != isSelected)
{
_data = new DataModel()
{
IsSelected = isSelected,
Name = this.items[i].Name,
Age = this.items[i].Age,
Mail = this.items[i].Mail
};
this.items.RemoveAt(i);
this.items.Insert(i, _data);
}
}
}
}
Problem is here, parameter "param" passed to RelayCommand is sometimes null (not always):
private RelayCommand checkAllCommand;
public ICommand CheckAllCommand
{
get
{
return checkAllCommand ??
(checkAllCommand = new RelayCommand(param => this.SelectUnselectAll(Convert.ToBoolean(param.ToString())))); // <-- this is the line that crashes when trying to convert boolean parameter "param" into boolean. Param is sometimes null, but not always.
}
}
My data model:
public class DataModel
{
public bool IsSelected
{
get;
set;
}
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
public string Mail
{
get;
set;
}
}
The RelayCommand class:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
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
}
}
Any idea why I am getting null?