WPF save Framework as Jpg via RenderTargetBitmap, DrawingVisual, DrawingContext, RenderTargetBitamp.Render(DrawingVisual), JpegBitmapEncoder Frame Add BitmapFrame Create(rendertargetbitmap)
private void SaveFrameworkElementAsJpg(FrameworkElement fe, string jpgFilePath, int dpi = 96) { var size = new Size(fe.ActualWidth, fe.ActualHeight); var renderBitmap = new RenderTargetBitmap((int)(size.Width*dipFactor), (int)(size.Height*dipFactor), dpi*dipFactor, dpi*dipFactor, PixelFormats.Pbgra32); var drawingVisual = new DrawingVisual(); using (DrawingContext drawingContext = drawingVisual.RenderOpen()) { var visualBrush = new VisualBrush(fe); drawingContext.DrawRectangle( visualBrush, null, new Rect(new Point(0, 0), new Point(size.Width, size.Height))); } renderBitmap.Render(drawingVisual); var jpgEncoder = new JpegBitmapEncoder(); jpgEncoder.QualityLevel=95; jpgEncoder.Frames.Add(BitmapFrame.Create(renderBitmap)); using (var fileStream = new FileStream(jpgFilePath, FileMode.Create)) { jpgEncoder.Save(fileStream); MessageBox.Show($"Saved in {jpgFilePath}", "Save Chart As Jpg"); } }
install-package LiveChartsCore.SkiaSharpView -version 2.0.0-rc5.4; install-package LiveChartsCore.SkiaSharpView.WPF -version 2.0.0-rc5.4;
//xaml <Window x:Class="WpfApp39.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF" WindowState="Maximized" xmlns:local="clr-namespace:WpfApp39" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <lvc:CartesianChart Grid.Row="1" Grid.Column="1" Series="{Binding SinLineSeries}" XAxes="{Binding XValueAxes}" YAxes="{Binding YValueAxes}" ZoomMode="X" TooltipPosition="Top" LegendPosition="Top" DrawMargin="{Binding SinFrame}" Background="White"> <lvc:CartesianChart.ContextMenu> <ContextMenu> <MenuItem Header="Save As Jpg" Command="{Binding SaveAsJpgCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContextMenu}}, Path=PlacementTarget}" /> </ContextMenu> </lvc:CartesianChart.ContextMenu> </lvc:CartesianChart> </Grid> </Window> //cs using LiveChartsCore; using LiveChartsCore.Defaults; using LiveChartsCore.SkiaSharpView; using LiveChartsCore.SkiaSharpView.Painting; using SkiaSharp; using System.Collections.ObjectModel; using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp39 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var vm = new MainVM(); this.DataContext=vm; } } public class MainVM { public ISeries[] SinLineSeries { get; set; } public Axis[] XValueAxes { get; set; } public Axis[] YValueAxes { get; set; } public DrawMarginFrame SinFrame { get; set; } private double dipFactor = 2; public MainVM() { InitSinChart(); } private void InitSinChart() { var points = new ObservableCollection<ObservablePoint>(); for (double x = 0; x<4*Math.PI; x+=0.1) { points.Add(new ObservablePoint(x, Math.Sin(x))); } SinLineSeries=new ISeries[] { new LineSeries<ObservablePoint> { Values=points, Stroke=new SolidColorPaint(SKColors.Blue) { StrokeThickness=3 }, Fill=null, GeometrySize=0, Name="Sin(x)", XToolTipLabelFormatter=x=>$"X:{x.Model!.X:F2}", YToolTipLabelFormatter=y=>$"Y:{y.Model!.Y:F4}" } }; var explicitXValues = new double[] { 0, Math.PI/2, Math.PI, 3*Math.PI/2, 2*Math.PI, 2.5*Math.PI, 3*Math.PI, 3.5*Math.PI, 4*Math.PI }; var explicitPoints = new ObservableCollection<ObservablePoint>(); foreach (var x in explicitXValues) { explicitPoints.Add(new ObservablePoint(x, Math.Sin(x))); } XValueAxes = new Axis[] { new Axis { Name = "x (radians)", // Force labels at exactly our explicit points ForceStepToMin = true, MinStep = Math.PI/2, Labeler = x => { // Custom label formatting for our specific points if (Math.Abs(x - 0) < 0.01) return "0"; if (Math.Abs(x - Math.PI/2) < 0.01) return "π/2"; if (Math.Abs(x - Math.PI) < 0.01) return "π"; if (Math.Abs(x - 3*Math.PI/2) < 0.01) return "1.5π"; if (Math.Abs(x - 2*Math.PI) < 0.01) return "2π"; if (Math.Abs(x - 2.5*Math.PI) < 0.01) return "2.5π"; if (Math.Abs(x - 3*Math.PI) < 0.01) return "3π"; if (Math.Abs(x - 3.5*Math.PI) < 0.01) return "3.5π"; if (Math.Abs(x - 4*Math.PI) < 0.01) return "4π"; return $"{x/Math.PI:F2}π"; }, LabelsRotation = 15, TextSize = 12 } }; YValueAxes=new Axis[] { new Axis { Name="Sin(X)", MinLimit=-1.2, MaxLimit=1.2, Labeler=value=>$"{value:F2}" } }; SinFrame=new DrawMarginFrame { Fill=new SolidColorPaint(SKColors.Transparent), Stroke=new SolidColorPaint(SKColors.LightGray, 1) }; } private ICommand saveAsJpgCommand; public ICommand SaveAsJpgCommand { get { if (saveAsJpgCommand==null) { saveAsJpgCommand=new DelCommand(SaveAsJpgCommandExecuted); } return saveAsJpgCommand; } } private void SaveAsJpgCommandExecuted(object? obj) { var fe = obj as FrameworkElement;
if(fe==null)
{
return;
} string jpgFilePath = $"Chart_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.jpg"; SaveFrameworkElementAsJpg(fe, jpgFilePath); } private void SaveFrameworkElementAsJpg(FrameworkElement fe, string jpgFilePath, int dpi = 96) { var size = new Size(fe.ActualWidth, fe.ActualHeight); var renderBitmap = new RenderTargetBitmap((int)(size.Width*dipFactor), (int)(size.Height*dipFactor), dpi*dipFactor, dpi*dipFactor, PixelFormats.Pbgra32); var drawingVisual = new DrawingVisual(); using (DrawingContext drawingContext = drawingVisual.RenderOpen()) { var visualBrush = new VisualBrush(fe); drawingContext.DrawRectangle( visualBrush, null, new Rect(new Point(0, 0), new Point(size.Width, size.Height))); } renderBitmap.Render(drawingVisual); var jpgEncoder = new JpegBitmapEncoder(); jpgEncoder.QualityLevel=95; jpgEncoder.Frames.Add(BitmapFrame.Create(renderBitmap)); using (var fileStream = new FileStream(jpgFilePath, FileMode.Create)) { jpgEncoder.Save(fileStream); MessageBox.Show($"Saved in {jpgFilePath}", "Save Chart As Jpg"); } } } public class DelCommand : ICommand { private readonly Action<object?> execute; private readonly Predicate<object?> canExecute; public DelCommand(Action<object?> executeValue, Predicate<object?> canExecuteValue = null) { execute=executeValue; canExecute=canExecuteValue; } public event EventHandler? CanExecuteChanged { add { CommandManager.RequerySuggested+=value; } remove { CommandManager.RequerySuggested -= value; } } public bool CanExecute(object? parameter) { return canExecute==null ? true : canExecute(parameter); } public void Execute(object? parameter) { execute(parameter); } } }