填充WinUI-3 ListView时出现无法捕获的异常(0xc000027b)
问题
将List<Windows.ApplicationModel.Package>作为ItemsSource填充ListView(或ItemsView)时,控件偶尔会崩溃;当使用Package.Logo作为Image的源时,崩溃概率显著升高。
预期效果是触发控件后显示所有已安装应用及其图标等内容,初始加载时能成功显示,但滚动列表时会触发崩溃。
用到的代码
XAML代码
<ContentDialog x:Class="Program.AllAppsList" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TITLE" Style="{ThemeResource DefaultContentDialogStyle}"> <Grid> <ListView ItemsSource="{x:Bind Packages}"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <!-- 取消注释后崩溃概率大幅上升 --> <Image Width="64" Height="64" Margin="5"> <Image.Source> <BitmapImage UriSource="{Binding Logo, TargetNullValue='ms-appx:///Assets/fallback.png', FallbackValue='ms-appx:///Assets/fallback.png'}"/> </Image.Source> </Image> <StackPanel Orientation="Vertical" Margin="5"> <TextBlock Text="{Binding DisplayName, TargetNullValue='Unknown', FallbackValue='Unknown'}" FontWeight="Bold" /> <TextBlock Text="{Binding PublisherDisplayName, TargetNullValue='Unknown', FallbackValue='Unknown'}" /> </StackPanel> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> </ContentDialog>
C#代码
using Microsoft.UI.Xaml.Controls; using System.Collections.Generic; using System.Linq; using System.Security.Principal; using Windows.ApplicationModel; using Windows.Management.Deployment; namespace Program { public sealed partial class AllAppsList : ContentDialog { public List<Package> Packages { get; set; } = new List<Package>(); public AllAppsList() { Packages = GetAllInstalledPackages(); this.DataContext = this; this.InitializeComponent(); } public static List<Package> GetAllInstalledPackages() { var pm = new PackageManager(); return pm.FindPackagesForUser(WindowsIdentity.GetCurrent().User?.Value).ToList(); } private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { } private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { } } }
修复方案
1. 异步加载应用图标,避免UI线程阻塞
直接绑定Package.Logo会导致UI线程在滚动时频繁阻塞,引发崩溃。可以通过转换器实现异步加载:
using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Media.Imaging; using System.Threading.Tasks; using Windows.ApplicationModel; public class PackageLogoConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { var bitmap = new BitmapImage(); if (value is Package package) { _ = LoadLogoAsync(bitmap, package); return bitmap; } bitmap.UriSource = new Uri("ms-appx:///Assets/fallback.png"); return bitmap; } private async Task LoadLogoAsync(BitmapImage bitmap, Package package) { try { if (package.Logo != null) { await bitmap.SetSourceAsync(await package.Logo.OpenReadAsync()); } else { bitmap.UriSource = new Uri("ms-appx:///Assets/fallback.png"); } } catch { bitmap.UriSource = new Uri("ms-appx:///Assets/fallback.png"); } } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
在XAML中引用转换器并修改Image绑定:
<ContentDialog.Resources> <local:PackageLogoConverter x:Key="PackageLogoConverter"/> </ContentDialog.Resources> <!-- 修改后的Image控件 --> <Image Width="64" Height="64" Margin="5" Source="{Binding Converter={StaticResource PackageLogoConverter}, FallbackValue='ms-appx:///Assets/fallback.png'}"/>
2. 封装Package数据,避免直接绑定系统对象
ListView虚拟化会频繁复用Item容器,直接绑定系统级的Package对象容易引发资源访问异常。可以将需要的属性封装到独立的ViewModel中:
public class AppPackageViewModel { public string DisplayName { get; set; } public string PublisherDisplayName { get; set; } public Package Package { get; set; } public static AppPackageViewModel FromPackage(Package package) { return new AppPackageViewModel { DisplayName = package.DisplayName ?? "Unknown", PublisherDisplayName = package.PublisherDisplayName ?? "Unknown", Package = package }; } }
修改数据加载逻辑:
public List<AppPackageViewModel> Packages { get; set; } = new List<AppPackageViewModel>(); public AllAppsList() { this.DataContext = this; this.InitializeComponent(); _ = LoadPackagesAsync(); } private async Task LoadPackagesAsync() { Packages = await Task.Run(() => GetAllInstalledPackages().Select(AppPackageViewModel.FromPackage).ToList() ); }
3. 异步加载数据,避免阻塞UI线程
将同步加载数据改为异步执行,避免构造函数阻塞UI导致滚动时异常:
private async Task LoadPackagesAsync() { // 可在此添加加载指示器 Packages = await Task.Run(() => GetAllInstalledPackages().Select(AppPackageViewModel.FromPackage).ToList() ); // 若使用ObservableCollection,无需手动更新;若用List,可调用绑定更新方法 }
内容的提问来源于stack exchange,提问作者ZacAttack
相关产品推荐
相关产品推荐

