2

I'm trying to save an UIElement created programmatically in a JPG/PNG/BMP image in a Windows Phone 8.1 (C#) application.

I'm using the class RenderTargetBitmap using the method RenderAsync() but it only works with UI elements created in the XAML code. When I use it on UI elements created directly in C# I have this exception: "System.ArgumentException (Value does not fall within the expected range.)"

Am I doing something wrong or this class doesn't allow rendering of UIElement(s) created programmatically? Is there any way to do this on Windows Phone 8.1? Thank you!

Here's the code I use:

        private static async void RenderText(string text, int width, int height, int fontsize, string imagename)
    {
        RenderTargetBitmap b = new RenderTargetBitmap();

        var canvas = new Grid();

        canvas.Width = width;
        canvas.Height = height;

        var background = new Canvas();
        background.Height = width;
        background.Width = height;

        SolidColorBrush backColor = new SolidColorBrush(Colors.Red);
        background.Background = backColor;

        var textBlock = new TextBlock();
        textBlock.Text = text;
        textBlock.FontWeight = FontWeights.Bold;
        textBlock.TextAlignment = TextAlignment.Left;
        textBlock.HorizontalAlignment = HorizontalAlignment.Center;
        textBlock.VerticalAlignment = VerticalAlignment.Stretch;
        textBlock.Margin = new Thickness(35);
        //textBlock.Width = b.PixelWidth - textBlock.Margin.Left * 2;
        textBlock.TextWrapping = TextWrapping.Wrap;
        textBlock.Foreground = new SolidColorBrush(Colors.White); //color of the text on the Tile
        textBlock.FontSize = fontsize;

        canvas.Children.Add(textBlock);

        await b.RenderAsync(background);
        await b.RenderAsync(canvas);

        // Get the pixels

        var pixelBuffer = await b.GetPixelsAsync();


        // Get the local folder.
        StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;

        // Create a new folder name DataFolder.
        var dataFolder = await local.CreateFolderAsync("DataFolder",
            CreationCollisionOption.OpenIfExists);

        StorageFile file = await dataFolder.CreateFileAsync(imagename, CreationCollisionOption.ReplaceExisting);


        // Encode the image to the selected file on disk
        using (var fileStream = await file.OpenStreamForWriteAsync())
        {

            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream.AsRandomAccessStream());

            encoder.SetPixelData(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Ignore,
                (uint)b.PixelWidth,
                (uint)b.PixelHeight,
                DisplayInformation.GetForCurrentView().LogicalDpi,
                DisplayInformation.GetForCurrentView().LogicalDpi,
                pixelBuffer.ToArray());

            await encoder.FlushAsync();
        }
    }
DarioDP
  • 627
  • 2
  • 9
  • 24

2 Answers2

2

It is not working only with elements created in XAML but with these that (as MSDN says) are in VisualTree:

Renders a snapshot of a UIElement visual tree to an image source.

So your method will work if you add your elements for example to your current Page:

LayoutRoot.Children.Add(canvas);
Romasz
  • 29,662
  • 13
  • 79
  • 154
0

Your UIElemet Height and Width values will be 0 when you create them using code. If you want the image in a particular resolution, try to assign the same height and width to the UIElement as well. The code shown below worked well for me.

    public static void SaveElementAsJPG(FrameworkElement element, string ImageName)
    {
        WriteableBitmap wBitmap = new WriteableBitmap(element, null);
        using (MemoryStream stream = new MemoryStream())
        {
            wBitmap.SaveJpeg(stream, (int)element.ActualWidth, (int)element.ActualHeight, 0, 100);
            wBitmap = null;
            //Use can either save the file to isolated storage or media library.
            //Creates file in Isolated Storage.
            using (var local = new IsolatedStorageFileStream(ImageName, FileMode.Create, IsolatedStorageFile.GetUserStoreForApplication()))
            {
                local.Write(stream.GetBuffer(), 0, stream.GetBuffer().Length);
            }
            //Creates file in Media Library.
            var lib = new MediaLibrary();
            var picture = lib.SavePicture(ImageName, stream.GetBuffer());
        }
    }
Anzal M S
  • 89
  • 1
  • 7
  • WriteableBitmap doesn't have that constructor and SaveJpeg method on Windows Phone 8.1, that's why I'm using RenderTargetBitmap... :\ – DarioDP Apr 07 '14 at 08:51
  • Also, I set width and height via code, I've added a snippet above. – DarioDP Apr 07 '14 at 08:58
  • Even if you set the Height and Width properties, the ActualHeight and ActualWidth will still be zero. Hence the issue. I faced the same problem and what I did was, I placed the UIElement to be exported in a page. Then on export button click navigated to that page, performed the export and then navigated back. My client's requirement was to export the image and prompt for sharing. So the end user will never see the additional page which we create solely for exporting the image. – Anzal M S Apr 07 '14 at 09:21