SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, iOS Chart, Android Chart and JavaScript Chart Components
SciChart WPF ships with hundreds of WPF Chart Examples which you can browse, play with, view the source-code and even export each WPF Chart Example to a stand-alone Visual Studio solution. All of this is possible with the new and improved SciChart WPF Examples Suite, which ships as part of the SciChart WPF SDK.
A Digital Analyzer is an electronic instrument that captures and displays multiple signals from a piece of hardware, such as a Data Acquisition Card or Logic Analyzer.
The Digital Analyzer demo shows multiple channels with randomly generated binary data with a common XAxis.
It is implemented using the MVVM pattern to display multi-surface pane rendering FastLineRenderableSeries that use UniformXyDataSeries as data source. This DataSeries type requires Y-Values only, thus reducing memory consumption.
In this way it is possible to display tens of billions of points on a single view.
This demo loads up to 64 Billion Datapoints! (Requires 64GB RAM). 100 Billion Datapoints are also possible with 128GB of RAM.
Interactivity is provided by ChartModifiers.
The following interactions are possible:
– CTRL & Mousewheel to scroll the channels up and down
– SHIFT & Mousewheel to change the height of channels
– Mousewheel to zoom channel data in and out
– Click and Drag Left Mouse button to zoom the channel data
– Double click the Left Mouse Button to zoom data to extents
On the left toolbar you can change the number of channels, number of points per channel and click LOAD to load incredible Big Datasets of up to 64 Billion Datapoints!
The C#/WPF source code for the WPF Digital Logic Analyzer Demo example is included below (Scroll down!).
Did you know you can also view the source code from one of the following sources as well?
<UserControl x:Class="SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer.DigitalAnalyzerExampleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ext="http://schemas.abtsoftware.co.uk/scichart/exampleExternals"
xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
xmlns:local="clr-namespace:SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer"
xmlns:common="clr-namespace:SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer.Common"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<UserControl.Resources>
<s:InvertBooleanConverter x:Key="InvertBooleanConverter" />
<s:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<common:TimeLabelProvider x:Key="TimeLabelProvider"/>
<common:PointCountToLabelConverter x:Key="PointCountToLabelConverter"/>
<Style x:Key="HiddenAxisStyle" TargetType="s:AxisBase">
<Setter Property="DrawLabels" Value="False" />
<Setter Property="DrawMajorBands" Value="False" />
<Setter Property="DrawMajorGridLines" Value="False" />
<Setter Property="DrawMajorTicks" Value="False" />
<Setter Property="DrawMinorGridLines" Value="False" />
<Setter Property="DrawMinorTicks" Value="False" />
<Setter Property="AutoRange" Value="Never" />
</Style>
<Style x:Key="TimeAxisMajorTickLineStyle" TargetType="Line">
<Setter Property="Stroke" Value="DimGray" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="StrokeEndLineCap" Value="Square" />
<Setter Property="X2" Value="6" />
<Setter Property="Y2" Value="6" />
</Style>
<Style x:Key="TimeAxisMinorTickLineStyle" TargetType="Line">
<Setter Property="Stroke" Value="DimGray" />
<Setter Property="StrokeThickness" Value="1" />
<Setter Property="StrokeEndLineCap" Value="Square" />
<Setter Property="X2" Value="3" />
<Setter Property="Y2" Value="3" />
</Style>
<x:Array x:Key="ChannelTypeSource" Type="sys:String">
<sys:String>Digital</sys:String>
<sys:String>Analog</sys:String>
</x:Array>
<x:Array x:Key="ChannelCountSource" Type="sys:Int32">
<sys:Int32>8</sys:Int32>
<sys:Int32>16</sys:Int32>
<sys:Int32>32</sys:Int32>
<sys:Int32>64</sys:Int32>
</x:Array>
<x:Array x:Key="PointCountSource" Type="sys:Int32">
<sys:Int32>1000000</sys:Int32>
<sys:Int32>10000000</sys:Int32>
<sys:Int32>100000000</sys:Int32>
<sys:Int32>1000000000</sys:Int32>
</x:Array>
<x:Array x:Key="StrokeThicknesses" Type="sys:Int32">
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
</x:Array>
<x:Array x:Key="ResamplingEnabled" Type="sys:Boolean">
<sys:Boolean>True</sys:Boolean>
<sys:Boolean>False</sys:Boolean>
</x:Array>
<x:Array x:Key="ResamplingPrecision" Type="local:ResamplingPrecision">
<local:ResamplingPrecision>Default</local:ResamplingPrecision>
<local:ResamplingPrecision>Precision2x</local:ResamplingPrecision>
<local:ResamplingPrecision>Precision4x</local:ResamplingPrecision>
</x:Array>
</UserControl.Resources>
<Grid Grid.IsSharedSizeScope="True" IsEnabled="{Binding IsLoading, Converter={StaticResource InvertBooleanConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Toolbar -->
<Border Grid.RowSpan="3" Background="#FF232426" BorderThickness="0,0,2,0" BorderBrush="#323539" Canvas.ZIndex="9999">
<StackPanel Orientation="Vertical">
<ToggleButton x:Name="IsZoomEnabled"
IsChecked="True"
Style="{StaticResource ZoomButtonStyle}"
ToolTipService.ToolTip="Toggle Rubber-Band Zoom"
s:ToggleButtonExtensions.GroupName="ToogleModifiersGroup" />
<ToggleButton x:Name="IsPanEnabled"
Style="{StaticResource PanButtonStyle}"
ToolTipService.ToolTip="Toggle Zoom-Pan"
s:ToggleButtonExtensions.GroupName="ToogleModifiersGroup" />
<ext:FlyoutSeparator />
<ext:FlyoutMenuButton Content="CT" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock Text="Channel Type: "/>
<ComboBox Margin="5"
ItemsSource="{StaticResource ChannelTypeSource}"
SelectedItem="{Binding SelectedChannelType}"/>
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
<Button Content="+"
Padding="0"
FontSize="18"
ToolTipService.ToolTip="Add New Channel"
Style="{StaticResource DefaultButtonStyle}"
Command="{Binding AddChannelCommand}"/>
<ext:FlyoutSeparator/>
<ext:FlyoutMenuButton Content="PC" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock Text="Channel Count:"/>
<ComboBox Margin="5"
SelectedItem="{Binding SelectedChannelCount}"
ItemsSource="{StaticResource ChannelCountSource}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="Point Count:"/>
<ComboBox Margin="5"
SelectedItem="{Binding SelectedPointCount}"
ItemsSource="{StaticResource PointCountSource}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource PointCountToLabelConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
<ext:FlyoutMenuButton Content="RS" Padding="0">
<ext:FlyoutMenuButton.PopupContent>
<StackPanel Orientation="Vertical" MinWidth="150">
<TextBlock Text="Line Stroke Thickness:"/>
<ComboBox Margin="5"
SelectedItem="{Binding SelectedStrokeThickness}"
ItemsSource="{StaticResource StrokeThicknesses}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="Resampling Precision:"/>
<ComboBox Margin="5"
SelectedItem="{Binding SelectedResamplingPrecision}"
ItemsSource="{StaticResource ResamplingPrecision}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</ext:FlyoutMenuButton.PopupContent>
</ext:FlyoutMenuButton>
<Button Content="LOAD"
Padding="0"
FontSize="9"
ToolTipService.ToolTip="Load Channels"
Style="{StaticResource DefaultButtonStyle}"
Command="{Binding LoadChannelsCommand}"/>
</StackPanel>
</Border>
<!-- Info -->
<StackPanel Grid.Row="0" Grid.Column="1" Focusable="False" Height="30" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Margin="5,0" Foreground="White" FontWeight="SemiBold">
<Run Text="Channels:" Foreground="Gray"/>
<Run Text="{Binding ChannelViewModels.Count, Mode=OneWay}"/>
</TextBlock>
<TextBlock VerticalAlignment="Center" Margin="5,0" Foreground="White" FontWeight="SemiBold">
<Run Text="Channel Points:" Foreground="Gray"/>
<Run Text="{Binding SelectedPointCount, Mode=OneWay, Converter={StaticResource PointCountToLabelConverter}}"/>
</TextBlock>
<TextBlock VerticalAlignment="Center" Margin="5,0" Foreground="White" FontWeight="SemiBold">
<Run Text="Total Points:" Foreground="Gray"/>
<Run Text="{Binding TotalPoints, Mode=OneWay, Converter={StaticResource PointCountToLabelConverter}}"/>
</TextBlock>
</StackPanel>
<!-- BottomAxis -->
<Grid Grid.Row="2" Grid.Column="1" Visibility="{Binding IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter='INVERSE'}">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ChannelNames"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="#1C1C1E"/>
<s:SciChartSurface Grid.Column="1">
<s:SciChartSurface.XAxis>
<s:NumericAxis Height="30"
AxisAlignment="Bottom"
VisibleRange="{Binding XRange, Mode=TwoWay}"
LabelProvider="{StaticResource TimeLabelProvider}"
MajorTickLineStyle="{StaticResource TimeAxisMajorTickLineStyle}"
MinorTickLineStyle="{StaticResource TimeAxisMinorTickLineStyle}"/>
</s:SciChartSurface.XAxis>
<s:SciChartSurface.YAxis>
<s:NumericAxis Visibility="Collapsed"/>
</s:SciChartSurface.YAxis>
</s:SciChartSurface>
</Grid>
<!-- Channels -->
<Border Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="1"
BorderThickness="0,1"
BorderBrush="#2D2C32"
Margin="0,0,0,30">
<ScrollViewer Background="#1C1C1E"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<b:Interaction.Behaviors>
<common:DigitalAnalyzerScrollBehavior ChannelHeightDelta="10" ChangeChannelHeightCommand="{Binding ChangeChannelHeightCommand}"/>
</b:Interaction.Behaviors>
<ItemsControl x:Name="chartItemsControl" ItemsSource="{Binding ChannelViewModels}">
<b:Interaction.Behaviors>
<common:FocusedChannelScrollBehavior ScrollToFocusedChannel="False"/>
</b:Interaction.Behaviors>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ChannelViewModel}">
<Grid Background="#2D2C32" Height="{Binding ChannelHeight, Mode=OneWay}" Focusable="False" UseLayoutRounding="False" >
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ChannelNames" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,0,1" BorderBrush="#1C1C1E">
<DockPanel>
<Border DockPanel.Dock="Left"
Background="{Binding ChannelColor, Mode=OneWay}"
Width="5"/>
<TextBlock DockPanel.Dock="Left"
Margin="10,5"
VerticalAlignment="Center"
Foreground="White"
Text="{Binding ChannelName}"/>
</DockPanel>
</Border>
<s:SciChartSurface x:Name="channelSurface" Grid.Column="1"
RenderableSeries="{Binding RenderableSeries}">
<!-- Create an X Axis with GrowBy -->
<s:SciChartSurface.XAxis>
<s:NumericAxis Style="{StaticResource HiddenAxisStyle}"
VisibleRangeLimitMode="Min"
VisibleRangeLimit="0,0"
VisibleRange="{Binding DataContext.XRange, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=ItemsControl, AncestorLevel=2}}"/>
</s:SciChartSurface.XAxis>
<!-- Create a Y Axis with GrowBy. Optional bands give a cool look and feel for minimal performance impact -->
<s:SciChartSurface.YAxis>
<s:NumericAxis Style="{StaticResource HiddenAxisStyle}"
VisibleRange="{Binding YRange, Mode=OneWay}"/>
</s:SciChartSurface.YAxis>
<s:SciChartSurface.ChartModifier>
<s:ModifierGroup s:MouseManager.MouseEventGroup="ChannelModifierGroup">
<s:RubberBandXyZoomModifier IsAnimated="False" IsXAxisOnly="True" ZoomExtentsY="False" ReceiveHandledEvents="True" IsEnabled="{Binding IsChecked, Mode=OneWay, ElementName=IsZoomEnabled}"/>
<s:ZoomPanModifier ZoomExtentsY="False" XyDirection="XDirection" IsEnabled="{Binding IsChecked, Mode=OneWay, ElementName=IsPanEnabled}"/>
<s:ZoomExtentsModifier XyDirection="XDirection" IsAnimated="False" />
<s:MouseWheelZoomModifier XyDirection="XDirection" />
</s:ModifierGroup>
</s:SciChartSurface.ChartModifier>
</s:SciChartSurface>
<Border Grid.Column="1"
BorderThickness="0,0,0,1"
BorderBrush="#2D2C32"
VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
<TextBlock
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
Text="There is no data to display"
FontSize="28"
FontWeight="Bold"
Foreground="LightGray"
Opacity="0.6"
Visibility="{Binding IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<!--Busy Indicator-->
<Border Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
Background="#FF1C1C1E"
Visibility="{Binding IsLoading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<ProgressBar Background="Transparent" BorderThickness="0" HorizontalAlignment="Center" IsIndeterminate="True" Height="10" Width="{Binding ActualWidth, Mode=OneWay, ElementName=BusyMessage}"/>
<TextBlock x:Name="BusyMessage" Text="Generating data points..." FontSize="16" Foreground="#FFF" Margin="0,10" TextAlignment="Center" Opacity="0.7"/>
</StackPanel>
</Border>
</Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using SciChart.Charting.Common.Helpers;
using SciChart.Data.Model;
using SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer.Common;
using SciChart.Examples.ExternalDependencies.Common;
namespace SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer
{
/// <summary>
/// Additional enum passed to ChannelViewModel: Resampling precisions
///
/// see https://www.scichart.com/documentation/win/current/webframe.html#The%20Extreme%20Resamplers%20API.html for info
/// on what the numeric values mean. Default is 0
/// </summary>
public enum ResamplingPrecision
{
Default = 0,
Precision2x = 1,
Precision4x = 2,
}
public class DigitalAnalyzerExampleViewModel : BaseViewModel
{
private bool _isLoading;
private DoubleRange _xRange;
public DigitalAnalyzerExampleViewModel()
{
ChannelViewModels = new ObservableCollection<ChannelViewModel>();
SelectedChannelType = "Digital";
SelectedChannelCount = 32;
SelectedPointCount = 1000000;
SelectedResamplingPrecision =ResamplingPrecision.Default;
SelectedStrokeThickness = 1;
ChangeChannelHeightCommand = new ActionCommand<object>((d) =>
{
var delta = (double)d;
foreach (var channelViewModel in ChannelViewModels)
{
channelViewModel.SetChannelHeightDelta(delta);
}
});
AddChannelCommand = new ActionCommand(async () =>
{
IsLoading = true;
var isDigital = SelectedChannelType == "Digital";
await AddChannels(isDigital ? 1 : 0, isDigital ? 0 : 1);
IsLoading = false;
});
LoadChannelsCommand = new ActionCommand(async () =>
{
IsLoading = true;
// Clear ViewModels
foreach (var channelVm in ChannelViewModels)
{
channelVm.Clear();
}
ChannelViewModels.Clear();
XRange = null;
// Create a bunch of Digital channels
await AddChannels(SelectedChannelCount, 0);
XRange = new DoubleRange(0, SelectedPointCount);
IsLoading = false;
});
LoadChannelsCommand.Execute(null);
}
private async Task AddChannels(int digitalChannelsCount, int analogChannelsCount)
{
List<byte[]> digitalChannels = new List<byte[]>();
List<float[]> analogChannels = new List<float[]>();
// Make sure we allow UI to show progress bar
await Task.Delay(200);
// Force GC to free memory
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
try
{
// Allocate memory first
await Task.Run(() =>
{
for (var i = 0; i < digitalChannelsCount; i++)
{
var newArray = new byte[SelectedPointCount];
digitalChannels.Add(newArray);
}
for (var i = 0; i < analogChannelsCount; i++)
{
var newArray = new float[SelectedPointCount];
analogChannels.Add(newArray);
}
});
// Generate random data and fill channels
await GenerateData(digitalChannels, analogChannels);
}
catch (OutOfMemoryException)
{
await Application.Current.Dispatcher.BeginInvoke(new Action(() => ChannelViewModels.Clear()));
MessageBox.Show(string.Format($"There is not enough RAM memory to allocate {SelectedChannelCount} channels with {SelectedPointCount} points each. Please select less channels or less points and try again."));
}
finally
{
OnPropertyChanged(nameof(IsEmpty));
OnPropertyChanged(nameof(ChannelViewModels));
OnPropertyChanged(nameof(SelectedPointCount));
OnPropertyChanged(nameof(TotalPoints));
OnPropertyChanged(nameof(XRange));
IsLoading = false;
}
}
private async Task GenerateData(List<byte[]> digitalChannels, List<float[]> analogChannels)
{
var digitalChannelsCount = digitalChannels.Count;
var analogChannelsCount = analogChannels.Count;
var totalChannelCount = digitalChannelsCount + analogChannelsCount;
var channelList = new List<ChannelViewModel>(totalChannelCount);
var channelIndex = ChannelViewModels.Count;
await Task.Run(async () =>
{
var xStart = 0d;
var xStep = 1d;
var digital = new List<Task<ChannelViewModel>>(digitalChannelsCount);
var analog = new List<Task<ChannelViewModel>>(analogChannelsCount);
foreach (var channel in digitalChannels)
{
var id = channelIndex++;
digital.Add(Task.Run(() => ChannelGenerationHelper.Instance.GenerateDigitalChannel(xStart, xStep, channel, id)));
}
foreach (var channel in analogChannels)
{
var id = channelIndex++;
analog.Add(Task.Run(() => ChannelGenerationHelper.Instance.GenerateAnalogChannel(xStart, xStep, channel, id)));
}
await Task.WhenAll(digital.Union(analog));
foreach (var p in digital.Union(analog))
{
channelList.Add(p.Result);
}
});
channelList.ForEach(ch =>
{
ch.StrokeThickness = this.SelectedStrokeThickness;
ch.ResamplingPrecision = (uint)this.SelectedResamplingPrecision;
ChannelViewModels.Add(ch);
});
}
public ObservableCollection<ChannelViewModel> ChannelViewModels { get; private set; }
public string SelectedChannelType { get; set; }
public int SelectedPointCount { get; set; }
/// <summary>
/// Additional property passed to ChannelViewModel: strokeThickness of the line (Default 1)
/// </summary>
public int SelectedStrokeThickness { get; set; }
/// <summary>
/// Additional property passed to ChannelViewModel: Resampling precisions
///
/// see https://www.scichart.com/documentation/win/current/webframe.html#The%20Extreme%20Resamplers%20API.html for info
/// on what the numeric values mean. Default is 0
/// </summary>
public ResamplingPrecision SelectedResamplingPrecision { get; set; }
public int SelectedChannelCount { get; set; }
public ActionCommand<object> ChangeChannelHeightCommand { get; }
public ActionCommand AddChannelCommand { get; }
public ActionCommand LoadChannelsCommand { get; }
public long TotalPoints => ChannelViewModels.Sum(c => (long)c.DataCount);
public bool IsLoading
{
get => _isLoading;
set
{
_isLoading = value;
OnPropertyChanged(nameof(IsLoading));
}
}
public bool IsEmpty => ChannelViewModels.Count <= 0;
public DoubleRange XRange
{
get => _xRange;
set
{
_xRange = value;
OnPropertyChanged(nameof(XRange));
}
}
}
}
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Media;
using SciChart.Charting.Model.DataSeries;
using SciChart.Charting.Visuals.RenderableSeries;
using SciChart.Data.Model;
using SciChart.Data.Numerics;
using SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer.Common;
namespace SciChart.Examples.Examples.PerformanceDemos2D.DigitalAnalyzer
{
public class ChannelViewModel : BindableObject
{
private Lazy<ObservableCollection<IRenderableSeries>> _renderableSeries;
private readonly IDataSeries _dataSeries;
private bool _isDigital;
private DoubleRange _yRange;
private string _channelName;
private readonly int _channelIndex;
private double _channelHeight;
private uint _resamplingPrecision;
private int _strokeThickness;
public ChannelViewModel(IDataSeries dataSeries, DoubleRange yRange, int channelIndex, string channelName)
{
_dataSeries = dataSeries;
_channelIndex = channelIndex;
YRange = yRange;
ChannelName = channelName;
ChannelHeight = 60;
_renderableSeries = new Lazy<ObservableCollection<IRenderableSeries>>(() =>
{
var coll = new ObservableCollection<IRenderableSeries>
{
new FastLineRenderableSeries
{
DataSeries = _dataSeries,
StrokeThickness = _strokeThickness,
Stroke = ColorHelper.GetDimColor(_channelIndex, 0.8),
IsDigitalLine = _isDigital,
ResamplingPrecision = _resamplingPrecision,
}
};
return coll;
});
}
public int DataCount => _dataSeries.Count;
public bool IsDigital
{
get => _isDigital;
set
{
_isDigital = value;
if (_renderableSeries?.IsValueCreated == true && _renderableSeries?.Value.Any() == true)
{
_renderableSeries.Value[0].IsDigitalLine = _isDigital;
}
}
}
public int StrokeThickness
{
get => _strokeThickness;
set
{
_strokeThickness = value;
if (_renderableSeries?.IsValueCreated == true && _renderableSeries?.Value.Any() == true)
{
_renderableSeries.Value[0].StrokeThickness = _strokeThickness;
}
}
}
public uint ResamplingPrecision
{
get => _resamplingPrecision;
set
{
_resamplingPrecision = value;
if (_renderableSeries?.IsValueCreated == true && _renderableSeries?.Value.Any() == true)
{
_renderableSeries.Value[0].ResamplingPrecision = _resamplingPrecision;
}
}
}
public void Clear()
{
foreach(var rs in RenderableSeries)
{
rs.DataSeries.Clear();
rs.DataSeries = null;
}
_renderableSeries = null;
OnPropertyChanged(nameof(RenderableSeries));
}
public double ChannelHeight
{
get => _channelHeight;
set
{
if (_channelHeight.CompareTo(value) == 0) return;
_channelHeight = value;
OnPropertyChanged(nameof(ChannelHeight));
}
}
public Brush ChannelColor => RenderableSeries.Any() ? new SolidColorBrush(RenderableSeries[0].Stroke) : Brushes.Transparent;
public ObservableCollection<IRenderableSeries> RenderableSeries => _renderableSeries?.Value;
public DoubleRange YRange
{
get => _yRange;
set
{
_yRange = value;
OnPropertyChanged(nameof(YRange));
}
}
public string ChannelName
{
get => _channelName;
set
{
_channelName = value;
OnPropertyChanged(nameof(ChannelName));
}
}
public void SetChannelHeightDelta(double delta)
{
var newHeight = ChannelHeight + delta;
if (newHeight >= 30 && newHeight <= 60)
{
ChannelHeight = newHeight;
}
}
}
}