UWP C#:如何在应用设置中保存并加载图像/文件流?
解决方案:保存与加载UWP ListView中的BitmapImage
针对你的需求,直接保存BitmapImage是不可行的——它是UI绑定对象,不支持序列化。这里提供两种实用方案,你可以根据场景选择:
方案一:将图像转为字节数组保存(不依赖原始文件)
这种方式把图像编码为字节数组(再转成Base64字符串)存储到应用设置,加载时反向解析。优点是不依赖原始文件,即使用户删除了原图片也能正常显示;缺点是大图像会占用较多设置空间,且序列化/反序列化有轻微性能开销。
步骤1:实现BitmapImage转字节数组的方法
private async Task<byte[]> BitmapImageToByteArray(BitmapImage bitmapImage) { byte[] bytes = null; using (var stream = new InMemoryRandomAccessStream()) { // 使用PNG编码避免质量损失 var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream); var writeableBitmap = new WriteableBitmap(bitmapImage.PixelWidth, bitmapImage.PixelHeight); await writeableBitmap.SetSourceAsync(await bitmapImage.OpenReadAsync()); encoder.SetPixelData( BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)writeableBitmap.PixelWidth, (uint)writeableBitmap.PixelHeight, DisplayInformation.GetForCurrentView().LogicalDpi, DisplayInformation.GetForCurrentView().LogicalDpi, writeableBitmap.PixelBuffer.ToArray() ); await encoder.FlushAsync(); bytes = new byte[stream.Size]; await stream.AsStream().ReadAsync(bytes, 0, bytes.Length); } return bytes; }
步骤2:保存图像到应用设置
建议用模型类封装每项数据,避免文本和图像的对应关系混乱:
// 定义模型类封装每项数据 public class ListItemModel { public string TextBoxContent { get; set; } public string ImageBase64 { get; set; } // 存储图像的Base64字符串 } // 保存所有项的方法 private async Task SaveAllItems() { var savedItems = new List<ListItemModel>(); foreach (var item in yourListView.Items) // 遍历ListView的所有项 { var model = item as ListItemModel; if (model == null) continue; // 匹配当前项对应的图像 var targetImg = imglist.ElementAt(savedItems.Count); if (targetImg != null && targetImg.UriSource != null) { var imgBytes = await BitmapImageToByteArray(targetImg); model.ImageBase64 = Convert.ToBase64String(imgBytes); } else { model.ImageBase64 = string.Empty; } savedItems.Add(model); } // 序列化模型列表到设置(使用System.Text.Json) var json = JsonSerializer.Serialize(savedItems); ApplicationData.Current.LocalSettings.Values["SavedListViewItems"] = json; }
步骤3:从设置加载图像
private async Task LoadAllItems() { imglist.Clear(); yourListView.Items.Clear(); if (!ApplicationData.Current.LocalSettings.Values.ContainsKey("SavedListViewItems")) return; var json = ApplicationData.Current.LocalSettings.Values["SavedListViewItems"] as string; var savedItems = JsonSerializer.Deserialize<List<ListItemModel>>(json); foreach (var model in savedItems) { // 加载文本(复用你已实现的逻辑) // 加载图像 if (!string.IsNullOrEmpty(model.ImageBase64)) { var imgBytes = Convert.FromBase64String(model.ImageBase64); using (var stream = new InMemoryRandomAccessStream()) { await stream.WriteAsync(imgBytes.AsBuffer()); stream.Seek(0); var bitmap = new BitmapImage(); await bitmap.SetSourceAsync(stream); imglist.Add(bitmap); } } else { imglist.Add(new BitmapImage()); } yourListView.Items.Add(model); } }
方案二:保存文件访问权限(依赖原始文件)
利用UWP的FutureAccessList保存用户选择文件的访问权限,只存储权限Token到设置。优点是节省空间,图像质量无损失;缺点是依赖原始文件存在,且FutureAccessList最多存储1000个项。
步骤1:选择文件时保存访问Token
修改你的文件选择逻辑,把文件添加到FutureAccessList并记录Token:
// 定义模型类封装每项数据 public class ListItemModel { public string TextBoxContent { get; set; } public string ImageFileToken { get; set; } // 存储文件访问Token } private async void openFileInputEvent(object sender, RoutedEventArgs e) { var picker = new Windows.Storage.Pickers.FileOpenPicker(); picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail; picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary; picker.FileTypeFilter.Add(".jpg"); picker.FileTypeFilter.Add(".jpeg"); picker.FileTypeFilter.Add(".png"); Windows.Storage.StorageFile file = await picker.PickSingleFileAsync(); if (file != null) { // 添加到FutureAccessList,获取长期访问Token string token = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(file); using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read)) { BitmapImage tmpimg = new BitmapImage(); await tmpimg.SetSourceAsync(fileStream); imglist.Add(tmpimg); } // 创建模型并添加到ListView(绑定文本和Token) yourListView.Items.Add(new ListItemModel { TextBoxContent = "...", ImageFileToken = token }); } else { var dialog = new MessageDialog("File not chosen", "No chosen Image"); await dialog.ShowAsync(); imglist.Add(new BitmapImage()); yourListView.Items.Add(new ListItemModel { TextBoxContent = "...", ImageFileToken = string.Empty }); } }
步骤2:保存Token和文本到设置
private void SaveAllItems() { var savedItems = yourListView.Items.Cast<ListItemModel>().ToList(); var json = JsonSerializer.Serialize(savedItems); ApplicationData.Current.LocalSettings.Values["SavedListViewItems"] = json; }
步骤3:从设置加载图像
private async Task LoadAllItems() { imglist.Clear(); yourListView.Items.Clear(); if (!ApplicationData.Current.LocalSettings.Values.ContainsKey("SavedListViewItems")) return; var json = ApplicationData.Current.LocalSettings.Values["SavedListViewItems"] as string; var savedItems = JsonSerializer.Deserialize<List<ListItemModel>>(json); foreach (var model in savedItems) { // 加载文本(复用你已实现的逻辑) // 加载图像 if (!string.IsNullOrEmpty(model.ImageFileToken)) { try { // 通过Token获取文件 StorageFile file = await Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.GetFileAsync(model.ImageFileToken); using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read)) { BitmapImage tmpimg = new BitmapImage(); await tmpimg.SetSourceAsync(fileStream); imglist.Add(tmpimg); } } catch (Exception ex) { // 处理文件无法访问的情况(如文件被删除、权限失效) imglist.Add(new BitmapImage()); } } else { imglist.Add(new BitmapImage()); } yourListView.Items.Add(model); } }
方案选择建议
- 如果希望图像不依赖原始文件,优先选方案一;
- 如果图像较大,且信任用户不会删除原始文件,优先选方案二。
内容的提问来源于stack exchange,提问作者Techrex Studios
相关产品推荐
相关产品推荐

