7200rpm
7200rpm

Reputation: 568

Access Denied when trying to replace file with binding in WinRT

I have a WinRT app that takes pictures and displays them in an Image. The first picture correctly takes and displays the Image. However, subsequent pictures, which are set to overwrite the original picture, throw an UnauthorizedAccessException with ACCESSDENIED. I'm also binding the Image source to the Uri of the student. I'm also positive the binding is what is causing the issue in that WinRT doesn't like me replacing a file that is currently in use. I've tried setting the source to null before the file replace, etc, but that is not working. What is an elegant way to handle this? Also note that I bind this file on the page before this one also and I needed to remove the binding on that page also in order to avoid the error.

private async void btnTakePic_Click(object sender, RoutedEventArgs e)
    {
        await CameraCapture();
    }

private async Task CameraCapture()
    {
        CameraCaptureUI camUI = new CameraCaptureUI();

        camUI.PhotoSettings.AllowCropping = true;
        camUI.PhotoSettings.MaxResolution = CameraCaptureUIMaxPhotoResolution.MediumXga;
        camUI.PhotoSettings.CroppedAspectRatio = new Size(4, 3);

        Windows.Storage.StorageFile imageFile = await camUI.CaptureFileAsync(CameraCaptureUIMode.Photo);

        if (imageFile != null)
        {

            IRandomAccessStream stream = await imageFile.OpenAsync(FileAccessMode.Read);
            BitmapImage bitmapCamera = new BitmapImage();
            bitmapCamera.SetSource(stream);

            // Use unique id for image name since name could change

            string filename = student.StudentID + "Photo.jpg";

            await imageFile.MoveAsync(ApplicationData.Current.LocalFolder, filename, NameCollisionOption.ReplaceExisting);

            student.UriPhoto = new Uri(string.Format("ms-appdata:///local/{0}", filename), UriKind.Absolute);

        }
    }

Here is the binding portion just for fun:

<Image x:Name="imgStudent" Grid.Row="0" Width="400" Height="300" Margin="15" Grid.ColumnSpan="2">
    <Image.Source>
        <BitmapImage DecodePixelWidth="200" UriSource="{Binding UriPhoto}" />
    </Image.Source>
</Image>

Upvotes: 2

Views: 542

Answers (1)

7200rpm
7200rpm

Reputation: 568

Ok, well after several hours of research, I've concluded that it is near impossible to bind to a Uri, and the alternative is to bind a BitmapImage. I couldn't find the exact reason, but it has something to do with the fact that using a Uri leaves the BitmapImage open and binding quickly breaks this paradigm. The solution is to bind the BitmapImage and set the BitmapImage to a stream, which seems to support binding quite well.

I liked the idea of the Uri since its easily seriliazable, while the BitmapImage is more difficult and takes up significantly more space (since you are serializing the picture data as opposed to a link). The solution that I decided on and that works is to serialize the Uri, and use the OnDeserialized attribute to create the BitmapImage on startup. I then needed a method/event to reset the BitmapImage whenever the Uri changed.

Here's the final code to recap:

private async Task CameraCapture()
{
    CameraCaptureUI camUI = new CameraCaptureUI();

    camUI.PhotoSettings.AllowCropping = true;
    camUI.PhotoSettings.MaxResolution = CameraCaptureUIMaxPhotoResolution.MediumXga;
    camUI.PhotoSettings.CroppedAspectRatio = new Size(4, 3);

    Windows.Storage.StorageFile imageFile = await camUI.CaptureFileAsync(CameraCaptureUIMode.Photo);

    if (imageFile != null)
    {
        // Use unique id for image name since name could change

        string filename = student.StudentID + "Photo.jpg";

        // Move photo to Local Storate and overwrite existing file

        await imageFile.MoveAsync(ApplicationData.Current.LocalFolder, filename, NameCollisionOption.ReplaceExisting);

        // Open file stream of photo

        IRandomAccessStream stream = await imageFile.OpenAsync(FileAccessMode.Read);
        BitmapImage bitmapCamera = new BitmapImage();
        bitmapCamera.SetSource(stream);

        student.BitmapPhoto = bitmapCamera // BitmapImage

        // Save Uri to photo since we can serialize this and re-create BitmapPhoto on startup/deserialization

        student.UriPhoto = new Uri(string.Format("ms-appdata:///local/{0}", filename), UriKind.Absolute);

    }
}

And the new XAML binding to the student BitmapImage

<Image x:Name="imgStudent" Source="{Binding BitmapPhoto}" Grid.Row="0" Width="400" Height="300" Margin="15" Grid.ColumnSpan="2"/>

Upvotes: 2

Related Questions