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();
}
}
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);
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());
}
}
来源:https://stackoverflow.com/questions/22896979/any-way-to-save-an-uielement-created-programmatically-in-an-image-file