Jump to content
Software FX Community

Chart Zoom throwing error when ObserverableCollection modified


kelias

Recommended Posts

I have a chart that I want to appear to scroll so it only shows a certain number of data points. I have the chart bound to an ObservableCollection, so when I get an item to add to the collection I remove the first item from the collection. This works fine except with Zoom. When I zoom and data comes in the chart throws a NullReferenceException. (I have the full stacktrace at the bottom)

Here is some code to reproduce the problem:


using System;

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Windows;

using ChartFX.WPF;

namespace ChartTest3

{

public partial class Window1 : Window

{

private readonly ObservableCollection<MyObject> col = new ObservableCollection<MyObject>();

public Window1()

{

InitializeComponent();

for (int i = 0; i < 10; i++)

{

col.Add(new MyObject {EffectiveDate = DateTime.Now.AddMinutes(i), Value1 = 1000, Value2 = 700});

}

myChart.Series.Clear();

myChart.AxesY.Clear();

Axis a = new Axis();

a.AutoScale = true;

a.ForceZero = false;

myChart.AxesY.Add(a);

myChart.AxisX.Labels.Format = AxisFormat.DateTime;

SeriesAttributes series1 = new SeriesAttributes("Value1");

series1.BindingPathX = "EffectiveDate";

series1.Gallery = Gallery.Line;

series1.AxisY = a;

myChart.Series.Add(series1);

series1.ItemsSource = col;

SeriesAttributes series2 = new SeriesAttributes("Value2");

series2.BindingPathX = "EffectiveDate";

series2.Gallery = Gallery.Line;

series2.AxisY = a;

myChart.Series.Add(series2);

series2.ItemsSource = col;

}

private void button1_Click(object sender, RoutedEventArgs e)

{

col.Add(new MyObject {EffectiveDate = col[col.Count - 1].EffectiveDate.AddMinutes(1), Value1 = 1000, Value2 = 700});

col.RemoveAt(0);

}

private void button2_Click(object sender, RoutedEventArgs e)

{

myChart.Zoom.Mode = ZoomMode.Selection;

}

}

public class MyObject : INotifyPropertyChanged

{

private DateTime effectiveDate;

private decimal value1;

private decimal value2;

public DateTime EffectiveDate

{

get { return effectiveDate; }

set

{

effectiveDate = value;

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("EffectiveDate"));

}

}

public decimal Value1

{

get { return value1; }

set

{

value1 = value;

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Value1"));

}

}

public decimal Value2

{

get { return value2; }

set

{

value2 = value;

if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Value2"));

}

}

public event PropertyChangedEventHandler PropertyChanged;

}

}

And the XAML:


<Window x:Class="ChartTest3.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:my="clr-namespace:ChartFX.WPF;assembly=ChartFX.WPF"

xmlns:Motifs="clr-namespace:ChartFX.WPF.Motifs;assembly=ChartFX.WPF"

Title="Window1" Height="390" Width="675">

<Grid>

<my:Chart x:Name="myChart" Gallery="Line" UseEffects="True" UseVisuals="True" Background="White" BorderThickness="0" Style="{x:Static Motifs:Basic.Style}" Palette="{x:Static my:Palettes.Basic}" Margin="0,4,0,79" >

<my:Chart.Titles>

<my:Title Content="Test"/>

</my:Chart.Titles>

</my:Chart>

<Button Height="23" HorizontalAlignment="Left" Margin="68,0,0,8" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click">Button</Button>

<Button Height="23" HorizontalAlignment="Left" Margin="188,0,0,12" Name="button2" VerticalAlignment="Bottom" Width="75" Click="button2_Click">Button</Button>

</Grid>

</Window>

Click the right button to put the chart into ZoomMode.Selection. Then select a range. Now that the range is selected click the left button to add a point to the ObservableCollection and remove one. Click this a few times and you will get the NullReferenceException.

Here is the full error I get:


System.NullReferenceException was unhandled

Message="Object reference not set to an instance of an object."

Source="ChartFX.WPF"

StackTrace:

at ChartFX.WPF.SeriesAttributes.ChartFX.WPF.Internal.IDataTarget.ReadData(IEnumerable data, Int32 from, Int32 count, NotifyCollectionChangedAction action, DataBindingHelper bindingHelper)

at ChartFX.WPF.Internal.DataBindingHelper.a(Object A_0, IEnumerable A_1, Int32 A_2, Int32 A_3, NotifyCollectionChangedAction A_4)

at ChartFX.WPF.Internal.DataBindingHelper.a(Object A_0, NotifyCollectionChangedEventArgs A_1)

at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)

at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)

at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)

at System.Collections.ObjectModel.Collection`1.Add(T item)

at ChartTest3.Window1.button1_Click(Object sender, RoutedEventArgs e) in C:\dev\ChartTest3\ChartTest3\Window1.xaml.cs:line 49

at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)

at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)

at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)

at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)

at System.Windows.Controls.Primitives.ButtonBase.OnClick()

at System.Windows.Controls.Button.OnClick()

at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)

at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)

at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)

at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)

at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)

at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)

at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)

at System.Windows.UIElement.CrackMouseButtonEventAndReRaiseEvent(DependencyObject sender, MouseButtonEventArgs e)

at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)

at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)

at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)

at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)

at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)

at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)

at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)

at System.Windows.Input.InputManager.ProcessStagingArea()

at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)

at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)

at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)

at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)

at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)

at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)

at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)

at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)

at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)

at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)

at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)

at System.Windows.Threading.Dispatcher.Run()

at System.Windows.Application.RunDispatcher(Object ignore)

at System.Windows.Application.RunInternal(Window window)

at System.Windows.Application.Run(Window window)

at System.Windows.Application.Run()

at ChartTest3.App.Main() in C:\dev\ChartTest3\ChartTest3\obj\Debug\App.g.cs:line 0

at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)

at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

at System.Threading.ThreadHelper.ThreadStart_Context(Object state)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

at System.Threading.ThreadHelper.ThreadStart()

InnerException:

Any ideas what I can do to get around this problem? Preferably I would like the zoom to stay in the current position, so if the user sets the zoom to show the last 10 items I would like it to show the last 10 items always. So if I add an item it would show in the zoom and remove one off the left side.

Link to comment
Share on other sites

We are actively working on this issue, I apologize for the lack of confirmation. As soon as we have a hotfix available we will respond to this thread.

I noticed in your code you are binding each series to your ObservableCollection even though both series point to the same collection (different fields), how many collections do you have in your real-world app? I ask this because we think we have already fixed the scenario when you use Chart.ItemsSource instead of Series.ItemsSource (which obviously only work if your app only uses 1 collection).

Also will your points be equally spaced in the X axis?

JuanC

Link to comment
Share on other sites

If you use the example above and bind the collection to the chart instead of the series it doesn't seem to resize correctly. When an item is added it refreshes properly but when I remove an item the chart does not scale the x axis correctly reclaiming the space. But it does work if you assign it to each series instead. (Let me know if I'm missing something here.)

Our charts use the X axis as a time, and the datapoints are not equally spaced.

Link to comment
Share on other sites

We have fixed most of these issues in build 3671 available as a hotfix.

I noticed in your sample app you are adding a point and removing another one, in ChartFX 8.1 we will support a real time API that would make this scenario perform better, note that in the current build we will "re-render" all the points when a point is added/removed. In 8.1 if you use the realtime API we will only render the new value.

Also note that we are more efficient when processing changes in an ObservableCollection that is bound to the chart instead of a specific series. The reclaiming space issue you mentioned in your last post was also fixed.

JuanC

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...