Layout Element Cloning

631
7
05-11-2020 11:45 AM
JeffBoyton
New Contributor II

I need to programmatically clone a layout element many times but it keeps crashing ArcGIS Pro.  I have narrowed it down to this snippet:

GraphicElement element = layout.FindElement(DefaultSymbols.DEFAULT_SYMBOL_NAME) as GraphicElement;
for (int i=0; i<1000; i++)
{
   GraphicElement clonedElement = element.Clone();
   clonedElement.SetName(elementName + i);
}

This code never completes.  It gets a couple of hundred in and then excepts (the number of times through the loop varies from run to run).  I am running this code inside of a QueuedTask.Run().  If I remove the SetName() I can get through more iterations (usually more than 1,000), but it still fails.  Below are the stack traces it produces.  Any suggestions about how to avoid this issue?

Trace with SetName()

System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>ArcGISPro.exe</AppDomain><Exception><ExceptionType>System.NullReferenceException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Object reference not set to an instance of an object.</Message><StackTrace> at ArcGIS.Desktop.Layouts.Element.IsFrameElement(Element element)
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at ArcGIS.Desktop.Layouts.Element.SelectionHasFrame()
at ArcGIS.Desktop.Layouts.Element.GetSelectedSubElements()
at ArcGIS.Desktop.Layouts.Layout.&amp;lt;&amp;gt;c__DisplayClass130_0.&amp;lt;UpdateGUISelectionAsync&amp;gt;b__0()
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)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext 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&amp;amp; handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
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)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG&amp;amp; msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at main(String[] args)</StackTrace><ExceptionString>System.NullReferenceException: Object reference not set to an instance of an object.
at ArcGIS.Desktop.Layouts.Element.IsFrameElement(Element element)
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at ArcGIS.Desktop.Layouts.Element.SelectionHasFrame()
at ArcGIS.Desktop.Layouts.Element.GetSelectedSubElements()
at ArcGIS.Desktop.Layouts.Layout.&amp;lt;&amp;gt;c__DisplayClass130_0.&amp;lt;UpdateGUISelectionAsync&amp;gt;b__0()
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)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext 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&amp;amp; handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
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)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG&amp;amp; msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at main(String[] args)</ExceptionString><DataItems><Data></Data></DataItems></Exception></TraceRecord>
An unhandled exception of type 'System.NullReferenceException' occurred in ArcGIS.Desktop.Layouts.dll
Object reference not set to an instance of an object.

Trace without SetName()

System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>ArcGISPro.exe</AppDomain><Exception><ExceptionType>System.ArgumentException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>Destination array was not long enough. Check destIndex and length, and the array's lower bounds.</Message><StackTrace> at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
at System.Collections.Generic.List`1.CopyTo(T[] array, Int32 arrayIndex)
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at ArcGIS.Desktop.Internal.Layouts.ElementContainer.ArcGIS.Desktop.Internal.Layouts.IElementContainerInternal.GetFlattenedElements()
at ArcGIS.Desktop.Internal.Layouts.ElementContainer.ArcGIS.Desktop.Internal.Layouts.IElementContainerInternal.FindElement(String elementName, Boolean recurse)
at ArcGIS.Desktop.Internal.Layouts.Utilities.ElementUtil.&amp;lt;&amp;gt;c__DisplayClass5_0.&amp;lt;SelectedElements&amp;gt;b__0(String name)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at ArcGIS.Desktop.Internal.Layouts.Utilities.ElementUtil.SelectedElements()
at ArcGIS.Desktop.Layouts.Element.SelectionHasFrame()
at ArcGIS.Desktop.Layouts.Element.GetSelectedSubElements()
at ArcGIS.Desktop.Layouts.Layout.&amp;lt;&amp;gt;c__DisplayClass130_0.&amp;lt;UpdateGUISelectionAsync&amp;gt;b__0()
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)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext 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&amp;amp; handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
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)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG&amp;amp; msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at main(String[] args)</StackTrace><ExceptionString>System.ArgumentException: Destination array was not long enough. Check destIndex and length, and the array's lower bounds.
at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
at System.Collections.Generic.List`1.CopyTo(T[] array, Int32 arrayIndex)
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at ArcGIS.Desktop.Internal.Layouts.ElementContainer.ArcGIS.Desktop.Internal.Layouts.IElementContainerInternal.GetFlattenedElements()
at ArcGIS.Desktop.Internal.Layouts.ElementContainer.ArcGIS.Desktop.Internal.Layouts.IElementContainerInternal.FindElement(String elementName, Boolean recurse)
at ArcGIS.Desktop.Internal.Layouts.Utilities.ElementUtil.&amp;lt;&amp;gt;c__DisplayClass5_0.&amp;lt;SelectedElements&amp;gt;b__0(String name)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at ArcGIS.Desktop.Internal.Layouts.Utilities.ElementUtil.SelectedElements()
at ArcGIS.Desktop.Layouts.Element.SelectionHasFrame()
at ArcGIS.Desktop.Layouts.Element.GetSelectedSubElements()
at ArcGIS.Desktop.Layouts.Layout.&amp;lt;&amp;gt;c__DisplayClass130_0.&amp;lt;UpdateGUISelectionAsync&amp;gt;b__0()
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)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext 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&amp;amp; handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean&amp;amp; handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
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)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG&amp;amp; msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at main(String[] args)</ExceptionString><DataItems><Data></Data></DataItems></Exception></TraceRecord>
An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll
Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

0 Kudos
7 Replies
JeremyWiles
New Contributor III

I have been successfully cloning using this code which is an extention of CIM.  I am sure you could modify it to work for your purposes.  The "CloneCIM" function does the heavy lifting, as you can see.

'Example of usage:


Public Function DuplicateLayout(ByVal layout As Layout, sName As String) As Layout
Try

If layout Is Nothing Then Throw New ArgumentNullException(NameOf(layout), "layout cannot be null")
Dim layout_clone As Layout = Clone(layout, sName)

Dim lytDef As CIMLayout = layout_clone.GetDefinition()
layout_clone.SetDefinition(lytDef)
layout_clone.SetName(sName)


Return layout_clone

Catch ex As Exception
MessageBox.Show(ex.Message)
WriteLine(ex.Message)
Return Nothing
End Try
End Function


<Extension()>
Function CloneAsync(ByVal layout As Layout, sName As String) As Task(Of Layout)
Return QueuedTask.Run(Function() Clone(layout, sName))
End Function


<Extension()>
Public Function Clone(ByVal layout As Layout, sName As String) As Layout
Dim layout_dup = LayoutFactory.Instance.CreateLayout(layout.GetPage)
Dim metadata_uri = layout_dup.GetDefinition().MetadataURI
Dim layout_def = layout.GetDefinition()
Dim layout_def_clone = TryCast(layout_def.CloneCIM(), CIMLayout)
layout_def_clone.URI = layout_dup.URI
layout_def_clone.MetadataURI = metadata_uri
'layout_dup.SetName(sName)
layout_dup.SetDefinition(layout_def_clone)

Return layout_dup
End Function
End Module

Module CIMExtensions
<Extension()>
Public Function CloneCIM(ByVal cimObject As CIMObject) As CIMObject
Dim clone = TryCast(System.Activator.CreateInstance("ArcGIS.Core", cimObject.[GetType]().ToString()).Unwrap(), CIMObject)
Dim stringReader = New StringReader(cimObject.ToXml())
Dim xmlReader = New XmlTextReader(stringReader)
clone.ReadXml(xmlReader)
xmlReader.Dispose()
Return clone
End Function
End Module

0 Kudos
JeffBoyton
New Contributor II

It appears as though you are cloning a layout, not creating new cloned elements in a layout.

0 Kudos
JeremyWiles
New Contributor III

Right, in the comment I mentioned that you could use the same concept for your purposes.  It doesn't need to be a CIMLayout, could be CIMGraphic.  Just modify it to fit your needs.

0 Kudos
JeffBoyton
New Contributor II

I changed the inner loop to this given Jeremy's suggestion.  This seems solve the crash issue but is also much slower and obviously very inefficient.  Is there a better solution out there?

GraphicElement defaultSymbol = layout.FindElement(DefaultSymbols.DEFAULT_SYMBOL_NAME) as GraphicElement;
CIMGraphicElement element = defaultSymbol.GetDefinition() as CIMGraphicElement;
string elementXml = element.ToXml();
GraphicElement symbolElement = LayoutElementFactory.Instance.CreatePointGraphicElement(layout, new Coordinate2D());
CIMElement symbolElementCIM = symbolElement.GetDefinition();
StringReader sr = new StringReader(elementXml);
using (XmlTextReader xtr = new XmlTextReader(sr))
{
    symbolElementCIM.ReadXml(xtr);
}
symbolElement.SetDefinition(symbolElementCIM);
symbolElement.SetName(elementName);

0 Kudos
UmaHarano
Esri Regular Contributor

Hi Jeff,

Couple of things to try:

1. Have you tried your code on 2.5.1 patch? This issue might be fixed in the patch.  Check out bug No. 000130503. Release notes for ArcGIS Pro 2.5—ArcGIS Pro | Documentation 

2. Another thing to try is to split the cloning and the "Naming" into separate QueuedTask class. Something like this:

    a. Create the elements in a loop within QueuedTask. Record the names in a dictionary, store with each name the name you want to change the element to. Exit the QueuedTask. This first loop is only create the elements.
    b. Change the name of each element in the CIM Defintion. Use the lookup dictionary to find the old and change it to the new. This second loop will help prevent the crash.

Thanks

Uma

0 Kudos
JeffBoyton
New Contributor II

I tested this in 2.5.1 and it still crashes and unfortunately my sequencing does not lend itself to separating the operations.  I have narrowed it down to this code snippet.  It is still quite slow.  I think this is due to the fact that every call to CreatePointGraphic is also selecting that element in the TOC and triggering other panes (like the Edit Feature pane).  Is there a way to create an element, but not have it be selected?

GraphicElement defaultSymbol = layout.FindElement(DefaultSymbols.DEFAULT_SYMBOL_NAME) as GraphicElement;
GraphicElement newSymbol = LayoutElementFactory.Instance.CreatePointGraphicElement(layout, new Coordinate2D());
CIMElement sourceCIM = defaultSymbol.GetDefinition();
newSymbol.SetDefinition(sourceCIM);
newSymbol.SetName(elementName);

0 Kudos
UmaHarano
Esri Regular Contributor

Hi Jeff,

We understand the issue you have encountered and have prioritized adding bulk creation overloads for a near term release.

Thank you!

Uma

0 Kudos