08-14-2017 04:17 AM
Hello.
I am using NI Graphs in my WPF application. The graphs are binded to ChartCollection<DateTime, double>. The application is appending every 1s the new point to the chart collection. The graphs are automatically refreshing and showing curves, but after some period (seems it is random period) the application sometimes stop with following exception:
A first chance exception of type 'System.ObjectDisposedException' occurred in NationalInstruments.Common.dll
An unhandled exception of type 'System.ObjectDisposedException' occurred in NationalInstruments.Common.dll
Additional information: The data store was modified.
Here is graph XAML code:
<Grid Grid.Column="2" Grid.Row="0" Grid.RowSpan="{Binding Path=testVoltageDropMeasurement, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, Converter={StaticResource b2num2}}">
<ni:Graph x:Name="graph_temperatures" PreferIndexData="False" RenderMode="Raster">
<ni:Graph.Axes>
<ni:AxisDateTime Orientation="Horizontal"/>
<ni:AxisDouble Label="Temperature [°C]" Orientation="Vertical" Range="0, 100, System.Double"/>
</ni:Graph.Axes>
</ni:Graph>
</Grid>
Binding to ChartColletion is done programmatically only ones in the software:
// update graph data sources according to assigned channels
graph_temperatures.Data.Clear(); // clear all data
graph_temperatures.Plots.Clear(); // clear all plots
if ((this.Test.temperatures != null) && (this.Test.temperatures.Count > 0))
{
for (int i = 0; i < this.Test.temperatures.Count; i++) // add plots for assigned channels
{
graph_temperatures.Data[i] = this.Test.temperatures[i].values;
Plot p = new Plot();
LinePlotRenderer lpr = new LinePlotRenderer();
lpr.Stroke = (Brush)(new BrushConverter()).ConvertFromString(this.Test.temperatures[i].channelColor);
p.Renderer = lpr;
p.DataContext = this.Test.temperatures[i];
Binding b = new Binding()
{
Path = new PropertyPath("showed"),
Source = this.Test.temperatures[i],
Converter = new BooleanToVisibilityConverter()
};
p.SetBinding(Plot.VisibilityProperty, b);
graph_temperatures.Plots.Add(p);
}
}
See graphs in application:
When I disable drawing of all channels (all show checkboxes are unchecked) the application is running well without any exception. I think the problem have to be in graphs controls.
Could you help me please to solve it?
Thank you for support.
Solved! Go to Solution.
08-14-2017 09:59 AM
Based on the description and that it is a first-chance exception, I expect this is an issue with the chart being modified while the graph is processing a previous append in the background. We would need to see the full call stack to confirm but, assuming this is the case, the graph should handle this exception (and, if it occurs repeatedly, should automatically switch away from background data processing).
For chart data, it does make sense to configure the graph to use immediate data processing in the first place. To do this, set the "immediate updates" attached property.
In XAML:
xmlns:ni="http://schemas.ni.com/controls/2009/xaml/presentation" xmlns:niPrimitives="http://schemas.ni.com/controls/2009/xaml/presentation/primitives" ... <ni:Graph x:Name="graph_temperatures" niPrimitives:GraphConfiguration.ImmediateUpdates="True" ...
Or in code:
GraphConfiguration.SetImmediateUpdates( graph_temperatures, true );
08-15-2017 01:11 AM
Hello Paul. Thank you for answer. Unfortunately I couldn't find this function, see following print-screens:
There is usable only function for set draw visual cache size.
Jakub T.
08-15-2017 09:48 AM
Sorry, my mistake: that functionality is not yet available in the current version of Measurement Studio.
Seeing the call stack from the exception would still be helpful in diagnosing the issue. Are you trying to modify the chart collection outside of the UI thread, like this question: Can't get Graph.DataSource to work in WPF i (threading issue?) ?
08-16-2017 04:27 AM
Yes, the chart collection is updated every 1s from another measuring thread. I supposed that the advantage of WPF controls is to be bound to datasource which can be updated independently. How the NI graph knows that the data was changed? Becasue I have chart collection defined as a following property which calls this property change event when changed, see:
private ChartCollection<DateTime, double> _values = new ChartCollection<DateTime, double>();
public ChartCollection<DateTime, double> values
{
get { return _values; }
set
{
if (_values != value)
{
_values = value;
OnPropertyChanged("values");
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
So you suggest me to update data in UI thread or call update from the delegate?
Thank you for support.
Jakub
08-16-2017 09:13 AM
So you suggest me to update data in UI thread or call update from the delegate?
Yes, the chart collection types are not thread-safe, so they must be updated from the UI thread to guarantee correctness. Any of the approaches from the other question would work.
How the NI graph knows that the data was changed?
Ultimately, the graph ends up listening to the DataChanged event, which lets it know and update automatically when values inside of the collection have changed.
08-17-2017 01:52 AM
Hello Paul.
Solved according your suggestions.
Thank you.
BTW, when do you plan to release the WPF graph controls with the new feature (immediate updates)? Does it solve this issue?
Jakub T.
08-17-2017 11:40 AM
First, I am afraid I do not know any details about the timing of the next Measurement Studio release.
The "immediate updates" setting is actually an option to return the graph to the UI-thread processing model that exists right now. (The new feature is for background-thread data processing, which can improve UI performance, but can also introduce more opportunities for cross-thread data invalidation.)
To give a little more background on the underlying issue, the disposed exception occurs because of an optimization made by the chart collection: when the graph requests access to the chart data, the chart returns a reference to the live data in the collection. This avoids a memory copy, but means that any change to the chart can invalidate the data held by the graph.
We have considered adding "thread-safe Append" methods to chart collections, but this would only end up doing the "dispatch to the UI thread" logic behind the scenes. In other words, to get this added convenience would mean a more complicated API (with limited opportunities for optimization), and it would end up hiding the inherent race conditions involved (ordering updates from multiple threads). Since the application is already performing multi-threaded operations, our conclusion so far has been that performing thread-safe chart updates is best left to that application.
08-17-2017 03:29 PM
OK, I understand. Thank you very much for the explanation and for the support.
Jakub
08-17-2017 03:30 PM
Happy to help!