如何覆盖 UI 中显示的存储文件(图像) [System.UnauthorizedAccessException]



我有一个列表视图,其中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。但我认为这是针对您问题的简单解决方案,您可以根据自己的实际情况决定是否使用它。

相关内容

最新更新