Hi everyone,
I’m converting one of our plugins from Rhino 7 to Rhino 8 and my users get an exception thrown by Rhino (not the plugin) - I would love to understand if it’s a problem created by the plugin or an unhandled exception on the Rhino side maybe ?
Steps to crash are as follows :
- User open the WPF plugin in a tab
- User clicks on button in plugin to import some geometries (OSM streets and buildings)
- The plugin creates the necessary layers, creates the geometry, sends the geometry on new layers.
- User switch to the Layers tab
- Rhino crash with the exception below
Useful notes
- The crash does not happen in Rhino 7 (same code)
- The crash does not happen in Rhino 8 DEBUG mode, BUT the layer table does not refresh (even after multiple redraw) in the Rhino UI
- The crash does happen in Rhino 8 in RELEASE mode
- The function to create geometry and layers are nested into larger background tasks
- I run 3 views.redraw() after the background tasks has finished
The bits of codes used in this bug
// function to create the layer if it does not exist
internal static int LayerByName(string layerName, Color c)
{
Layer newLayer = RhinoDoc.ActiveDoc.Layers.FindName(layerName);
int idx = -1;
if (newLayer == null)
{
newLayer = new Layer();
newLayer.Name = layerName;
newLayer.Color = c;
idx = RhinoDoc.ActiveDoc.Layers.Add(newLayer);
// here i have also added a 500ms delay in testing but it does not change anything
}
else
idx = newLayer.LayerIndex;
return idx;
}
// function to create the object
internal void ToRhino(bool temp = false, bool force3d = false)
{
// Set the object's attributes
ObjectAttributes attributes = new ObjectAttributes();
attributes.Name = Name;
attributes.ObjectId = new Guid();
int layerIndex = -1;
attributes.ObjectColor = ColorUtil.GisObjectColor(Category);
if(Category == null)
Category = "";
layerIndex = RhinoUtil.LayerByName($"OSM Context - {Category}", attributes.ObjectColor);
attributes.LayerIndex = layerIndex;
// Add the object to the document
RhinoDoc doc = RhinoDoc.ActiveDoc;
if (this.Outline != null)
{
if (Tags.ContainsKey("building"))
{
double dist = (double)GetValue(this, "building:height", true);
if (dist == 0 && !force3d)
{
RhinoApp.WriteLine("No height found.");
doc.Objects.AddCurve(Outline, attributes);
return;
}
etc etc
}
}
.
.
.
.
Rhino crash log
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: An ItemsControl is inconsistent with its items source.\n See the inner exception for more information.
---> System.Exception: Information for developers (use Text Visualizer to read this):
This exception was thrown because the generator for control 'Eto.Wpf.Forms.Controls.EtoDataGrid Items.Count:9' with name '(unnamed)' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection. The following differences were detected:
Accumulated count 6 is different from actual count 9. [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]
At index 0: Generator's item 'Rhino.UI.DialogPanels.LayerItem' is different from actual item 'Rhino.UI.DialogPanels.LayerItem'.
At index 1: Generator's item 'Rhino.UI.DialogPanels.LayerItem' is different from actual item 'Rhino.UI.DialogPanels.LayerItem'.
At index 2: Generator's item 'Rhino.UI.DialogPanels.LayerItem' is different from actual item 'Rhino.UI.DialogPanels.LayerItem'.
(... 3 more instances ...)
One or more of the following sources may have raised the wrong events:
System.Windows.Controls.ItemContainerGenerator
System.Windows.Controls.ItemCollection
System.Windows.Data.ListCollectionView
* Eto.CustomControls.TreeController
(The starred sources are considered more likely to be the cause of the problem.)
The most common causes are (a) changing the collection or its Count without raising a corresponding event, and (b) raising an event with an incorrect index or item parameter.
The exception's stack trace describes how the inconsistencies were detected, not how they occurred. To get a more timely exception, set the attached property 'PresentationTraceSources.TraceLevel' on the generator to value 'High' and rerun the scenario. One way to do this is to run a command similar to the following:\n System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator, System.Diagnostics.PresentationTraceLevel.High)
from the Immediate window. This causes the detection logic to run after every CollectionChanged event, so it will slow down the application.
--- End of inner exception stack trace ---
at System.Windows.Controls.ItemContainerGenerator.Verify()
at System.Windows.Controls.VirtualizingStackPanel.MeasureChild(IItemContainerGenerator& generator, IContainItemStorage& itemStorageProvider, IContainItemStorage& parentItemStorageProvider, Object& parentItem, Boolean& hasUniformOrAverageContainerSizeBeenSet, Double& computedUniformOrAverageContainerSize, Double& computedUniformOrAverageContainerPixelSize, Boolean& computedAreContainersUniformlySized, Boolean& hasAnyContainerSpanChanged, IList& items, Object& item, IList& children, Int32& childIndex, Boolean& visualOrderChanged, Boolean& isHorizontal, Size& childConstraint, Rect& viewport, VirtualizationCacheLength& cacheSize, VirtualizationCacheLengthUnit& cacheUnit, Int64& scrollGeneration, Boolean& foundFirstItemInViewport, Double& firstItemInViewportOffset, Size& stackPixelSize, Size& stackPixelSizeInViewport, Size& stackPixelSizeInCacheBeforeViewport, Size& stackPixelSizeInCacheAfterViewport, Size& stackLogicalSize, Size& stackLogicalSizeInViewport, Size& stackLogicalSizeInCacheBeforeViewport, Size& stackLogicalSizeInCacheAfterViewport, Boolean& mustDisableVirtualization, Boolean isBeforeFirstItem, Boolean isAfterFirstItem, Boolean isAfterLastItem, Boolean skipActualMeasure, Boolean skipGeneration, Boolean& hasBringIntoViewContainerBeenMeasured, Boolean& hasVirtualizingChildren)
at System.Windows.Controls.VirtualizingStackPanel.MeasureOverrideImpl(Size constraint, Nullable`1& lastPageSafeOffset, List`1& previouslyMeasuredOffsets, Nullable`1& lastPagePixelSize, Boolean remeasure)
at System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(Size constraint)
at System.Windows.Controls.Primitives.DataGridRowsPresenter.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
at System.Windows.Controls.ScrollContentPresenter.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean& hasDesiredSizeUChanged)
at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.ScrollViewer.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Control.MeasureOverride(Size constraint)
at System.Windows.Controls.DataGrid.MeasureOverride(Size availableSize)
at Eto.Wpf.Forms.WpfFrameworkElement`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfFrameworkElement.cs:line 114
at Eto.Wpf.Forms.Controls.EtoDataGrid.MeasureOverride(Size constraint) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\Controls\GridHandler.cs:line 24
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean& hasDesiredSizeUChanged)
at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
at Eto.Wpf.Forms.EtoGrid.MeasureOverride(Size constraint) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\TableLayoutHandler.cs:line 65
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at Eto.Wpf.Forms.WpfFrameworkElement`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfFrameworkElement.cs:line 114
at Eto.Wpf.Forms.TableLayoutHandler.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\TableLayoutHandler.cs:line 189
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean& hasDesiredSizeUChanged)
at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
at Eto.Wpf.Forms.EtoGrid.MeasureOverride(Size constraint) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\TableLayoutHandler.cs:line 65
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at Eto.Wpf.Forms.WpfFrameworkElement`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfFrameworkElement.cs:line 114
at Eto.Wpf.Forms.TableLayoutHandler.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\TableLayoutHandler.cs:line 189
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at Eto.Wpf.Forms.WpfFrameworkElement`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfFrameworkElement.cs:line 114
at Eto.Wpf.Forms.WpfContainer`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfContainer.cs:line 39
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.Controls.Border.MeasureOverride(Size constraint)
at Eto.Wpf.Forms.WpfFrameworkElement`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfFrameworkElement.cs:line 114
at Eto.Wpf.Forms.WpfContainer`3.MeasureOverride(Size constraint, Func`2 measure) in D:\BuildAgent\work\dujour\src4\DotNetSDK\Eto\src\Eto.Wpf\Forms\WpfContainer.cs:line 39
at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
at System.Windows.UIElement.Measure(Size availableSize)
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
[END ERROR]