1 year ago
#361024
ekt
WPF DataGrid throws ArgumentOutOfRangeException when inserting a second row
Scenario is quite simple. A WPF DataGrid whose rows come from an ObservableCollection. Things worth noting are:
- the DataGrid has a GroupStyle defined
- the ObservableCollection is initially empty
- rows are added at the top of the ObservableCollection (ie: with
Items.Insert(0, element)
)
First insert is successful. The second insert crashes with
System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection.
The problem disappears if I remove the virtualization of the columns, OR if I remove the GroupStyle OR if I put a fixed size on the columns.
Calling CollectionViewSource.GetDefaultView(Items).Refresh()
immediately after Items.Insert also resolves.
However, all the above solutions are arbitrary and it is not obvious to me why I shouls do them. I think it is simply a DataGrid bug but I would like to understand if instead there is something fundamentally wrong that I am doing.
Following, a simplified app that reproduces the issue. Click the button twice and you'll get the exception.
MainWindow.xaml
<Window x:Class="WpfDataGridGroupsBug.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfDataGridGroupsBug"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="600"
d:DataContext="{d:DesignInstance d:Type=local:MyViewModel}"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="GridGroupContainerStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<DataGrid
x:Name="theDataGrid"
Height="280"
RowHeight="20"
ItemsSource="{Binding Items}"
AutoGenerateColumns="False"
CanUserAddRows="False"
EnableColumnVirtualization="True"
>
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GridGroupContainerStyle}"/>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" MinWidth="100" Width="*" Binding="{Binding Path=SomeValue}"/>
</DataGrid.Columns>
</DataGrid>
<Button Click="AddRows" Height="30" Content="Add Row (click me twice)"/>
</StackPanel>
</Window>
GridGroupContainerStyle.xaml
<Style x:Key="GridGroupContainerStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="true">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=ItemCount, StringFormat=' ({0})'}" FontWeight="Bold"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml.cs
using System.Windows;
namespace WpfDataGridGroupsBug
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var viewModel = new MyViewModel();
this.DataContext = viewModel;
}
private void AddRows(object sender, RoutedEventArgs e)
{
if (DataContext is MyViewModel viewModel)
viewModel.AddRowAtTop();
}
}
}
ViewModel.cs
public class MyItemModel
{
public string SomeValue { get; set; }
}
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<MyItemModel> Items { get; set; }
public MyViewModel()
{
Items = new ObservableCollection<MyItemModel>();
}
public void AddRowAtTop()
{
Items.Insert(0, new MyItemModel() { SomeValue = "blah" });
}
public event PropertyChangedEventHandler PropertyChanged;
}
callstack
at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.Collections.Generic.List`1.System.Collections.IList.get_Item(Int32 index)
at System.Windows.Controls.DataGridCellsPanel.ArrangeOverride(Size arrangeSize)
at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)
at System.Windows.UIElement.Arrange(Rect finalRect)
at MS.Internal.Helper.ArrangeElementWithSingleChild(UIElement element, Size arrangeSize)
at System.Windows.Controls.ItemsPresenter.ArrangeOverride(Size arrangeSize)
at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)
c#
wpf
datagrid
observablecollection
0 Answers
Your Answer