dotnet/wpf · error · ArgumentException

SR.TableCollectionWrongProxyParent

Error message

SR.TableCollectionWrongProxyParent

What it means

When adding an item, PrivateConnectChild checks parentage. If the item's logical Parent is a DummyProxy (a lightweight parent surrogate used by table text element collections), the proxy must resolve through the logical tree to this collection's Owner. If GetParent(item.Parent) != Owner, the item already belongs to a different table/collection and ArgumentException(TableCollectionWrongProxyParent) is thrown.

Solutions

  1. Detach the item from its current parent first (remove it from the old collection) before adding it here.
  2. Create a new instance instead of reusing an element already parented elsewhere.
  3. Check item.Parent (and logical ancestors) before adding; only add unparented items.

Example fix

// before
var row = oldTable.RowGroups[0];
newTable.RowGroups.Add(row); // throws
// after
var row = oldTable.RowGroups[0];
oldTable.RowGroups.Remove(row);
newTable.RowGroups.Add(row);
Defensive patterns

Strategy: validation

Validate before calling

if (item.Parent != null && !(item.Parent is DummyProxy && LogicalTreeHelper.GetParent(item.Parent) == owner))
    throw new InvalidOperationException("item is parented to another collection");

Type guard

static bool IsUnparentedOrOwnedBy(this TextElement item, object owner)
    => item.Parent == null
       || (item.Parent is DummyProxy && LogicalTreeHelper.GetParent(item.Parent) == owner);

Prevention

When it happens

Trigger: InternalAdd on an item whose Parent is a DummyProxy owned by another TableTextElementCollectionInternal — i.e. re-parenting a TableRowGroup/TableRow/Run that is already in another table.

Common situations: Moving a TableRowGroup from one Table to another without first detaching it; adding an element still referenced by a previous document/collection; sharing a child element between two tables.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/55570f180d99c338. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/TableTextElementCollectionInternal.cs:265

        /// <exception cref="ArgumentException">
        /// If the new item has already a parent or if the slot at the specified index is not null.
        /// </exception>
        /// <remarks>
        /// Note that the function requires that _item[index] == null and
        /// it also requires that the passed in item is not included into another ContentElementCollection.
        /// </remarks>
        internal override void PrivateConnectChild(int index, TElementType item)
        {
            Debug.Assert(item != null && item.Index == -1);
            Debug.Assert(Items[index] == null);

            // If the TElementType is already parented correctly through a proxy, there's no need
            // to change parentage.  Otherwise, it should be parented to Owner.
            if (item.Parent is DummyProxy)
            {
                if (LogicalTreeHelper.GetParent(item.Parent) != Owner)
                {
                    throw new System.ArgumentException(SR.TableCollectionWrongProxyParent);
                }
            }

            // add the item into collection's array
            Items[index] = item;
            item.Index = index;

            // notify the TElementType about the change
            item.OnEnterParentTree();
        }


        /// <summary>
        /// Notifies the TItem about the event;
        /// Disconnects the item from the model tree;
        /// Sets the TItem's slot in the collection's array to null.
        /// </summary>
        internal override void PrivateDisconnectChild(TElementType item)

View on GitHub (pinned to 81131a70a4)