选择新添加的项目时,WinUI 3.0 ListView 崩溃。尝试读取或写入受保护的内存
WinUI 3.0 ListView crashes when selecting newly added item. Attempted to read or write protected memory
我正在编写一个 WinUI 3.0 应用程序并且 ListView 绑定有效,当我将项目添加到我的集合时,它们出现在 ListView 中,但是当我想单击新添加的项目时我得到一个异常:试图读取或写入受保护的内存。我怀疑绑定有问题,尽管我有一个用于 ListView 源的 Observable 集合,并且我为 SelectedVehicle 实现了 INotifyPropertyChanged。
当我在初始化时将项目添加到集合中时,这个问题不会发生,只有当我在程序运行时使用按钮添加它们时才会出现 运行。也就是说,我可以 select 车辆 1 和 2 没有问题,就在我 select 车辆 3 出现此异常时。
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)
{
}
}
我的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>
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;
}
}
}
将绑定到 Timestamp
属性 的 Mode
更改为 OneWay
或 OneTime
或使用转换器将 string
到 DateTime
:
<TextBox Margin="10"
Header="Timestamp"
Text="{x:Bind ViewModel.SelectedVehicle.Timestamp, Mode=OneWay}" />
如果您尝试编辑添加到视图模型中的源集合的任何两个项目的时间戳,您将遇到相同的异常。
最后是绑定 vs x:Bind。我发现绑定是在 运行 时间内创建的,x:Bind 是在编译期间创建的。
因此我无法将 SelectedVehicle 设置为新添加的车辆。
解决方案是使用绑定。
留在这里以防对某人有帮助。
我正在编写一个 WinUI 3.0 应用程序并且 ListView 绑定有效,当我将项目添加到我的集合时,它们出现在 ListView 中,但是当我想单击新添加的项目时我得到一个异常:试图读取或写入受保护的内存。我怀疑绑定有问题,尽管我有一个用于 ListView 源的 Observable 集合,并且我为 SelectedVehicle 实现了 INotifyPropertyChanged。
当我在初始化时将项目添加到集合中时,这个问题不会发生,只有当我在程序运行时使用按钮添加它们时才会出现 运行。也就是说,我可以 select 车辆 1 和 2 没有问题,就在我 select 车辆 3 出现此异常时。
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)
{
}
}
我的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>
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;
}
}
}
将绑定到 Timestamp
属性 的 Mode
更改为 OneWay
或 OneTime
或使用转换器将 string
到 DateTime
:
<TextBox Margin="10"
Header="Timestamp"
Text="{x:Bind ViewModel.SelectedVehicle.Timestamp, Mode=OneWay}" />
如果您尝试编辑添加到视图模型中的源集合的任何两个项目的时间戳,您将遇到相同的异常。
最后是绑定 vs x:Bind。我发现绑定是在 运行 时间内创建的,x:Bind 是在编译期间创建的。
因此我无法将 SelectedVehicle 设置为新添加的车辆。
解决方案是使用绑定。
留在这里以防对某人有帮助。