Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scrollable charts #1102

Merged
merged 15 commits into from
Jul 8, 2023
3 changes: 3 additions & 0 deletions samples/AvaloniaSample/AvaloniaSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
<Compile Update="General\MultiThreading\View.axaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<Compile Update="General\Scrollable\View.axaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<Compile Update="General\VisualElements\View.axaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
Expand Down
38 changes: 38 additions & 0 deletions samples/AvaloniaSample/General/Scrollable/View.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<UserControl x:Class="AvaloniaSample.General.Scrollable.View"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia"
xmlns:vms="using:ViewModelsSamples.General.Scrollable">
<UserControl.DataContext>
<vms:ViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>

<lvc:CartesianChart
Grid.Row="0"
Series="{Binding Series}"
XAxes="{Binding ScrollableAxes}"
ZoomMode="X"
DrawMargin="{Binding Margin}"
UpdateStarted="OnChart_Updated">
</lvc:CartesianChart>

<lvc:CartesianChart
x:Name="ScrollBarChart"
Grid.Row="1"
Series="{Binding ScrollbarSeries}"
DrawMargin="{Binding Margin}"
Sections="{Binding Thumbs}"
XAxes="{Binding InvisibleX}"
YAxes="{Binding InvisibleY}"
PointerPressed="OnPointerPressed"
PointerMoved="OnPointerMoved"
PointerReleased="OnPointerReleased"
TooltipPosition="Hidden">
</lvc:CartesianChart>
</Grid>
</UserControl>
72 changes: 72 additions & 0 deletions samples/AvaloniaSample/General/Scrollable/View.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Linq;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.SkiaSharpView.Avalonia;
using LiveChartsCore.SkiaSharpView.Drawing;
using ViewModelsSamples.General.Scrollable;

namespace AvaloniaSample.General.Scrollable;

public class View : UserControl
{
private bool _isDown = true;

public View()
{
InitializeComponent();
}

private void OnChart_Updated(IChartView<SkiaSharpDrawingContext> chart)
{
var vm = (ViewModel)DataContext!;
var cartesianChart = (CartesianChart)chart;

var x = cartesianChart.XAxes.First();

// update the scroll bar thumb when the chart is updated (zoom/pan)
// this will let the user know the current visible range
var thumb = vm.Thumbs[0];

thumb.Xi = x.MinLimit;
thumb.Xj = x.MaxLimit;
}

private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
_isDown = true;
}

private void OnPointerMoved(object? sender, PointerEventArgs e)
{
if (!_isDown) return;

var vm = (ViewModel)DataContext!;
var scrollBarChart = this.Find<CartesianChart>("ScrollBarChart");

var pointerPosition = e.GetPosition(scrollBarChart);
var positionInData = scrollBarChart.ScalePixelsToData(new(pointerPosition.X, pointerPosition.Y));

var thumb = vm.Thumbs[0];
var currentRange = thumb.Xj - thumb.Xi;

// update the scroll bar thumb when the user is dragging the chart
thumb.Xi = positionInData.X - currentRange / 2;
thumb.Xj = positionInData.X + currentRange / 2;

// update the chart visible range
vm.ScrollableAxes[0].MinLimit = thumb.Xi;
vm.ScrollableAxes[0].MaxLimit = thumb.Xj;
}

private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
{
_isDown = false;
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
94 changes: 94 additions & 0 deletions samples/BlazorSample/Pages/General/Scrollable.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@page "/General/Scrollable"
@using LiveChartsCore.Kernel.Sketches;
@using LiveChartsCore.SkiaSharpView.Blazor
@using LiveChartsCore.SkiaSharpView.Drawing;
@using ViewModelsSamples.General.Scrollable

<div class="bg-primary text-white p-3">
This is an experimental feature, it is not ready for production in Blazor, it seems that
the chart is updating multiple times resulting in a poor performance.
</div>

<CartesianChart
@ref="_mainChart"
Series="ViewModel.Series"
XAxes="ViewModel.ScrollableAxes"
ZoomMode="LiveChartsCore.Measure.ZoomAndPanMode.X"
DrawMargin="ViewModel.Margin">
</CartesianChart>

<div style="height: 100px">
<CartesianChart
@ref="_scrollableChart"
Series="ViewModel.ScrollbarSeries"
DrawMargin="ViewModel.Margin"
Sections="ViewModel.Thumbs"
XAxes="ViewModel.InvisibleX"
YAxes="ViewModel.InvisibleY"
OnPointerDownCallback="OnPointeDown"
OnPointerUpCallback="OnPointeUp"
OnPointerMoveCallback="OnPointeMove"
TooltipPosition="LiveChartsCore.Measure.TooltipPosition.Hidden">
</CartesianChart>
</div>

@code {
private bool _isDown;

public CartesianChart _mainChart = null!;
public CartesianChart _scrollableChart = null!;
public ViewModel ViewModel { get; set; } = new();

protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);

_mainChart.UpdateStarted += OnChart_Updated;
}

private void OnChart_Updated(IChartView<SkiaSharpDrawingContext> chart)
{
var vm = ViewModel;
var cartesianChart = (CartesianChart)chart;

var x = cartesianChart.XAxes.First();

// update the scroll bar thumb when the chart is updated (zoom/pan)
// this will let the user know the current visible range
var thumb = vm.Thumbs[0];

thumb.Xi = x.MinLimit;
thumb.Xj = x.MaxLimit;
}

private void OnPointeDown(PointerEventArgs e)
{
_isDown = true;
}

private void OnPointeMove(PointerEventArgs e)
{
if (!_isDown) return;

var vm = ViewModel;
var scrollBarChart = _scrollableChart;

var positionInData = scrollBarChart.ScalePixelsToData(new(e.OffsetX, e.OffsetY));

var thumb = vm.Thumbs[0];
var currentRange = thumb.Xj - thumb.Xi;

// update the scroll bar thumb when the user is dragging the chart
thumb.Xi = positionInData.X - currentRange / 2;
thumb.Xj = positionInData.X + currentRange / 2;

// update the chart visible range
vm.ScrollableAxes[0].MinLimit = thumb.Xi;
vm.ScrollableAxes[0].MaxLimit = thumb.Xj;
}

private void OnPointeUp(PointerEventArgs e)
{
_isDown = false;
}
}
3 changes: 1 addition & 2 deletions samples/EtoFormsSample/Axes/Shared/View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public View()

Content = new DynamicLayout(
new DynamicRow(new DynamicControl() { Control = cartesianChart, YScale = true }),
new DynamicRow(new DynamicControl() { Control = cartesianChart2, YScale = true })
);
new DynamicRow(new DynamicControl() { Control = cartesianChart2, YScale = true }));
}
}
94 changes: 94 additions & 0 deletions samples/EtoFormsSample/General/Scrollable/View.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System.Linq;
using Eto.Forms;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.SkiaSharpView.Drawing;
using LiveChartsCore.SkiaSharpView.Eto;
using ViewModelsSamples.General.Scrollable;

namespace EtoFormsSample.General.Scrollable;

public class View : Panel
{
private readonly ViewModel _viewModel = new();
private readonly CartesianChart _scrollBarChart;
private bool _isDown = true;

public View()
{
var viewModel = _viewModel;

var cartesianChart = new CartesianChart
{
Series = viewModel.Series,
XAxes = viewModel.ScrollableAxes,
ZoomMode = LiveChartsCore.Measure.ZoomAndPanMode.X,
DrawMargin = viewModel.Margin
};

cartesianChart.UpdateStarted += OnChart_Updated;

var cartesianChart2 = _scrollBarChart = new CartesianChart
{
Series = viewModel.ScrollbarSeries,
DrawMargin = viewModel.Margin,
Sections = viewModel.Thumbs,
XAxes = viewModel.InvisibleX,
YAxes = viewModel.InvisibleY,
TooltipPosition = LiveChartsCore.Measure.TooltipPosition.Hidden
};

cartesianChart2.MouseDown += CartesianChart2_MouseDown;
cartesianChart2.MouseMove += CartesianChart2_MouseMove;
cartesianChart2.MouseUp += CartesianChart2_MouseUp;

Content = new DynamicLayout(
new DynamicRow(new DynamicControl() { Control = cartesianChart, YScale = true }),
new DynamicRow(new DynamicControl() { Control = cartesianChart2, YScale = true }));
}

private void OnChart_Updated(IChartView<SkiaSharpDrawingContext> chart)
{
var vm = _viewModel;
var cartesianChart = (CartesianChart)chart;

var x = cartesianChart.XAxes.First();

// update the scroll bar thumb when the chart is updated (zoom/pan)
// this will let the user know the current visible range
var thumb = vm.Thumbs[0];

thumb.Xi = x.MinLimit;
thumb.Xj = x.MaxLimit;
}

private void CartesianChart2_MouseDown(object sender, MouseEventArgs e)
{
_isDown = true;
}

private void CartesianChart2_MouseMove(object sender, MouseEventArgs e)
{
if (!_isDown) return;

var vm = _viewModel;
var scrollBarChart = _scrollBarChart;

var positionInData = scrollBarChart.ScalePixelsToData(new(e.Location.X, e.Location.Y));

var thumb = vm.Thumbs[0];
var currentRange = thumb.Xj - thumb.Xi;

// update the scroll bar thumb when the user is dragging the chart
thumb.Xi = positionInData.X - currentRange / 2;
thumb.Xj = positionInData.X + currentRange / 2;

// update the chart visible range
vm.ScrollableAxes[0].MinLimit = thumb.Xi;
vm.ScrollableAxes[0].MaxLimit = thumb.Xj;
}

private void CartesianChart2_MouseUp(object sender, MouseEventArgs e)
{
_isDown = true;
}
}
7 changes: 6 additions & 1 deletion samples/MauiSample/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
xmlns:local="clr-namespace:MauiSample"
x:Class="MauiSample.App">
<Application.Resources>
<ResourceDictionary Source="Resources/Styles.xaml" />
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
52 changes: 52 additions & 0 deletions samples/MauiSample/General/Scrollable/View.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="MauiSample.General.Scrollable.View"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.Maui;assembly=LiveChartsCore.SkiaSharpView.Maui"
xmlns:vms="clr-namespace:ViewModelsSamples.General.Scrollable;assembly=ViewModelsSamples"
>
<ContentPage.BindingContext>
<vms:ViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<!--
This sample requies net MAUI 7 at least, if you are on net6.0 please update:
https://devblogs.microsoft.com/dotnet/dotnet-maui-dotnet-7/#upgrading-from-net-6
-->

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>

<lvc:CartesianChart
Grid.Row="0"
Series="{Binding Series}"
XAxes="{Binding ScrollableAxes}"
ZoomMode="X"
DrawMargin="{Binding Margin}"
UpdateStarted="OnChart_Updated">
</lvc:CartesianChart>

<lvc:CartesianChart
x:Name="ScrollBarChart"
Grid.Row="1"
Series="{Binding ScrollbarSeries}"
DrawMargin="{Binding Margin}"
Sections="{Binding Thumbs}"
XAxes="{Binding InvisibleX}"
YAxes="{Binding InvisibleY}"
TooltipPosition="Hidden"
>
<lvc:CartesianChart.GestureRecognizers>
<PointerGestureRecognizer
PointerEntered="OnPointerEntered"
PointerMoved="OnPointerMoved"
PointerExited="OnPointerExited">
</PointerGestureRecognizer>
</lvc:CartesianChart.GestureRecognizers>
</lvc:CartesianChart>
</Grid>
</ContentPage.Content>
</ContentPage>