12
Vote

Chart throws exception when bound to an empty datasource and then to a populated datasource

description

When I create a chart and set it's DataContext it to an empty collection, then set its DataContext to a new collection that has data, the program throws the following exception: Cannot modify the logical children for this node at this time because a tree walk is in progress.   However, if I set the DataContext to a collection that has data and then to an empty collection and then to a collection that has data, it works fine.   This occurs in WPF. I haven't tested it on Silverlight.   If you need more info, you can email or IM my MS alias (t-sturne)   Code Sample:   XAML: <charting:Chart Grid.Row="0" Title="Title" LegendTitle="Legend" Name="Chart1" Grid.RowSpan="2"> <charting:AreaSeries ItemsSource="{Binding}"   DependentValuePath="Value"   IndependentValuePath="Key"   Background="Red" />     </charting:Chart>   C#: int runCount = 0; private void bindChart(string searchString) { List<KeyValuePair<DateTime, int>> dataEmpty = new List<KeyValuePair<DateTime, int>>();   List<KeyValuePair<DateTime, int>> dataFilled = new List<KeyValuePair<DateTime, int>>(); dataFilled.Add(new KeyValuePair<DateTime, int>(DateTime.Today, 1)); if (runCount == 0) { Chart1.DataContext= dataEmpty; } else { Chart1.DataContext = dataFilled; } runCount++; }           Stack Trace: System.Reflection.TargetInvocationException was unhandled Message="Exception has been thrown by the target of an invocation." Source="mscorlib" StackTrace: at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Delegate.DynamicInvokeImpl(Object[] args) 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.DispatcherOperation.InvokeImpl() at System.Threading.ExecutionContext.runTryCode(Object userData) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Windows.Threading.DispatcherOperation.Invoke() at System.Windows.Threading.Dispatcher.ProcessQueue() at System.Windows.Threading.Dispatcher.WndProcHook(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.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter) 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.TranslateAndDispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Application.RunInternal(Window window) at NewsCluesWpf.App.Main() in C:\SoftwareInstall\VSProjects\NewsClues\NewsCluesWpf\obj\Debug\App.g.cs:line 0 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at System.Runtime.Hosting.ApplicationActivator.CreateInstance(ActivationContext activationContext, String[] activationCustomData) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssemblyDebugInZone() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: System.InvalidOperationException Message="Cannot modify the logical children for this node at this time because a tree walk is in progress." Source="PresentationFramework" StackTrace: at System.Windows.FrameworkElement.AddLogicalChild(Object child) at System.Windows.Controls.UIElementCollection.InsertInternal(Int32 index, UIElement element) at System.Windows.Controls.DataVisualization.ObservableCollectionListAdapter`1.<>c__DisplayClass1.<OnCollectionChanged>b__0(T item, Int32 index) at System.Windows.Controls.DataVisualization.EnumerableFunctions.ForEachWithIndex[T](IEnumerable`1 that, Action`2 action) at System.Windows.Controls.DataVisualization.ObservableCollectionListAdapter`1.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index) at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item) at System.Windows.Controls.DataVisualization.ReadOnlyObservableCollection`1.InsertItem(Int32 index, T item) at System.Collections.ObjectModel.Collection`1.Insert(Int32 index, T item) at System.Windows.Controls.DataVisualization.AggregatedObservableCollection`1.<>c__DisplayClass14.<>c__DisplayClass16.<ChildCollectionCollectionChanged>b__f(ReadOnlyObservableCollection`1 that) at System.Windows.Controls.DataVisualization.ReadOnlyObservableCollection`1.Mutate(Action`1 action) at System.Windows.Controls.DataVisualization.AggregatedObservableCollection`1.<>c__DisplayClass14.<ChildCollectionCollectionChanged>b__e(T item, Int32 index) at System.Windows.Controls.DataVisualization.EnumerableFunctions.ForEachWithIndex[T](IEnumerable`1 that, Action`2 action) at System.Windows.Controls.DataVisualization.AggregatedObservableCollection`1.ChildCollectionCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index) at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item) at System.Collections.ObjectModel.Collection`1.Add(T item) at System.Windows.Controls.DataVisualization.Charting.Chart.AddAxisToChartArea(Axis axis) at System.Windows.Controls.DataVisualization.Charting.Chart.ActualAxesCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) 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.OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index) at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item) at System.Windows.Controls.DataVisualization.UniqueObservableCollection`1.InsertItem(Int32 index, T item) at System.Collections.ObjectModel.Collection`1.Add(T item) at System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.GetAxes(DataPoint firstDataPoint, Func`2 independentAxisPredicate, Func`1 independentAxisFactory, Func`2 dependentAxisPredicate, Func`1 dependentAxisFactory) at System.Windows.Controls.DataVisualization.Charting.AreaSeries.GetAxes(DataPoint firstDataPoint) at System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.GetAxes() at System.Windows.Controls.DataVisualization.Charting.DataPointSeriesWithAxes.OnDataPointsChanged(IList`1 newDataPoints, IList`1 oldDataPoints) at System.Windows.Controls.DataVisualization.Charting.DataPointSingleSeriesWithAxes.OnDataPointsChanged(IList`1 newDataPoints, IList`1 oldDataPoints) at System.Windows.Controls.DataVisualization.Charting.LineAreaBaseSeries`1.OnDataPointsChanged(IList`1 newDataPoints, IList`1 oldDataPoints) at System.Windows.Controls.DataVisualization.Charting.DataPointSeries.LoadDataPoints(IEnumerable newItems, IEnumerable oldItems) at System.Windows.Controls.DataVisualization.Charting.DataPointSeries.Refresh() at System.Windows.Controls.DataVisualization.Charting.DataPointSeries.OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) at System.Windows.Controls.DataVisualization.Charting.DataPointSeries.OnItemsSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, OperationType operationType) at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp) at System.Windows.Data.BindingExpression.Invalidate(Boolean isASubPropertyChange) at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange) at System.Windows.Data.BindingExpression.Activate(Object item) at System.Windows.Data.BindingExpression.HandlePropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) at System.Windows.Data.BindingExpression.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs) at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, OperationType operationType) at System.Windows.TreeWalkHelper.OnInheritablePropertyChanged(DependencyObject d, InheritablePropertyChangeInfo info) at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d) at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren) at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d) at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren) at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d) at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren) at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d) at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren) at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d) at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) at System.Windows.DescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode) at System.Windows.TreeWalkHelper.InvalidateOnInheritablePropertyChange(FrameworkElement fe, FrameworkContentElement fce, InheritablePropertyChangeInfo info, Boolean skipStartNode) at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, OperationType operationType) at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, OperationType operationType, Boolean isInternal) at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) at System.Windows.FrameworkElement.set_DataContext(Object value) at NewsCluesWpf.ArticlesPerDay.<bg_RunWorkerCompleted>b__1(List`1 allDates) in C:\SoftwareInstall\VSProjects\NewsClues\NewsCluesWpf\ArticlesPerDay.xaml.cs:line 72 InnerException:

file attachments

comments

davidans wrote Jul 8, 2009 at 7:26 PM

This problem happens because the update to the Chart's DataContext prompts WPF to do a tree walk (see DescendentsWalker in the stack above). That in-progress walk means that Charting's attempt to add a new Axis to the Chart's Children collection fails because WPF complains that a collection can't be modified while an enumeration is in progress.

davidans wrote Jul 8, 2009 at 7:34 PM

Attaching repro project. Simple workaround is to modify DataPointSeries.ItemsSource directly instead of via Chart.DataContext.

thriol wrote Sep 16, 2009 at 2:21 PM

I have a similar problem.
I have bound the ShowGridLines to property in my view model.
When I stop viewing the chart, the binding is removed by WPF. This causes WPF to invalidate all properties and makes a walk through the visual and logical tree. A result of this is that WPF sets the property ShowGridLines back to the default value, which is "False" (i.e. no grid lines).
In the chart DisplayAxis.SetShowGridLines is called when this property is changed. This method will set DisplayAxis.GridLines = null. This then causes DisplayAxis.OnGridLinesPropertyChanged to be called, which will do SeriesHost.BackgroundElements.Remove(oldValue) in order to remove the Canvas (actually OrientedAxisGridLines) that was used to draw the grid lines.
However, this will be a modification of the logical tree which is not allowed since a property traversal is in progress.

Essentially this means that ShowGridLines cannot be used for binding. I guess that the rule is that you should never modify the logical tree in a dependency property callback.
Any ideas of a workaround? Or perhaps a fix of the chart control?