我有一个列表视图,其中OberservableCollection绑定到其itemSource。每个列表视图项都显示一个图像(图像源由定位本地文件夹中文件的 URI 设置:
<Image Source={x:Bind ImageURI} />
现在我希望用户能够更改文件,但是当我覆盖它时,我得到一个System.UnauthorizedException,这是有意义的,因为图像"正在使用"(它显示在屏幕上)。
mscorlib.ni 中发生了类型为"System.UnauthorizedAccessException"的异常.dll但未在用户代码中处理
其他信息:访问被拒绝。(HRESULT的例外:0x80070005(E_ACCESSDENIED))
现在我尝试用不同的图像快速替换图像,然后覆盖文件,但仍然会出现同样的问题。
public static async Task SaveToLocalStorage(string FileName, WriteableBitmap Bitmap)
{
StorageFile outputFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(FileName + ".png", CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream writeStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
byte[] pixels;
using (Stream stream = Bitmap.PixelBuffer.AsStream())
{
pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
}
// Encode pixels into stream
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, writeStream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)Bitmap.PixelWidth, (uint)Bitmap.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
}
}
有什么想法吗?
问候尼尔斯
如果要绑定图像路径并希望更改显示的内容,而不是替换磁盘上的文件,请使用不同的文件名添加新图像,然后更新视图模型,以便改为显示新图像。
针对您的问题的一种可能的解决方案可能是使用转换器将ImageURI
转换为用于绑定的BitmapImage
。在转换器中,我们可以使用 using 语句来释放文件资源。
下面是使用您发布的代码的示例:
XAML:
<Page.Resources>
<local:ImageURIConverter x:Key="ImageURIConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView IsItemClickEnabled="True" ItemClick="ListView_ItemClick" ItemsSource="{x:Bind ImageList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ViewModel">
<Image Height="200" Source="{x:Bind ImageURI, Converter={StaticResource ImageURIConverter}, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
代码隐藏:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
ImageList = new ObservableCollection<ViewModel> { new ViewModel { ImageURI = "ms-appdata:///local/1.png" }, new ViewModel { ImageURI = "ms-appdata:///local/2.png" }, new ViewModel { ImageURI = "ms-appdata:///local/3.png" } };
}
public ObservableCollection<ViewModel> ImageList;
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null)
{
WriteableBitmap Bitmap = new WriteableBitmap(1, 1);
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
{
Bitmap.SetSource(stream);
}
var clickedItem = e.ClickedItem as ViewModel;
var FileName = clickedItem.ImageURI.Replace("ms-appdata:///local/", string.Empty);
await SaveToLocalStorage(FileName, Bitmap);
clickedItem.NotifyPropertyChanged("ImageURI");
}
}
public static async Task SaveToLocalStorage(string FileName, WriteableBitmap Bitmap)
{
StorageFile outputFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(FileName, CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream writeStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
byte[] pixels;
using (Stream stream = Bitmap.PixelBuffer.AsStream())
{
pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
}
// Encode pixels into stream
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, writeStream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)Bitmap.PixelWidth, (uint)Bitmap.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
}
}
}
public class ImageURIConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var image = new BitmapImage();
using (IRandomAccessStream stream = (StorageFile.GetFileFromApplicationUriAsync(new Uri(value.ToString())).AsTask().Result).OpenAsync(FileAccessMode.Read).AsTask().Result)
image.SetSource(stream);
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
public class ViewModel : INotifyPropertyChanged
{
private string imageURI;
public string ImageURI
{
get
{
return imageURI;
}
set
{
imageURI = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
但是,请注意,在某些情况下,使用 Task.Result
可能不是一个好的做法,因为它可能会阻止您的 UI。但我认为这是针对您问题的简单解决方案,您可以根据自己的实际情况决定是否使用它。