dotnet/wpf · error · InvalidOperationException
inverse ? SR.Visual_NotADescendant : SR.Visual_NotAnAncestor
Error message
inverse ? SR.Visual_NotADescendant : SR.Visual_NotAnAncestor
What it means
Visual.TryTransformToAncestor/ancestor transform helpers throw InvalidOperationException with SR.Visual_NotAnAncestor (or Visual_NotADescendant when inverse) when, after walking the visual/visual3D ancestry, the target does not stand in the required ancestor/descendant relation.
Solutions
- Verify the relationship first (walk VisualTreeHelper.GetParent chain up to the target)
- Recompute the transform after any layout/re-parenting changes
- For 2D-in-3D content, use the containing Viewport2DVisual3D as the ancestor
Example fix
// before
var t = child.TransformToAncestor(sibling);
// after
bool isAncestor = false;
for (DependencyObject p = VisualTreeHelper.GetParent(child); p != null; p = VisualTreeHelper.GetParent(p))
if (p == sibling) { isAncestor = true; break; }
if (isAncestor) { var t = child.TransformToAncestor(sibling); } Defensive patterns
Strategy: validation
Validate before calling
bool IsAncestor(DependencyObject ancestor, DependencyObject node) { for (DependencyObject p = node; p != null; p = VisualTreeHelper.GetParent(p)) if (p == ancestor) return true; return false; } Type guard
bool IsAncestorOf(Visual a, Visual v) => IsAncestor(a, v);
Try / catch
try { var t = child.TransformToAncestor(anc); } catch (InvalidOperationException) { /* not ancestor/descendant */ } Prevention
- Verify ancestry chain before calling transform APIs
- Recompute after layout changes
- For 2D-in-3D, use Viewport2DVisual3D as intermediary
When it happens
Trigger: Calling TransformToAncestor(a) where a is not an ancestor of this visual, or TransformToDescendant(d) where d is not a descendant; 2D/3D boundary cases where GetContainingVisual2D does not reach the given ancestor.
Common situations: Passing a sibling instead of an ancestor; element moved/re-parented between computing and transforming; mixing Viewport2DVisual3D content with ordinary 2D ancestry assumptions.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- SR.Visual_CannotTransformPoint
- SR.Visual_NoCommonAncestor
- ' ' is not a Visual or Visual3D.
- Page can have only Window or Frame as parent.
- Specified index is out of range or child at index is null…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/95fe8d1648766f6f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Visual.cs:4573
if (embedded2Don3D)
{
visualForGenTransform = gAsVisual3D.Visual;
}
else
{
visualForGenTransform = this;
embedded2Don3D = true;
}
group.Children.Add(new GeneralTransform2DTo3DTo2D(gAsVisual3D, visualForGenTransform));
g = VisualTreeHelper.GetContainingVisual2D(gAsVisual3D);
}
}
if (g != ancestor)
{
throw new System.InvalidOperationException(inverse ? SR.Visual_NotADescendant : SR.Visual_NotAnAncestor);
}
// At this point, we will have 0 or more transforms in the GeneralTransformGroup
// and the matrix which, if not identity, should be appended to the GeneralTransformGroup.
// If, as is commonly the case, this loop terminates without encountering a bitmap effect
// we will simply use the Matrix.
// Assert that a non-null group implies at least one child
Debug.Assert((group == null) || (group.Children.Count > 0));
// Do we have a group?
if (group != null)
{
if (!m.IsIdentity)
{
group.Children.Add(new MatrixTransform(m));
}
View on GitHub (pinned to 81131a70a4)