Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Export scrollview as an image

Hello everybody,
I would like to export the content of a scrollview as an image, like I already did for another W8.1 project.
I know there is not a native Xamarin Forms function or method to get this result, so I started creating a **DependencyService **with no luck. Using the following code the exported image is a full screenshot of my application, and not only the content of the scrollview.

using AuditManager.Interfaces;
using AuditManager.UWP.Helpers;
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: Dependency(typeof(ScreenshotManager))]
namespace AuditManager.UWP.Helpers
{
public class ScreenshotManager : IScreenshotManager
{
public async Task<string> CaptureAsync(View view, Rectangle size, string controlName, string pathFolder, string nomeFile)
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
IVisualElementRenderer renderer = Platform.GetRenderer(view);
FrameworkElement frameworkElement = renderer.ContainerElement;

try
{
await renderTargetBitmap.RenderAsync(frameworkElement.FindControl<ScrollViewer>(controlName));
IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();

StorageFolder storageFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(pathFolder, CreationCollisionOption.OpenIfExists);
StorageFile file = await storageFolder.CreateFileAsync(nomeFile, CreationCollisionOption.GenerateUniqueName);

using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight, 96d, 96d,
pixelBuffer.ToArray());

await encoder.FlushAsync();
}

return file.Path;
}
catch (Exception ex)
{
return string.Empty;
}
}
}
}

This is the FindControl method, which always returns null:

public static T FindControl<T>(this UIElement parent, string ControlName) where T : FrameworkElement
{
if (parent == null)
return null;

if (parent.GetType() == typeof(T) && ((T)parent).Name == ControlName)
return (T)parent;

T result = null;

int count = VisualTreeHelper.GetChildrenCount(parent);

for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);

if (FindControl<T>(child, ControlName) != null)
{
result = FindControl<T>(child, ControlName);
break;
}
}

return result;
}

So I decided to create a CustomRenderer of the scrollview, but the result is always the same. Defining an EventHandler and an event method associated to it, I correctly get the "ContainerElement" but when I try to render it with "RenderAsync" I always get null or an exception like "_Value does not fall within the expected range".

This is the CustomRenderer:

using System;
using Xamarin.Forms;

namespace AuditManager.Controls
{
public class CustomScrollView : ScrollView
{
public event EventHandler ExportAsImageRequested;

public CustomScrollView()
{

}

public string ExportAsImage(string attachFolder, string fileName, string objectName)
{
MyEventArgs eventArgs = new MyEventArgs
{
FolderName = attachFolder,
FileName = fileName,
ObjectName = objectName
};

ExportAsImageRequested?.Invoke(this, eventArgs);

return eventArgs.FilePath;
}
}

public class MyEventArgs : EventArgs
{
public string FileName { get; set; }
public string FolderName { get; set; }
public string ObjectName { get; set; }
public string FilePath { get; set; }
}
}

using System;
using System.Runtime.InteropServices.WindowsRuntime;
using AuditManager.Controls;
using AuditManager.UWP.Helpers;
using AuditManager.UWP.Renderer;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Xamarin.Forms.Platform.UWP;

[assembly: ExportRenderer(typeof(CustomScrollView), typeof(CustomScrollViewRenderer))]
namespace AuditManager.UWP.Renderer
{
public class CustomScrollViewRenderer : ViewRenderer<CustomScrollView, ScrollViewer>
{
ScrollViewer scrollView;

protected override void OnElementChanged(ElementChangedEventArgs<CustomScrollView> e)
{
base.OnElementChanged(e);

if (e.OldElement != null)
{
e.OldElement.ExportAsImageRequested -= OnElementExportAsImageRequested;
}

if (e.NewElement != null)
{
if (Control == null)
{
scrollView = new ScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
VerticalScrollMode = ScrollMode.Auto,
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
HorizontalScrollMode = ScrollMode.Disabled,
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
ZoomMode = ZoomMode.Disabled,
IsEnabled = true
};

SetNativeControl(scrollView);
}

e.NewElement.ExportAsImageRequested += OnElementExportAsImageRequested;
}
}

private async void OnElementExportAsImageRequested(object sender, EventArgs e)
{
//CustomScrollView scrollView = sender as CustomScrollView;

if (e is MyEventArgs)
{
MyEventArgs evt = e as MyEventArgs;

RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
//IVisualElementRenderer renderer = scrollView.GetOrCreateRenderer();
//IVisualElementRenderer renderer = Platform.GetRenderer(scrollView);
//FrameworkElement frameworkElement = renderer.ContainerElement;

try
{
//await renderTargetBitmap.RenderAsync(frameworkElement.FindName(evt.ObjectName) as ScrollViewer);
//await renderTargetBitmap.RenderAsync(frameworkElement.FindControl<ScrollViewer>(evt.ObjectName));
await renderTargetBitmap.RenderAsync(this);
IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();

StorageFolder storageFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(evt.FolderName, CreationCollisionOption.OpenIfExists);
StorageFile file = await storageFolder.CreateFileAsync(evt.FileName, CreationCollisionOption.GenerateUniqueName);

using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight, 96d, 96d,
pixelBuffer.ToArray());

await encoder.FlushAsync();
}

evt.FilePath = file.Path;
}
catch (Exception ex)
{
evt.FilePath = string.Empty;
}
}
}
}
}

I have then another problem with the scrollviewer, but that's another story...

How can I achieve my goal?

Thanks in advance,
Matteo

Sign In or Register to comment.