Search code examples
listviewbindingselecteditemwinui

WinUI 3.0 ListView crashes when selecting newly added item. Attempted to read or write protected memory


I am writing a WinUI 3.0 app and the ListView Binding works, when I add items to my Collections, they appear in the ListView, but when I want to click on a newly added item I get an Exception: Attempted to read or write protected memory. I suspect a problem with the bindings, although I have an Observable Collection for the source of the ListView and I have INotifyPropertyChanged implemented for the SelectedVehicle.

When I add items to the collection upon initialization, this problem doesnt happen, only when I add them using a button while the program is running. That is I can select vehicles 1 and 2 without a problem, just when I select vehicle 3 this exception shows up.

The MainViewModel:

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<VehicleViewModel> _vehicles = new ObservableCollection<VehicleViewModel>();

    public ObservableCollection<VehicleViewModel> Vehicles
    {
        get => _vehicles;
        set
        {
            if (_vehicles != value)
            {
                _vehicles = value;
                OnPropertyChanged("Vehicles");
            }
        }
    }

    public MainViewModel()
    {
        this.OnInitialize();
    }
    private void OnInitialize()
    {
        Vehicles.Add(new VehicleViewModel
        {
            Id = 1,
            Timestamp = DateTime.Now,
            FrontPlate = "5Z97725",
            FrontPlateCountry = "CZE",
            RearPlate = "5Z97725",
            RearPlateCountry = "CZE",
            Speed = 52.4F,
            LaneName = "LN1",
            LaneDescription = "Pomaly pruh",
            ClassificationType = "ucid",
            ClassificationClass = 2,
            InfoFlagCount = 1,
            WarningFlagCount = 0,
            ErrorFlagCount = 0,
            HasViolation = false,
            City = "Zlin",
            RoadNumber = "I/49",
            XmlString = "<nejake xml>"

        });
        Vehicles.Add(new VehicleViewModel
        {
            Id = 2,
            Timestamp = DateTime.Now,
            FrontPlate = "1Z35725",
            FrontPlateCountry = "PL",
            RearPlate = "1Z35725",
            RearPlateCountry = "PL",
            Speed = 55.8F,
            LaneName = "LN2",
            LaneDescription = "Rychly pruh",
            ClassificationType = "ucid",
            ClassificationClass = 1,
            InfoFlagCount = 1,
            WarningFlagCount = 0,
            ErrorFlagCount = 0,
            City = "Zlin",
            RoadNumber = "I/49",
            HasViolation = true,
            ViolationType = "overspeed",
            XmlString = "<nejake xml>"
        });
        _selectedVehicle = Vehicles.ElementAt(0);
    }
    public VehicleViewModel SelectedVehicle
    {
        get => _selectedVehicle;
        set
        {
            if (_selectedVehicle != value)
            {
                _selectedVehicle = value;
                OnPropertyChanged("SelectedVehicle");
            }
        }
    }

    private VehicleViewModel _selectedVehicle;
}

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        this.PropertyChanged?.Invoke(this, e);
    }

    public virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        InvalidateCommands(propertyName);
    }

    public virtual void InvalidateCommands(string? propertyName)
    {
    }
}

My XAML:

    <Window
    x:Class="WinUI.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUI"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <ListView ItemsSource="{x:Bind ViewModel.Vehicles, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  SelectedItem="{x:Bind ViewModel.SelectedVehicle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  DisplayMemberPath="Id" />

        <StackPanel Grid.Column="1">
            <TextBox Margin="10"
                     Header="Id"
                     Text="{x:Bind ViewModel.SelectedVehicle.Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            <TextBox Margin="10"
                     Header="Timestamp"
                     Text="{x:Bind ViewModel.SelectedVehicle.Timestamp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            <CheckBox IsChecked="{x:Bind ViewModel.SelectedVehicle.HasViolation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </StackPanel>

        <Button Grid.Row="1"
                Content="Click"
                Click="Button_Click"
                ClickMode="Press"
                Margin="5,5,5,5"
                Width="150"
                HorizontalAlignment="left" />
    </Grid>
</Window>

The Window class:

    namespace WinUI
{
    /// <summary>
    /// An empty window that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
            ViewModel = new MainViewModel();
        }

        public MainViewModel ViewModel { get; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var vehicles = ViewModel.Vehicles;
            vehicles.Add(new VehicleViewModel
            {
                Id = 3,
                Timestamp = DateTime.Now,
                FrontPlate = "1Z35725",
                FrontPlateCountry = "PL",
                RearPlate = "1Z35725",
                RearPlateCountry = "PL",
                Speed = 55.8F,
                LaneName = "LN2",
                LaneDescription = "Rychly pruh",
                ClassificationType = "ucid",
                ClassificationClass = 1,
                InfoFlagCount = 1,
                WarningFlagCount = 0,
                ErrorFlagCount = 0,
                City = "Zlin",
                RoadNumber = "I/49",
                HasViolation = true,
                ViolationType = "overspeed",
                XmlString = "<nejake xml>"
            });
            ViewModel.Vehicles = vehicles;
        }
    }
}

Solution

  • In the end it was Bindings vs x:Bind. I found out Bindings are made during run time, x:Bind is made during compilation.

    I couldn't set SelectedVehicle to the newly added vehicles because of this.

    The solution was to use Bindings.

    Leaving it here in case it helps someone.