Sam Hicks
Sam Hicks

Reputation: 33

Binding points to chart from Interactive Data Display WPF library

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

Answers (1)

Morten
Morten

Reputation: 11

In the Plot base class, there is already a dependency property registered for the Pointsproperty. As a 'quick-fix', I added this to the LineGraphclass:

    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

Related Questions