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