Install-Package LiveChartsCore.SkiaSharpView.WPF -version 2.0.0-rc5.4
//xaml
<Window x:Class="WpfApp44.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"
xmlns:behavior="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:WpfApp44"
WindowState="Maximized"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<behavior:Interaction.Triggers>
<behavior:EventTrigger EventName="MouseLeftButtonDown">
<behavior:InvokeCommandAction Command="{Binding MouseLeftButtonDownCommand}"/>
</behavior:EventTrigger>
</behavior:Interaction.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<lvc:CartesianChart
Background="White"
Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="2"
Series="{Binding XYLineSeries}"
XAxes="{Binding XValues}"
YAxes="{Binding YValues}">
<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>
<lvc:CartesianChart
Background="White"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
Margin="50,0,0,0"
Series="{Binding XYScatterSeries}"
XAxes="{Binding XValues}"
YAxes="{Binding YValues}">
<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 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;
using LiveChartsCore.SkiaSharpView;
using System.Drawing;
using LiveChartsCore;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView.Painting;
using SkiaSharp;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using LiveChartsCore.SkiaSharpView.WPF;
using System.IO;
using System.Diagnostics;
namespace WpfApp44
{
/// <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 : INotifyPropertyChanged
{
private ISeries[] xYLineSeries;
public ISeries[] XYLineSeries
{
get
{
return xYLineSeries;
}
set
{
if(value!=xYLineSeries)
{
xYLineSeries = value;
OnPropertyChanged();
}
}
}
private ISeries[] xYScatterSeries;
public ISeries[] XYScatterSeries
{
get
{
return xYScatterSeries;
}
set
{
if(value!=xYScatterSeries)
{
xYScatterSeries=value;
OnPropertyChanged();
}
}
}
public Axis[] XValues { get; set; }
public Axis[] YValues { get; set; }
public List<ObservablePoint> DataPoints { get; } = new List<ObservablePoint>();
Random rnd = new Random();
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "")
{
var handler = PropertyChanged;
if (handler!=null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
private int dpiFactor = 2;
public MainVM()
{
XValues=new Axis[]
{
new Axis
{
Name="X Axis",
Labeler=value=>value.ToString("N")
}
};
YValues=new Axis[]
{
new Axis
{
Name="Y Axis",
Labeler=value=>value.ToString("N2")
}
};
InitData();
}
private void InitData()
{
DataPoints.Clear();
for (int i = 0; i<10; i++)
{
DataPoints.Add(new ObservablePoint(i, rnd.NextDouble()*10));
}
XYLineSeries=new ISeries[]
{
new LineSeries<ObservablePoint>
{
Values=DataPoints,
Stroke=new SolidColorPaint(SKColors.Blue)
{
StrokeThickness=2
},
Fill=null,
GeometryStroke=null,
GeometryFill=null
}
};
XYScatterSeries=new ISeries[]
{
new ScatterSeries<ObservablePoint>
{
Values=DataPoints,
Stroke=null,
Fill=new SolidColorPaint(SKColors.Red),
GeometrySize=10
}
};
}
private ICommand mouseLeftButtonDownCommand;
public ICommand MouseLeftButtonDownCommand
{
get
{
if (mouseLeftButtonDownCommand==null)
{
mouseLeftButtonDownCommand=new DelCommand(MouseLeftButtonDownCommandExecuted);
}
return mouseLeftButtonDownCommand;
}
}
private void MouseLeftButtonDownCommandExecuted(object? obj)
{
InitData();
}
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)
{
SaveFrameworkElementAsJpg(fe);
}
}
public void SaveFrameworkElementAsJpg(FrameworkElement fe)
{
if(fe==null)
{
return;
}
int feWidth=(int)fe.ActualWidth;
int feHeight=(int)fe.ActualHeight;
RenderTargetBitmap rtb = new RenderTargetBitmap(feWidth*dpiFactor, feHeight*dpiFactor,
96*dpiFactor, 96*dpiFactor, PixelFormats.Pbgra32);
DrawingVisual drawingVisual=new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
VisualBrush visualBrush = new VisualBrush(fe);
drawingContext.DrawRectangle(visualBrush, null, new Rect(0, 0, feWidth, feHeight));
}
rtb.Render(drawingVisual);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
string jpgFile = $"Chart_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}{Guid.NewGuid().ToString("N")}.jpg";
using (FileStream fileStream = new FileStream(jpgFile, FileMode.Create))
{
encoder.Save(fileStream);
}
var proc = new Process
{
StartInfo=new ProcessStartInfo()
{
FileName= jpgFile,
UseShellExecute=true
}
};
proc.Start();
}
}
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);
}
}
}
![image]()
![image]()
![image]()