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");
}
}
![image]()
![image]()
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);
}
}
}