Home > Software design >  WinUI 3.0 ListView crashes when selecting newly added item. Attempted to read or write protected mem
WinUI 3.0 ListView crashes when selecting newly added item. Attempted to read or write protected mem

Time:04-21

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;
        }
    }
}

CodePudding user response:

Change the Mode of the binding to the Timestamp property to OneWay or OneTime or use a converter that converts the string to a DateTime:

<TextBox Margin="10"
         Header="Timestamp"
         Text="{x:Bind ViewModel.SelectedVehicle.Timestamp, Mode=OneWay}" />

You will get the same exception if you are trying to edit the timestamp of any of the two items that you add to the source collection in the view model.

CodePudding user response:

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.

  • Related