Export RadCartesianChart to JPG/PNG/PDF UWP windows 10

5 posts, 1 answers
  1. Bhushan
    Bhushan avatar
    3 posts
    Member since:
    Oct 2016

    Posted 01 Dec 2016 Link to this post

    Do kendo support export chart functionality for RadCartesianChart control. If yes how can achieve it?

    I am using RadCartesianChart with MVVM pattern.

  2. Answer
    Lance | Tech Support Engineer, Sr.
    Admin
    Lance | Tech Support Engineer, Sr. avatar
    283 posts

    Posted 01 Dec 2016 Link to this post

    Hello Bhushan,

    I'm not sure which product you're asking about since you mention Kendo and RadCartesianChart. The product you submitted this post under (UI for UWP) doesn't use Kendo components.

    I will answer in the context of UI for UWP:

    The UI for UWP suite doesn't explicitly have an export chart function, however this is very easy to do in a UWP application. You can render any UIElement to a jpg by using RenderTargetBitmap. See that documentation's example for the full code, here is how to use it for the Chart.

    Just pass the chart into the RenderAsync method like this:

    var renderTargetBitmap = new RenderTargetBitmap();
    await renderTargetBitmap.RenderAsync(MyChart);
    RenderOutputImage.Source = renderTargetBitmap;

    You can then encode the bitmap and share/save it as you see fit using the BitmapEncoder class. Note that this isn't directly related to Telerik UI for UWP components, so it falls outside the scope of Telerik Support.

    That said, I built you a small demo that shows how to accomplish this (see screenshot and demo attached). The first button click renders the chart to a bitmap, the second button click saves it to a file.

    Here is the code:

    MainPage.xaml
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
     
            <TextBlock Text="Telerik RadChart"
                       HorizontalAlignment="Center"
                       Style="{StaticResource TitleTextBlockStyle}" />
     
            <TextBlock Text="Output Image"
                       Grid.Column="1"
                       HorizontalAlignment="Center"
                       Style="{StaticResource TitleTextBlockStyle}" />
     
            <chart:RadCartesianChart x:Name="MyChart"
                                     Grid.Row="1">
                <chart:RadCartesianChart.HorizontalAxis>
                    <chart:CategoricalAxis />
                </chart:RadCartesianChart.HorizontalAxis>
                <chart:RadCartesianChart.VerticalAxis>
                    <chart:LinearAxis />
                </chart:RadCartesianChart.VerticalAxis>
                <chart:BarSeries x:Name="MyBarSeries" />
            </chart:RadCartesianChart>
     
            <Image x:Name="RenderOutputImage"
                   Grid.Column="1"
                   Grid.Row="1" />
             
            <Button x:Name="RenderButton"
                    Content="Render Chart to Bitmap"
                    Margin="20,10,20,30"
                    Grid.Row="2"
                    Grid.ColumnSpan="2"
                    HorizontalAlignment="Center"
                    Click="RenderToJpgButton_OnClick" />
     
            <Button x:Name="SaveButton"
                    Content="Render Chart to Bitmap"
                    Margin="20,10,20,30"
                    Grid.Row="2"
                    Grid.ColumnSpan="2"
                    HorizontalAlignment="Center"
                    Visibility="Collapsed"
                    Click="SaveButton_OnClick"/>
     
            <TextBlock x:Name="StatusTextBlock"
                       Text="Click button to render Chart into bitmap"
                       Grid.Row="2"
                       Grid.ColumnSpan="2"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Bottom"/>
        </Grid>


    MainPage.xaml.cs
    public sealed partial class MainPage : Page
        {
            private RenderTargetBitmap rtb;
     
            public MainPage()
            {
                this.InitializeComponent();
                MyBarSeries.ItemsSource = new ObservableCollection<double>  { 20, 30, 50, 10, 60, 40, 20, 80 };
            }
     
            private async void RenderToJpgButton_OnClick(object sender, RoutedEventArgs e)
            {
                await RenderChartToBitmapAsync();
            }
     
            private async void SaveButton_OnClick(object sender, RoutedEventArgs e)
            {
                await SaveBitmapAsync();
            }
     
            private async Task RenderChartToBitmapAsync()
            {
                StatusTextBlock.Text = $"starting render...";
     
                rtb = new RenderTargetBitmap();
                await rtb.RenderAsync(MyChart);
     
                StatusTextBlock.Text = $"render complete, setting Image source in UI...";
     
                // Show the image in the UI
                RenderOutputImage.Source = rtb;
     
                StatusTextBlock.Text = $"Rednering done! Click button to save to file";
     
                RenderButton.Visibility = Visibility.Collapsed;
                SaveButton.Visibility = Visibility.Visible;
            }
     
            private async Task SaveBitmapAsync()
            {
                if (rtb == null)
                {
                    StatusTextBlock.Text = $"RenderTargetBitmap was null. Try again.";
                    RenderButton.Visibility = Visibility.Visible;
                    SaveButton.Visibility = Visibility.Collapsed;
                    return;
                }
     
                var pixelBuffer = await rtb.GetPixelsAsync();
     
                var savePicker = new FileSavePicker();
                savePicker.DefaultFileExtension = ".png";
                savePicker.FileTypeChoices.Add(".png", new List<string> { ".png" });
                savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                savePicker.SuggestedFileName = "MyChart.png";
                 
                var saveFile = await savePicker.PickSaveFileAsync();
                 
                if (saveFile == null)
                    return;
     
                // Encode the image to the selected file on disk (docs https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmapencoder.aspx )
                using (var fileStream = await saveFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream);
     
                    encoder.SetPixelData(
                        BitmapPixelFormat.Bgra8,
                        BitmapAlphaMode.Ignore,
                        (uint)rtb.PixelWidth,
                        (uint)rtb.PixelHeight,
                        DisplayInformation.GetForCurrentView().LogicalDpi,
                        DisplayInformation.GetForCurrentView().LogicalDpi,
                        pixelBuffer.ToArray());
     
                    await encoder.FlushAsync();
                }
     
                StatusTextBlock.Text = $"File Saved to {saveFile.Path}";
            }
        }

    Please let us know if you have any further questions. If this answer works for you, please consider marking this thread as answered.

    Thank you for contacting support and for choosing Telerik by Progress!

    Regards,
    Lance | Tech Support Engineer, Sr.
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  3. Bhushan
    Bhushan avatar
    3 posts
    Member since:
    Oct 2016

    Posted 01 Dec 2016 in reply to Lance | Tech Support Engineer, Sr. Link to this post

    Hello Lance,

    Thank you for your help.

    I am using "UI for Universal Windows Platform" product in my UWP application.

    I have tried your given solution and it works fine. Thank you.

    Can we do the same thing in ViewModel instead of Code Behind.

    Thanks.

  4. Lance | Tech Support Engineer, Sr.
    Admin
    Lance | Tech Support Engineer, Sr. avatar
    283 posts

    Posted 02 Dec 2016 Link to this post

    Hello Bhushan,

    I don't see why not. All you need from the View is the UIElement for the RenderTargetBitmap to do it's job. 

    Create a Command that passes a UIElement as it's parameter. A DelegateCommand is best for this type of thing. For your convenience, here is a DelegateCommand that I've written that works with and without a parameter:

    public class DelegateCommand : DelegateCommand<object>
        {
            public DelegateCommand(Action execute, Func<bool> canExecute)
                : base(_ => execute(), _ => canExecute())
            {
     
                if (execute == null)
                    throw new ArgumentNullException(nameof(execute));
     
                if (canExecute == null)
                    throw new ArgumentNullException(nameof(canExecute));
            }
     
            public DelegateCommand(Action execute)
                : this(execute, () => true)
            {
            }
        }
     
        public class DelegateCommand<T> : ICommand
        {
     
            private Action<T> execute;
            private Func<T, bool> canExecute;
     
            public event EventHandler CanExecuteChanged;
     
            public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
            {
     
                if (execute == null)
                    throw new ArgumentNullException(nameof(execute));
     
                if (canExecute == null)
                    throw new ArgumentNullException(nameof(canExecute));
     
     
                this.execute = execute;
                this.canExecute = canExecute;
            }
     
            public DelegateCommand(Action<T> execute)
                : this(execute, _ => true)
            {
            }
     
            public bool CanExecute(object parameter)
            {
                if (!(parameter is T) && parameter != (object)default(T))
                    return false;
     
                return canExecute((T)parameter);
            }
     
            public void Execute(object parameter)
            {
                execute((T)parameter);
            }
     
            public void NotifyCanExecuteChanged()
            {
                EventHandler eventHandler = CanExecuteChanged;
     
                eventHandler?.Invoke(this, EventArgs.Empty);
            }
        }

    With this, in your ViewModel you can create an instance of the command and the UIElement is passed to the action.

    private DelegateCommand<UIElement> renderChartCommand;
    public DelegateCommand<UIElement> RenderChartCommand
    {
        get { return renderChartCommand ?? (renderChartCommand = new DelegateCommand<UIElement>(async (e) => await RenderElement(e))); }
    }
     
    private async Task RenderElement(UIElement element)
    {
        // Do your rendering work here
    }


    Then in the view, you can set up your Button to use the command and pass the UIElement:

    <chart:RadCartesianChart x:Name="MyChart" ...>
     
    <Button  Command="{Binding RenderChartCommand}"
         CommandParameter="{Binding ElementName=MyChart}"/>


    I've updated my demo to use MVVM for everything and the rendering happens in MainPageViewModel.

    Regards,
    Lance | Tech Support Engineer, Sr.
    Telerik by Progress
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  5. Bhushan
    Bhushan avatar
    3 posts
    Member since:
    Oct 2016

    Posted 04 Dec 2016 in reply to Lance | Tech Support Engineer, Sr. Link to this post

    Hello lance,

    Thank you for your help. :)

    This is working as per expectations. 

     

Back to Top