Reputation: 33
I want plot oscilloscope -like dynamic line chart in WPF and I found this library: Interactive Data Display which emerged from this library: D3 Dynamic Data Display. The advantage is that its light which is important for me.
From sample program, I can see that they do not bind the LineGraph.Points with any collection, and when I tried that it did not work, there is also no Refresh or Update method on the Graph object. Currently, I'm forced to use LineGraph.PlotY()
method every time I want to update my graph.
Does anyone know if it's possible to use this library in MVVM way?
Sample code:
double[] x = new double[200];
for (int i = 0; i < x.Length; i++)
x[i] = 3.1415 * i / (x.Length - 1);
for (int i = 0; i < 25; i++)
{
var lg = new LineGraph();
lines.Children.Add(lg);
lg.Stroke = new SolidColorBrush(Color.FromArgb(255, 0, (byte)(i * 10), 0));
lg.Description = String.Format("Data series {0}", i + 1);
lg.StrokeThickness = 2;
lg.Plot(x, x.Select(v => Math.Sin(v + i / 10.0)).ToArray());
}
XAML:
<d3:Chart Name="plotter">
<d3:Chart.Title>
<TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">Line graph legend sample</TextBlock>
</d3:Chart.Title>
<d3:Chart.LegendContent>
<d3:LegendItemsPanel>
<d3:LegendItemsPanel.Resources>
<DataTemplate x:Key="InteractiveDataDisplay.WPF.LineGraph">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=Visibility, Converter={StaticResource VisibilityToCheckedConverter}, Mode=TwoWay}"/>
<Line Width="15" Height="15" X1="0" Y1="0" X2="15" Y2="15" Stroke="{Binding Path=Stroke}" StrokeThickness="2"/>
<TextBlock Margin="5,0,0,0" Text="{Binding Path=Description}"/>
</StackPanel>
</DataTemplate>
</d3:LegendItemsPanel.Resources>
</d3:LegendItemsPanel>
</d3:Chart.LegendContent>
<Grid Name="lines"/>
</d3:Chart>
Upvotes: 1
Views: 3348
Reputation: 11
In the Plot
base class, there is already a dependency property registered for the Points
property. As a 'quick-fix', I added this to the LineGraph
class:
public ObservableCollection<Point> ObservablePoints
{
get { return (ObservableCollection<Point>)GetValue(ObservablePointsProperty); }
set { SetValue(ObservablePointsProperty, value); }
}
public static readonly DependencyProperty ObservablePointsProperty =
DependencyProperty.RegisterAttached(
"ObservablePoints",
typeof(ObservableCollection<Point>),
typeof(LineGraph),
new PropertyMetadata(
new ObservableCollection<Point>(),
(d, e) =>
{
var linePlot = (LineGraph)d;
var updateAction = new NotifyCollectionChangedEventHandler(
(o, args) =>
{
if (linePlot != null)
{
InteractiveDataDisplay.WPF.Plot.SetPoints(linePlot.polyline, new PointCollection((ObservableCollection<Point>)o));
}
});
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
coll.CollectionChanged -= updateAction;
}
if (e.NewValue != null)
{
var coll = (INotifyCollectionChanged)e.NewValue;
coll.CollectionChanged += updateAction;
if (linePlot != null)
{
InteractiveDataDisplay.WPF.Plot.SetPoints(linePlot.polyline, new PointCollection((ObservableCollection<Point>)e.NewValue));
}
}
}));
Then, bound my collection of points to the ObservablePoints
property:
<d3:LineGraph Description="MyGraph"
ObservablePoints="{Binding Path=PointsFromMyDatamodel}"/>
The drawback is that the graph for all points is redrawn - thus the 'quick-fix'. Redrawing only added, modified or removed points would require more changes to underlying base classes...
Upvotes: 1