File: System\Windows\Media\VisualTreeHelper.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
 
using System;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.Windows.Media.Effects;
 
using System.Collections;
using System.Diagnostics;
using MS.Internal;
using MS.Internal.Media;
 
using SR=MS.Internal.PresentationCore.SR;
 
namespace System.Windows.Media
{
    /// <summary>
    /// The VisualTreeHelper class contains static methods that are useful for performing
    /// common tasks with visual tree nodes.  Nodes in the visual tree may be Visual or
    /// Visual3Ds.
    ///
    /// When possible methods are typed to DependencyObject and may accept or
    /// return either type of visual tree node (e.g. GetParent).
    ///
    /// When the result of the operation is specific to 2D or 3D the methods are typed
    /// to Visual or Visual3D (e.g., GetDescendantBounds()).
    /// </summary>
    public static class VisualTreeHelper
    {
        private static void CheckVisualReferenceArgument(DependencyObject reference)
        {
            ArgumentNullException.ThrowIfNull(reference);
        }
 
        /// <summary>
        /// Return true if the given DependencyObject is a non-null visual type.
        /// This is useful as a precondition for many of the VisualTreeHelper methods,
        /// to avoid exceptions.
        /// </summary>
        /// <remarks>
		/// This could be considered to be made public
		/// </remarks>
        internal static bool IsVisualType(DependencyObject reference)
        {
            return (reference is Visual) || (reference is Visual3D);
        }
 
        /// <summary>
        /// Get the number of children of the specified Visual.
        /// </summary>
        public static int GetChildrenCount(DependencyObject reference)
        {
            Visual visual;
            Visual3D visual3D;
 
            VisualTreeUtils.AsNonNullVisual(reference, out visual, out visual3D);
 
            // x86 branch prediction skips the branch on first encounter.  We favor 2D.
            if (visual3D != null)
            {
                return visual3D.InternalVisual2DOr3DChildrenCount;
            }
 
            //
            // Even though visual is a 2D visual, it still may have
            // 3D children.
            //
            return visual.InternalVisual2DOr3DChildrenCount;
        }
 
        /// <summary>
        /// Returns the child of Visual visual at the specified index.
        /// </summary>
        public static DependencyObject GetChild(DependencyObject reference, int childIndex)
        {
            Visual visual;
            Visual3D visual3D;
 
            VisualTreeUtils.AsNonNullVisual(reference, out visual, out visual3D);
 
            // x86 branch prediction skips the branch on first encounter.  We favor 2D.
            if (visual3D != null)
            {
                return visual3D.InternalGet2DOr3DVisualChild(childIndex);
            }
 
            //
            // Even though visual is a 2D visual, it still may have
            // 3D children.
            //
            return visual.InternalGet2DOr3DVisualChild(childIndex);
        }
 
        /// <summary>
        /// Returns the DPI information at which this Visual is measured and rendered.
        /// </summary>
        public static DpiScale GetDpi(Visual visual)
        {
            return visual.GetDpi();
        }
 
 
        /// <summary>
        /// This method updates the DPI information of a visual. It can only be called on a Visual with no parent.
        /// </summary>
        public static void SetRootDpi(Visual visual, DpiScale dpiInfo)
        {
            if ((object)dpiInfo == null)
            {
                throw new NullReferenceException("dpiInfo cannot be null");
            }
            if (visual.InternalVisualParent != null)
            {
                throw new InvalidOperationException("UpdateDPI should only be called on the root of a Visual tree");
            }
            DpiFlags dpiFlags = DpiUtil.UpdateDpiScalesAndGetIndex(dpiInfo.PixelsPerInchX, dpiInfo.PixelsPerInchY);
            visual.RecursiveSetDpiScaleVisualFlags(new DpiRecursiveChangeArgs(dpiFlags, visual.GetDpi(), dpiInfo));
        }
 
        /// <summary>
        /// Visual parent of this Visual.
        /// </summary>
        public static DependencyObject GetParent(DependencyObject reference)
        {
            Visual visual;
            Visual3D visual3D;
 
            VisualTreeUtils.AsNonNullVisual(reference, out visual, out visual3D);
 
            // x86 branch prediction skips the branch on first encounter.  We favor 2D.
            if (visual3D != null)
            {
                return visual3D.InternalVisualParent;
            }
 
            return visual.InternalVisualParent;
        }
 
        /// <summary>
        /// Equivalent to GetParent except that it does not VerifyAccess and only asserts
        /// in CHK that the argument is actually a Visual or Visual3D.
        ///
        /// It is also different in that null is allowed as an argument, in which case
        /// it returns null.
        /// </summary>
        internal static DependencyObject GetParentInternal(DependencyObject reference)
        {
            Visual visual;
            Visual3D visual3D;
 
            VisualTreeUtils.AsVisualInternal(reference, out visual, out visual3D);
 
            if (visual != null)
            {
                return visual.InternalVisualParent;
            }
 
            if (visual3D != null)
            {
                return visual3D.InternalVisualParent;
            }
 
            Debug.Assert(reference == null);
 
            return null;
        }
 
        /// <summary>
        /// Returns the closest Visual that contains the given DependencyObject
        /// </summary>
        internal static Visual GetContainingVisual2D(DependencyObject reference)
        {
            Visual visual = null;
 
            while (reference != null)
            {
                visual = reference as Visual;
 
                if (visual != null) break;
 
                reference = VisualTreeHelper.GetParent(reference);
            }
 
            return visual;
        }
 
        /// <summary>
        /// Returns the closest Visual3D that contains the given DependencyObject
        /// </summary>
        internal static Visual3D GetContainingVisual3D(DependencyObject reference)
        {
            Visual3D visual3D = null;
 
            while (reference != null)
            {
                visual3D = reference as Visual3D;
 
                if (visual3D != null) break;
 
                reference = VisualTreeHelper.GetParent(reference);
            }
 
            return visual3D;
        }
 
        internal static bool IsAncestorOf(DependencyObject reference, DependencyObject descendant)
        {
            Visual visual;
            Visual3D visual3D;
 
            VisualTreeUtils.AsNonNullVisual(reference, out visual, out visual3D);
 
            // x86 branch prediction skips the branch on first encounter.  We favor 2D.
            if (visual3D != null)
            {
                return visual3D.IsAncestorOf(descendant);
            }
 
            return visual.IsAncestorOf(descendant);
        }
 
        // a version of IsAncestorOf that stops looking when it finds an ancestor
        // of the given type
        internal static bool IsAncestorOf(DependencyObject ancestor, DependencyObject descendant, Type stopType)
        {
            ArgumentNullException.ThrowIfNull(ancestor);
            ArgumentNullException.ThrowIfNull(descendant);
 
            VisualTreeUtils.EnsureVisual(ancestor);
            VisualTreeUtils.EnsureVisual(descendant);
 
            // Walk up the parent chain of the descendant until we run out of parents,
            // or we find the ancestor, or we reach a node of the given type.
            DependencyObject current = descendant;
 
            while ((current != null) && (current != ancestor) && !stopType.IsInstanceOfType(current))
            {
                if (current is Visual visual)
                {
                    current = visual.InternalVisualParent;
                }
                else if (current is Visual3D visual3D)
                {
                    current = visual3D.InternalVisualParent;
                }
                else
                {
                    current = null;
                }
            }
 
            return current == ancestor;
        }
 
        internal static DependencyObject FindCommonAncestor(DependencyObject reference, DependencyObject otherVisual)
        {
            Visual visual;
            Visual3D visual3D;
 
            VisualTreeUtils.AsNonNullVisual(reference, out visual, out visual3D);
 
            // x86 branch prediction skips the branch on first encounter.  We favor 2D.
            if (visual3D != null)
            {
                return visual3D.FindCommonVisualAncestor(otherVisual);
            }
 
            return visual.FindCommonVisualAncestor(otherVisual);
        }
 
        /// <summary>
        /// Gets the clip of this Visual.
        /// </summary>
        public static Geometry GetClip(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualClip;
        }
 
        /// <summary>
        /// Gets the opacity of the Visual.
        /// </summary>
        public static double GetOpacity(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualOpacity;
        }
 
        /// <summary>
        /// Gets the OpacityMask.
        /// </summary>
        public static Brush GetOpacityMask(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualOpacityMask;
        }
 
        /// <summary>
        /// Returns the offset of the Visual.
        /// </summary>
        public static Vector GetOffset(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualOffset;
        }
 
         /// <summary>
        /// Returns the Visual transform.
        /// </summary>
        public static Transform GetTransform(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualTransform;
        }
 
        /// <summary>
        /// Returns X-coordinate (vertical) guideline collection.
        /// </summary>
        public static DoubleCollection GetXSnappingGuidelines(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualXSnappingGuidelines;
        }
 
         /// <summary>
        /// Returns Y-coordinate (horizontal) guideline collection.
        /// </summary>
        public static DoubleCollection GetYSnappingGuidelines(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualYSnappingGuidelines;
        }
 
        /// <summary>
        /// GetDrawing returns the drawing content of the reference Visual
        /// </summary>
        public static DrawingGroup GetDrawing(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.GetDrawing();
        }
 
        /// <summary>
        /// GetContentBounds returns the bounding box for the contents of the specified visual.
        /// </summary>
        public static Rect GetContentBounds(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualContentBounds;
        }
 
        /// <summary>
        /// GetContentBounds returns the bounding box for the contents of the specified visual.
        /// </summary>
        public static Rect3D GetContentBounds(Visual3D reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualContentBounds;
        }
 
        /// <summary>
        /// GetDescendantBounds returns the union of all of the content bounding
        /// boxes of the specified Visual's sub-graph.
        /// </summary>
        public static Rect GetDescendantBounds(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualDescendantBounds;
        }
 
        /// <summary>
        /// GetDescendantBounds returns the union of all of the content bounding
        /// boxes of the specified Visual3D's sub-graph.
        /// </summary>
        public static Rect3D GetDescendantBounds(Visual3D reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualDescendantBounds;
        }
 
        /// <summary>
        /// Gets the BitmapEffect.
        /// </summary>
        public static BitmapEffect GetBitmapEffect(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
#pragma warning disable 0618
            return reference.VisualBitmapEffect;
#pragma warning restore 0618
        }
 
        /// <summary>
        /// Gets the BitmapEffectInput.
        /// </summary>
        public static BitmapEffectInput GetBitmapEffectInput(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
#pragma warning disable 0618
            return reference.VisualBitmapEffectInput;
#pragma warning restore 0618
        }
 
        /// <summary>
        /// Gets the Effect.
        /// </summary>
        public static Effect GetEffect(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualEffect;
        }
 
        /// <summary>
        /// Gets the CacheMode.
        /// </summary>
        public static CacheMode GetCacheMode(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualCacheMode;
        }
 
        /// <summary>
        /// Gets the EdgeMode of the Visual.
        /// </summary>
        public static EdgeMode GetEdgeMode(Visual reference)
        {
            CheckVisualReferenceArgument(reference);
            return reference.VisualEdgeMode;
        }
 
        /// <summary>
        /// Return top most visual of a hit test.
        /// </summary>
        public static HitTestResult HitTest(Visual reference, Point point)
        {
            return HitTest(reference, point, true);
        }
 
        /// <summary>
        /// </summary>
        internal static HitTestResult HitTest(Visual reference, Point point, bool include2DOn3D)
        {
            CheckVisualReferenceArgument(reference);
 
            return reference.HitTest(point, include2DOn3D);
        }
 
        /// <summary>
        /// Initiate a hit test using delegates.
        /// </summary>
        public static void HitTest(
            Visual reference,
            HitTestFilterCallback filterCallback,
            HitTestResultCallback resultCallback,
            HitTestParameters hitTestParameters)
        {
            CheckVisualReferenceArgument(reference);
            reference.HitTest(filterCallback, resultCallback, hitTestParameters);
        }
 
        /// <summary>
        /// Initiate a hit test using delegates.
        /// </summary>
        public static void HitTest(
            Visual3D reference,
            HitTestFilterCallback filterCallback,
            HitTestResultCallback resultCallback,
            HitTestParameters3D hitTestParameters)
        {
            CheckVisualReferenceArgument(reference);
            reference.HitTest(filterCallback, resultCallback, hitTestParameters);
        }
 
#if WCP_MF_ENABLED
        /// <summary>
        /// Serialize a visual to fixed (S0) XAML
        /// </summary>
        /// <param name="visual"></param>
        /// <param name="writer"></param>
        static internal void SaveAsXml(Visual visual, System.Xml.XmlWriter writer)
        {
            VisualTreeFlattener.SaveAsXml(visual, writer, new FixedXamlDesigner());
        }
 
        /// <summary>
        /// Walk a visual tree and flatten it to (S0) DrawingContext
        /// </summary>
        /// <param name="visual"></param>
        /// <param name="dc"></param>
        static public void Walk(Visual visual, DrawingContext dc)
        {
            VisualTreeFlattener flattener = new VisualTreeFlattener(dc);
 
            flattener.Walk(visual, null);
        }
#endif // WCP_MF_ENABLED
    }
}