DataGrid Exception
-
I have a DataGrid that allows users to define CSV file columns for a UI I'm creating. It's bound to an ObservableCollection. I have CanUserAddRows = true. Apparantly setting this to True exposes some strange bug. The grid's first column has a button:
<DataGrid Grid.Row="2" Grid.Column="0" ItemsSource="{Binding ColumnInfos}" HorizontalGridLinesBrush="LightGray" VerticalGridLinesBrush="LightGray" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" BorderBrush="SteelBlue" BorderThickness="1" HeadersVisibility="Column" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" VerticalAlignment="Stretch" Margin="5,0,5,5"> <DataGrid.Resources> <local:BindingProxy x:Key="proxy" Data="{Binding IsColumnWidthVisibile}"/> </DataGrid.Resources> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Command="{Binding DeleteColumnCommand, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Window}}}" CommandParameter="{Binding}" Margin="2" Height="18" Width="18"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Header="Column Name" Binding="{Binding ColumnName}" Width="\*"/> <DataGridTextColumn Header="Width" Binding="{Binding ColumnWidth}" Width="65" Visibility="{Binding Data, Conve
-
I have a DataGrid that allows users to define CSV file columns for a UI I'm creating. It's bound to an ObservableCollection. I have CanUserAddRows = true. Apparantly setting this to True exposes some strange bug. The grid's first column has a button:
<DataGrid Grid.Row="2" Grid.Column="0" ItemsSource="{Binding ColumnInfos}" HorizontalGridLinesBrush="LightGray" VerticalGridLinesBrush="LightGray" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" BorderBrush="SteelBlue" BorderThickness="1" HeadersVisibility="Column" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" VerticalAlignment="Stretch" Margin="5,0,5,5"> <DataGrid.Resources> <local:BindingProxy x:Key="proxy" Data="{Binding IsColumnWidthVisibile}"/> </DataGrid.Resources> <DataGrid.Columns> <DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Button Command="{Binding DeleteColumnCommand, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Window}}}" CommandParameter="{Binding}" Margin="2" Height="18" Width="18"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Header="Column Name" Binding="{Binding ColumnName}" Width="\*"/> <DataGridTextColumn Header="Width" Binding="{Binding ColumnWidth}" Width="65" Visibility="{Binding Data, Conve
The problem is in your
RelayCommand<T>
class, which indiscriminately tries to cast the command parameter to the specified type. YourDeleteCommand
is an instance ofRelayCommand<ColumnInfoEntity>
, so it will try to cast the parameter to theColumnInfoEntity
. But for the "new item" row, the parameter will be passed the CollectionView.NewItemPlaceholder[^] value, which will be anMS.Internal.NamedObject
instance. Hence the cast will fail, and your code will crash. Change theRelayCommand<T>
to test whether the parameter is the expected type:private bool CanExecuteCore(T parameter)
{
return _canExecute is null || _canExecute(parameter);
}public bool CanExecute(object parameter)
{
return parameter is T param && CanExecuteCore(param);
}public void Execute(object parameter)
{
if (parameter is T param && CanExecuteCore(param))
{
_execute(param);
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
The problem is in your
RelayCommand<T>
class, which indiscriminately tries to cast the command parameter to the specified type. YourDeleteCommand
is an instance ofRelayCommand<ColumnInfoEntity>
, so it will try to cast the parameter to theColumnInfoEntity
. But for the "new item" row, the parameter will be passed the CollectionView.NewItemPlaceholder[^] value, which will be anMS.Internal.NamedObject
instance. Hence the cast will fail, and your code will crash. Change theRelayCommand<T>
to test whether the parameter is the expected type:private bool CanExecuteCore(T parameter)
{
return _canExecute is null || _canExecute(parameter);
}public bool CanExecute(object parameter)
{
return parameter is T param && CanExecuteCore(param);
}public void Execute(object parameter)
{
if (parameter is T param && CanExecuteCore(param))
{
_execute(param);
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
That did it. Thank you!
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
-
The problem is in your
RelayCommand<T>
class, which indiscriminately tries to cast the command parameter to the specified type. YourDeleteCommand
is an instance ofRelayCommand<ColumnInfoEntity>
, so it will try to cast the parameter to theColumnInfoEntity
. But for the "new item" row, the parameter will be passed the CollectionView.NewItemPlaceholder[^] value, which will be anMS.Internal.NamedObject
instance. Hence the cast will fail, and your code will crash. Change theRelayCommand<T>
to test whether the parameter is the expected type:private bool CanExecuteCore(T parameter)
{
return _canExecute is null || _canExecute(parameter);
}public bool CanExecute(object parameter)
{
return parameter is T param && CanExecuteCore(param);
}public void Execute(object parameter)
{
if (parameter is T param && CanExecuteCore(param))
{
_execute(param);
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
OK, so that works fine now. Now I added an event trigger to the grid:
When I run this, I get the exception
System.InvalidOperationException: 'Items collection must be empty before using ItemsSource.'
It's thrown in the class ItemCollection.cs. The ItemSource is set to an ObservableCollection which is loaded in the CTOR. This is why I hate the DataGrid. I commited this change. Any thoughts on this?
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
-
OK, so that works fine now. Now I added an event trigger to the grid:
When I run this, I get the exception
System.InvalidOperationException: 'Items collection must be empty before using ItemsSource.'
It's thrown in the class ItemCollection.cs. The ItemSource is set to an ObservableCollection which is loaded in the CTOR. This is why I hate the DataGrid. I commited this change. Any thoughts on this?
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
You've added the
<i:EventTrigger>
as a direct child of the grid. Looking at the documentation[^], it should be wrapped in an<i:Interation.Triggers>
element instead.<DataGrid ...>
<DataGrid.Resources>
...
</DataGrid.Resources><i:Interaction.Triggers> <i:EventTrigger EventName="AddingNewItem"> <i:InvokeCommandAction Command="{Binding NewItemAddedCommand}" CommandParameter="{Binding}"/> </i:EventTrigger> </i:Interaction.Triggers>
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
You've added the
<i:EventTrigger>
as a direct child of the grid. Looking at the documentation[^], it should be wrapped in an<i:Interation.Triggers>
element instead.<DataGrid ...>
<DataGrid.Resources>
...
</DataGrid.Resources><i:Interaction.Triggers> <i:EventTrigger EventName="AddingNewItem"> <i:InvokeCommandAction Command="{Binding NewItemAddedCommand}" CommandParameter="{Binding}"/> </i:EventTrigger> </i:Interaction.Triggers>
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
duh. Too many late nights Thanks
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.