locked
Poor functioning of W8 Store SDK: saving a bitmap from a FlyOut

    General discussion

  • I've spent hours to find out the following. In my app's mainscreen, a photo could be made, displayed in an imagecontrol and saved in the picture directory. The screen contains many controls so to avoid the user having to scroll back I decided to create a FlyOut to display the photo and allow the user to modify the name before saving it. The photo can be taken by pressing a function key.

    The image shows in the image control nicely on the Flyout but when saving it as below I get a runtime error with the useless message Value does not fall within the expected range on:
    await renderTargetBitmap.RenderAsync(element, (int)element.Width, (int)element.Height);

    Finally I found the following link:

    http://social.msdn.microsoft.com/Forums/en-US/257a1dda-3b0c-4eec-9b08-85c1b9cda187/rendertargetbitmap-not-working-with-simple-stack-panel?forum=w81prevwCsharp

    with one of the replies is "Currently not supported". And indeed, even if I open, display and save the bitmap with exactly the same code as from the MainPage, RenderAsync doesn't work from the FlyOut.

    Why does Microsoft expect developers to create Windows Store apps with such a crippled OS? It's not the first thing I encountered which doesn't work as one would expect.

    Here's the code I use:

            private async void StorePhoto_Click(object sender, RoutedEventArgs e)
            // Clicked on button to save photo in FlyOut 22-7-2014
            {
                try
                {

                var picker = new Windows.Storage.Pickers.FileOpenPicker();
                picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
                picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
                picker.FileTypeFilter.Add("*");
                StorageFile file = await picker.PickSingleFileAsync();
                if (file != null)
                {
                    BitmapImage bitmapImage = new BitmapImage();
                    using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read))
                    {
                        bitmapImage.SetSource(fileStream);
                    }
                    CapturedPhoto.Source = bitmapImage;   // CapturedPhoto is an image control
                }
                    string cPhotoName = "Test.jpg";
                    if (!String.IsNullOrEmpty(cPhotoName))
                    {
                        StorageFolder storageFolder = KnownFolders.PicturesLibrary;                                
                        StorageFile fileout = await storageFolder.CreateFileAsync(cPhotoName,CreationCollisionOption.ReplaceExisting);    
                        if (file != null)                    
                        {
                            await SaveVisualElementToFile(this.CapturedPhoto, fileout);
                        }
                        else
                        {
                            await Extensions.ShowMessage("Empty file", "(Can not save photo)");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Extensions.ShowMessage(ex.Message,"");
                }
            }

            async Task SaveVisualElementToFile(FrameworkElement element, StorageFile file)
            // Save content from image control in picture lib under name assigned from calling method
            {
                try
                {
                    var renderTargetBitmap = new RenderTargetBitmap();
                    await renderTargetBitmap.RenderAsync(element, (int)element.Width, (int)element.Height);
                    var pixels = await renderTargetBitmap.GetPixelsAsync();

                    using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                    {
                        var encoder = await
                            BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
                        byte[] bytes = pixels.ToArray();
                        encoder.SetPixelData(BitmapPixelFormat.Bgra8,
                                             BitmapAlphaMode.Ignore,
                                             (uint)element.ActualWidth, (uint)element.ActualHeight,
                                             96, 96, bytes);

                        await encoder.FlushAsync();
                        var dlg = new Windows.UI.Popups.MessageDialog("Photo saved as " + file.Name);
                        await dlg.ShowAsync();

                    }
                }            // try
                catch (Exception ex)
                {
                    Extensions.ShowMessage("File can not be saved ", ex.Message);
                }
            }

    I've solved my problem by showing the image in the MainPage in an imagecontrol as well, and by calling the very same code to save the picture from the MainPage class on pressing the Save button in the FlyOut , but it has taken me hours to find out.

    Dick

    Friday, July 25, 2014 1:54 PM

All replies

  • This is called out in the RenderTargetBitmap documentation http://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn298556.aspx

    Do you have a suggestion for how that could be made more clear?

    I agree that the error message could be more specific.

    Note that Tim's "not supported" comment was in reference to background tasks and not relevant to your problem, although it has the same underlying cause. There is now a background task for rendering on the phone (that thread was about Windows Phone)


    Friday, July 25, 2014 2:07 PM
    Owner
  • Thank you Rob, for your quick reply. I think my main concerns is that the image shows in a control but can not be saved. Even though it now appears to be documented, the problem is IMO that it should work in the first place. Even better: it should have a had a SaveContent method by default...

    Together with the unclear error message it took me a lot of time before I suspected RenderTargetBitmap to be the problem, and when I finally found out that my working code from the MainPage didn't work in the FlyOut, I found the posting in the forum first instead of the RenderTargetBitmap MSDN doc.

    Apart from that: Content that's not directly connected to the XAML visual tree and the content of the main window won't be captured. This includes Popup content, which is considered to be like a sub-window.

    to me would suggest I can't use it from MainPage either (where it works) but as a FlyOut is a kind of popup I think this message would be clear enough. You may consider to also include FlyOut in the wording though. Again, I see no reason why it doesn't work in the first place. If OneCore is to be a success, there's still a lot to do for Microsoft. I've lost lots of time trying to use, in my W8 Store App, perfectly valid C# WPF code which just misses a property or a method or a parameter which I needed in my solution.


    Friday, July 25, 2014 3:38 PM