2017-01-12 7 views
0

У меня есть listview с привязкой OberservableCollection к его источнику items. Каждый ListViewItem показывает изображение (ImageSource устанавливается с помощью URI размещения файла в localfolder:Как перезаписать StorageFile (изображение), которое отображается в пользовательском интерфейсе [System.UnauthorizedAccessException]?

<Image Source={x:Bind ImageURI} /> 

Теперь я хочу, чтобы пользователь мог изменить файл, но когда я перезаписать его я получаю систему. UnauthorizedException который имеет смысл, так как изображения «используется» (это показано на экране).

исключение типа «System.UnauthorizedAccessException» произошло в mscorlib.ni.dll, но не был обработан в пользовательском коде

Дополнительная информация: Доступ запрещен. (Исключение из HRESULT: 0x80070005 (E_ACCESSDENIED))

Теперь я попытался быстро заменить изображение другим изображением, а THEN перезаписать файл, но та же проблема все еще возникает.

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(); 
     } 
    } 

Любые идеи?

С уважением, Niels

+0

Как вы меняли изображение? Думаю, твоя догадка правильная. Проблема здесь в том, что изображение, показанное в пользовательском интерфейсе, по-прежнему содержит ссылку на файл, который вы пытаетесь изменить. –

+0

Я просто обновляю ObservableCollection [Item] с новым Uri для ссылки. Вы видите, что ListView обновляется, но он все еще, кажется, «используется» где-то. – Niels

ответ

0

Если вы связываете путь изображения и хотите, чтобы изменить то, что отображается, а не заменять файл на диске, добавьте новое изображение с другим именем файла, а затем обновить ViewModel так вместо этого отображается новое изображение.

+0

Я хочу сделать как на самом деле .. Было бы лучшим решением быть: 1) Создайте новый файл с (2) после него, привяжите его к пользовательскому интерфейсу. 2) В следующий раз, когда загружается страница, удалите исходный файл и переименуйте (2) в исходное? – Niels

+0

@Niels Да, создайте новый файл и обновите viewmodel. В следующий раз при загрузке приложения или страницы (где вы больше не используете старый файл) удалите файл, который больше не нужен. –

0

Одним из возможных решений для вашей проблемы может быть использование Converter для преобразования ImageURI в BitmapImage для привязки. И в конвертере мы можем использовать using statement, чтобы освободить файловый ресурс.

Ниже приведен пример использования кода, размещенных:

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> 

Code-за:

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 не может быть хорошей практикой, это некоторые сценарии, так как это может блокировать ваш пользовательский интерфейс. Но я думаю, что это простое решение для вашей проблемы, вы можете решить, использовать ли его в соответствии с вашим реальным сценарием.