0

This happens when DataGridCheckBoxColumn is used in a DataGrid and is bound to a boolean property in the backing model. When the column is clicked and the check boxes checked/unchecked -- it behaves as expected -- but then when I close the main window, the exception is thrown. This does not happen when only text columns are used and interacted with. It also doesn't happen if I do NOT interact with the checkbox column at all and just close the window. It only happens when I close the window AFTER I have interacted with the check box column.

Development environment:

OSes:
1. Arch Linux , Gnome 3 , Wayland.
2. Windows 10 Pro

Dev:
dotnet core 3.1.103
IDE: VS Code

Steps to reproduce:

General procedure to use a DataGrid was that mentioned in the tutorial article: Avalonia DataGrid Getting Started

The only changes I made to the article were adding a DataGridCheckBoxColumn to the DataGrid and adding a boolean IsChecked property to the "Person" model class used in the tutorial -- and then bind the two together.

So the key steps are:

  1. $ dotnet new avalonia.mvvm
  2. In the *csproj file, knock up the versions of all Avalonia includes to 0.9.10 as thats the version of the DataGrid I'm using.
  3. $ dotnet add package Avalonia.Controls.DataGrid
  4. In the App.xaml , add the style:
    <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
  5. Add the following property to the model class "Person" --- rest of the the code is untouched and is as per the linked tutorial:
 public bool IsChecked
        {get;set;}
  1. Add the DataGridCheckBoxColumn to the DataGrid and bind it as shown below (rest of the columns are untouched and are as per the linked tutorial):
<DataGrid.Columns>
        <DataGridTextColumn Header="First Name" 
                            Binding="{Binding FirstName}" 
                            Width="2*" />
        <DataGridTextColumn Header="Last Name" 
                            Binding="{Binding LastName}" 
                            Width="2*" />
        <DataGridTextColumn Header="Department" 
                            Binding="{Binding DepartmentNumber}" 
                            Width="*" />

        <DataGridCheckBoxColumn Header="IsChecked" 
                            Binding="{Binding IsChecked}" 
                            Width="*" />

    </DataGrid.Columns>
  1. Then when I run the application (either from command line or vscode) -- the GUI shows up as expected-- with all the columns and values -- and the additional checkbox column is normally shown. __ When I click in the checkboxes column -- the check boxes do get checked and unchecked as expected (although one extra click is required). But then when I close the window by clicking on the "X" on right upper corner -- that's when the unhandled exception happens -- NOT before that.

The complete exception text:

Unhandled exception. System.InvalidOperationException: Collection was modified; enumeration operation may not execute. at System.Collections.Generic.List`1.Enumerator.MoveNextRare() at Avalonia.Collections.AvaloniaList`1.Enumerator.MoveNext() at System.Linq.Enumerable.OfTypeIterator[TResult](IEnumerable source)+MoveNext() at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Visual.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Input.InputElement.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.Control.OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) at Avalonia.Controls.TopLevel.HandleClosed() at Avalonia.Controls.WindowBase.HandleClosed() at Avalonia.Controls.Window.HandleClosed() at Avalonia.X11.X11Window.Cleanup() at Avalonia.X11.X11Window.Dispose() at Avalonia.X11.X11Window.OnEventSync(XEvent ev) at Avalonia.X11.X11Window.OnEvent(XEvent ev) at Avalonia.X11.X11PlatformThreading.HandleX11(CancellationToken cancellationToken) at Avalonia.X11.X11PlatformThreading.RunLoop(CancellationToken cancellationToken) at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken) at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args) at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime[T](T builder, String[] args, ShutdownMode shutdownMode) at d3.Program.Main(String[] args) in /home/[myusername]/[pathtoproject]/Program.cs:line 14

The code for Person class :

public class Person
    {
        public int DepartmentNumber { get; set; }

        public string DeskLocation{ get; set; }

        public int EmployeeNumber { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public bool IsChecked
        {get;set;}
    }

The code for MainWindowViewModel which is acting as code behind for the MainWindow and using the Person class above to host an ObservableCollection and expose it as a public property named People. DataGrid is bound to this People property which serves as the collection of Person class (who's properties are represented by different columns such as DataGridTextColumn and DataGridCheckBoxColumn):

public class MainWindowViewModel : ViewModelBase
    {
        public ObservableCollection<Person> People { get; set;}

        public MainWindowViewModel()
        {
            People = new ObservableCollection<Person>(GenerateMockPeopleTable());
        }

        private ObservableCollection<Person> GenerateMockPeopleTable()
        {
            var defaultPeople = new ObservableCollection<Person>()
            {
                new Person()
                {
                    FirstName = "Pat", 
                    LastName = "Patterson", 
                    EmployeeNumber = 1010,
                    DepartmentNumber = 100, 
                    DeskLocation = "B3F3R5T7",
                    IsChecked = false
                },
                new Person()
                {
                    FirstName = "Jean", 
                    LastName = "Jones", 
                    EmployeeNumber = 973,
                    DepartmentNumber = 200, 
                    DeskLocation = "B1F1R2T3",
                    IsChecked = true
                },
                new Person()
                {
                    FirstName = "Terry", 
                    LastName = "Tompson", 
                    EmployeeNumber = 300,
                    DepartmentNumber = 100, 
                    DeskLocation = "B3F2R10T1",
                    IsChecked = false
                }
            };

            return defaultPeople;
        }
    }

UPDATE: I have given up on finding a solution. Here is my work around:

Instead of using DataGridCheckBoxColumn (which also had an additional annoyance of being only checked if clicked twice -- I'm using DataGridTemplateColumn. It solves the above problem of exception -- as well as have my desired property of being checked on a single click.

<DataGridTemplateColumn Header="Select Todo">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding IsChecked}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
explorer
  • 11,710
  • 5
  • 32
  • 39

2 Answers2

0

I think your problem is about your collection which is used, not your checkbox. you must call .ToList() after your collection. please check the following link: Collection was modified; enumeration operation may not execute

  • If it were about the underlying collection than why does it ONLY occur with DataGridCheckBoxColumn and NOT with DataGridTextColumn ?. Both are using the same exact underlying collection. Please see the details in the post. – explorer May 22 '20 at 12:34
  • Edited the post to add the code behind towards the end. – explorer May 22 '20 at 12:56
  • 1
    please try to change selected row after editing checkbox column and check if the exception is thrown or not – Aliasghar Ahmadpour May 22 '20 at 13:04
0

Replace GenerateMockPeopleTable() with GenerateMockPeopleTable().ToList() and check if problem is solved.

  • The ObservabeCollection does NOT have a ToList() method. So I replaced the Observable collection itself with a List in the whole file. Now where ever there was ObservableCollection, there is a List and the problem still persists. If you look at the stack trace of the exception in the original post , the issue originates somewhere in Avalonia's collections of UI controls -- rather than my collection. I think Avalonia's developers need to get involved here. – explorer May 22 '20 at 13:21
  • 1
    Maybe. Usually i got this error when i'm trying to modify the collection inside the loop. – Aliasghar Ahmadpour May 22 '20 at 13:25