LayoutManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / LayoutManager.cs / 6 / LayoutManager.cs

                            /****************************************************************************\ 
*
* File: ContextLayoutManager.cs
*
*  Defines a top-level ContextLayoutManager - a layout dirtiness tracking/clearing system. 
*
* Copyright (C) 2001 by Microsoft Corporation.  All rights reserved. 
* 
\***************************************************************************/
using System; 
using System.Windows.Threading;
using System.Collections;

using System.Windows.Media; 
using System.Windows.Media.Media3D;
using System.Windows.Automation.Peers; 
 
using MS.Internal;
using MS.Utility; 
using System.Security;
using System.Security.Permissions;

using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
 
namespace System.Windows 
{
 
    /// 
    /// Top-level ContextLayoutManager object. Manages the layout update and layout dirty state.
    /// 
    internal sealed class ContextLayoutManager : DispatcherObject 
    {
 
        internal ContextLayoutManager() 
        {
            _shutdownHandler = new EventHandler(this.OnDispatcherShutdown); 
            Dispatcher.ShutdownFinished += _shutdownHandler;
        }

        void OnDispatcherShutdown(object sender, EventArgs e) 
        {
            if(_shutdownHandler != null) 
                Dispatcher.ShutdownFinished -= _shutdownHandler; 

            _shutdownHandler = null; 
            _layoutEvents = null;
            _measureQueue = null;
            _arrangeQueue = null;
            _sizeChangedChain = null; 
            _isDead = true;
        } 
 

        ///  
        /// The way to obtain ContextLayoutManager associated with particular Dispatcher.
        /// 
        /// A dispatcher for which ContextLayoutManager is queried.
        /// There is only one ContextLayoutManager associuated with all elements in a single context 
        /// ContextLayoutManager
        internal static ContextLayoutManager From(Dispatcher dispatcher) 
        { 
            ContextLayoutManager lm = dispatcher.Reserved3 as ContextLayoutManager;
            if(lm == null) 
            {
                if(Dispatcher.CurrentDispatcher != dispatcher)
                {
                    throw new InvalidOperationException(); 
                }
 
                lm = new ContextLayoutManager(); 
                dispatcher.Reserved3 = lm;
            } 
            return lm;
        }

        private void setForceLayout(UIElement e) 
        {
            _forceLayoutElement = e; 
        } 

        private void markTreeDirty(UIElement e) 
        {
            //walk up until we are the topmost UIElement in the tree.
            while(true)
            { 
                UIElement p = e.GetUIParentNo3DTraversal() as UIElement;
                if(p == null) break; 
                e = p; 
            }
 
            markTreeDirtyHelper(e);
            MeasureQueue.Add(e);
            ArrangeQueue.Add(e);
        } 

        private void markTreeDirtyHelper(Visual v) 
        { 
            //now walk down and mark all UIElements dirty
            if(v != null) 
            {
                if(v.CheckFlagsAnd(VisualFlags.IsUIElement))
                {
                    UIElement uie = ((UIElement)v); 
                    uie.InvalidateMeasureInternal();
                    uie.InvalidateArrangeInternal(); 
                } 

                //walk children doing the same, don't stop if they are already dirty since there can 
                //be insulated dirty islands below
                int cnt = v.InternalVisualChildrenCount;

                for(int i=0; i s_LayoutRecursionLimit)
                throw new InvalidOperationException(SR.Get(SRID.LayoutManager_DeepRecursion, s_LayoutRecursionLimit)); 
 
            _firePostLayoutEvents = true;
        } 

        internal void ExitMeasure()
        {
            _measuresOnStack--; 
            Dispatcher._disableProcessingCount--;
        } 
 
        internal void EnterArrange()
        { 
            Dispatcher._disableProcessingCount++;
            _lastExceptionElement = null;
            _arrangesOnStack++;
            if(_arrangesOnStack > s_LayoutRecursionLimit) 
                throw new InvalidOperationException(SR.Get(SRID.LayoutManager_DeepRecursion, s_LayoutRecursionLimit));
 
            _firePostLayoutEvents = true; 
        }
 
        internal void ExitArrange()
        {
            _arrangesOnStack--;
            Dispatcher._disableProcessingCount--; 
        }
 
        ///  
        /// Tells ContextLayoutManager to finalize possibly async update.
        /// Used before accessing services off Visual. 
        /// 

        //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647
        internal void UpdateLayout() 
        {
            VerifyAccess(); 
 
            //make UpdateLayout to be a NOP if called during UpdateLayout.
            if (   _isInUpdateLayout 
                || _measuresOnStack > 0
                || _arrangesOnStack > 0
                || _isDead) return;
 
            bool etwTracingEnabled = false;
 
#if DEBUG_CLR_MEM 
            bool clrTracingEnabled = false;
 
            // Start over with the Measure and arrange counters for this layout pass
            int measureCLRPass = 0;
            int arrangeCLRPass = 0;
 
            if (CLRProfilerControl.ProcessIsUnderCLRProfiler)
            { 
                clrTracingEnabled = true; 
                if (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance)
                { 
                    ++_layoutCLRPass;
                    CLRProfilerControl.CLRLogWriteLine("Begin_Layout_{0}", _layoutCLRPass);
                }
            } 
#endif // DEBUG_CLR_MEM
 
            if (!_isUpdating && EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
            {
                etwTracingEnabled = true; 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.LAYOUTPASSGUID), EventType.StartEvent,Dispatcher.GetHashCode());
            }

            int cnt = 0; 
            bool gotException = true;
            UIElement currentElement = null; 
 
            try
            { 
                invalidateTreeIfRecovering();


                while(hasDirtiness || _firePostLayoutEvents) 
                {
                    if(++cnt > 153) 
                    { 
                        //loop detected. Lets go over to background to let input/user to correct the situation.
                        //most frequently, we get such a loop as a result of input detecting a mouse in the "bad spot" 
                        //and some event handler oscillating a layout-affecting property depending on hittest result
                        //of the mouse. Going over to background will not break the loopp but will allow user to
                        //move the mouse so that it goes out of the "bad spot".
                        Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); 
                        currentElement = null;
                        gotException = false; 
                        return; 
                    }
 

                    //this flag stops posting update requests to MediaContext - we are already in one
                    //note that _isInUpdateLayout is close but different - _isInUpdateLayout is reset
                    //before firing LayoutUpdated so that event handlers could call UpdateLayout but 
                    //still could not cause posting of MediaContext work item. Posting MediaContext workitem
                    //causes infinite loop in MediaContext. 
                    _isUpdating = true; 
                    _isInUpdateLayout = true;
 
#if DEBUG_CLR_MEM
                    if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose))
                    {
                        ++measureCLRPass; 
                        CLRProfilerControl.CLRLogWriteLine("Begin_Measure_{0}_{1}", _layoutCLRPass, measureCLRPass);
                    } 
#endif // DEBUG_CLR_MEM 

                    if (etwTracingEnabled) 
                    {
                        EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.MEASUREGUID), EventType.StartEvent, Dispatcher.GetHashCode());
                    }
 
                    // Disable processing of the queue during blocking operations to prevent unrelated reentrancy.
                    using(Dispatcher.DisableProcessing()) 
                    { 

                        //loop for Measure 
                        //We limit the number of loops here by time - normally, all layout
                        //calculations should be done by this time, this limit is here for
                        //emergency, "infinite loop" scenarios - yielding in this case will
                        //provide user with ability to continue to interact with the app, even though 
                        //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one
                        //and it will be impossible to save results or even close the window. 
                        int loopCounter = 0; 
                        DateTime loopStartTime = new DateTime(0);
                        while(true) 
                        {
                            if(++loopCounter > 153)
                            {
                                loopCounter = 0; 
                                //first bunch of iterations is free, then we start count time
                                //this way, we don't call DateTime.Now in most layout updates 
                                if(loopStartTime.Ticks == 0) 
                                {
                                    loopStartTime = DateTime.Now; 
                                }
                                else
                                {
                                    TimeSpan loopDuration = (DateTime.Now - loopStartTime); 
                                    if(loopDuration.Milliseconds > 153*2) // 153*2 = magic*science
                                    { 
                                        //loop detected. Lets go over to background to let input work. 
                                        Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this);
                                        currentElement = null; 
                                        gotException = false;
                                        return;
                                    }
                                } 
                            }
 
                            currentElement = MeasureQueue.GetTopMost(); 

                            if(currentElement == null) break; //exit if no more Measure candidates 

                            currentElement.Measure(currentElement.PreviousConstraint);
//[....], bug 1150880: not clear why this is needed, remove for now
//if the parent was just computed, the chidlren should be clean. If they are not clean and in the queue 
//that means that there is cross-tree dependency and they most likely shodul be updated by themselves.
//                            MeasureQueue.RemoveOrphans(currentElement); 
                        } 

                        if (etwTracingEnabled) 
                        {
                            EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.MEASUREGUID), EventType.EndEvent);
                            EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.ARRANGEGUID), EventType.StartEvent, Dispatcher.GetHashCode());
                        } 

 
#if DEBUG_CLR_MEM 
                        if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose))
                        { 
                            CLRProfilerControl.CLRLogWriteLine("End_Measure_{0}_{1}", _layoutCLRPass, measureCLRPass);
                            ++arrangeCLRPass;
                            CLRProfilerControl.CLRLogWriteLine("Begin_Arrange_{0}_{1}", _layoutCLRPass, arrangeCLRPass);
                        } 
#endif // DEBUG_CLR_MEM
 
                        //loop for Arrange 
                        //if Arrange dirtied the tree go clean it again
 
                        //We limit the number of loops here by time - normally, all layout
                        //calculations should be done by this time, this limit is here for
                        //emergency, "infinite loop" scenarios - yielding in this case will
                        //provide user with ability to continue to interact with the app, even though 
                        //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one
                        //and it will be impossible to save results or even close the window. 
                        loopCounter = 0; 
                        loopStartTime = new DateTime(0);
                        while(MeasureQueue.IsEmpty) 
                        {
                            if(++loopCounter > 153)
                            {
                                loopCounter = 0; 
                                //first bunch of iterations is free, then we start count time
                                //this way, we don't call DateTime.Now in most layout updates 
                                if(loopStartTime.Ticks == 0) 
                                {
                                    loopStartTime = DateTime.Now; 
                                }
                                else
                                {
                                    TimeSpan loopDuration = (DateTime.Now - loopStartTime); 
                                    if(loopDuration.Milliseconds > 153*2) // 153*2 = magic*science
                                    { 
                                        //loop detected. Lets go over to background to let input work. 
                                        Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this);
                                        currentElement = null; 
                                        gotException = false;
                                        return;
                                    }
                                } 
                            }
 
                            currentElement = ArrangeQueue.GetTopMost(); 

                            if(currentElement == null) break; //exit if no more Measure candidates 

                            Rect finalRect = getProperArrangeRect(currentElement);

                            currentElement.Arrange(finalRect); 
//[....], bug 1150880: not clear why this is needed, remove for now
//if the parent was just computed, the chidlren should be clean. If they are not clean and in the queue 
//that means that there is cross-tree dependency and they most likely shodul be updated by themselves. 
//                            ArrangeQueue.RemoveOrphans(currentElement);
                        } 

                        if (etwTracingEnabled)
                        {
                            EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.ARRANGEGUID), EventType.EndEvent); 
                        }
 
#if DEBUG_CLR_MEM 
                        if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose))
                        { 
                            CLRProfilerControl.CLRLogWriteLine("End_Arrange_{0}_{1}", _layoutCLRPass, arrangeCLRPass);
                        }
#endif // DEBUG_CLR_MEM
 
                        //if Arrange dirtied the tree go clean it again
                        //it is not neccesary to check ArrangeQueue sicnce we just exited from Arrange loop 
                        if(!MeasureQueue.IsEmpty) continue; 

                        //let LayoutUpdated handlers to call UpdateLayout 
                        //note that it means we can get reentrancy into UpdateLayout past this point,
                        //if any of event handlers call UpdateLayout sync. Need to protect from reentrancy
                        //in the firing methods below.
                        _isInUpdateLayout = false; 

                    } 
 
                    fireSizeChangedEvents();
                    if(hasDirtiness) continue; 
                    fireLayoutUpdateEvent();
                    if(hasDirtiness) continue;
                    fireAutomationEvents();
 
                }
 
                currentElement = null; 
                gotException = false;
            } 
            finally
            {
                _isUpdating = false;
                _layoutRequestPosted = false; 
                _isInUpdateLayout = false;
 
                if(gotException) 
                {
                    //set indicator 
                    _gotException = true;
                    _forceLayoutElement = currentElement;

                    //make attempt to request the subsequent layout calc 
                    //some exception handler schemas use Idle priorities to
                    //wait until dust settles. Then they correct the issue noted in the exception handler. 
                    //We don't want to attempt to re-do the operation on the priority higher then that. 
                    Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, _updateLayoutBackground, this);
                } 
            }

            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.LAYOUTPASSGUID), EventType.EndEvent);
            } 
 
#if DEBUG_CLR_MEM
            if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance)) 
            {
                CLRProfilerControl.CLRLogWriteLine("End_Layout_{0}", _layoutCLRPass);
            }
#endif // DEBUG_CLR_MEM 

        } 
 
        private Rect getProperArrangeRect(UIElement element)
        { 
            Rect arrangeRect = element.PreviousArrangeRect;

            // ELements without a parent (top level) get Arrange at DesiredSize
            // if they were measured "to content" (as infinity indicates). 
            // If we arrange the element that is temporarily disconnected
            // so it is not a top-level one, the assumption is that it will be 
            // layout-invalidated and/or recomputed by the parent when reconnected. 
            if (element.GetUIParentNo3DTraversal() == null)
            { 
                arrangeRect.X = arrangeRect.Y = 0;

                if (double.IsPositiveInfinity(element.PreviousConstraint.Width))
                    arrangeRect.Width = element.DesiredSize.Width; 

                if (double.IsPositiveInfinity(element.PreviousConstraint.Height)) 
                    arrangeRect.Height = element.DesiredSize.Height; 
            }
 
            return arrangeRect;
        }

        private void invalidateTreeIfRecovering() 
        {
            if((_forceLayoutElement != null) || _gotException) 
            { 
                if(_forceLayoutElement != null)
                { 
                    markTreeDirty(_forceLayoutElement);
                }

                _forceLayoutElement = null; 
                _gotException = false;
            } 
        } 

        internal LayoutQueue MeasureQueue 
        {
            get
            {
                if(_measureQueue == null) 
                    _measureQueue = new InternalMeasureQueue();
                return _measureQueue; 
            } 
        }
 
        internal LayoutQueue ArrangeQueue
        {
            get
            { 
                if(_arrangeQueue == null)
                    _arrangeQueue = new InternalArrangeQueue(); 
                return _arrangeQueue; 
            }
        } 

        internal class InternalMeasureQueue: LayoutQueue
        {
            internal override void setRequest(UIElement e, Request r) 
            {
                e.MeasureRequest = r; 
            } 

            internal override Request getRequest(UIElement e) 
            {
                return e.MeasureRequest;
            }
 
            internal override bool canRelyOnParentRecalc(UIElement parent)
            { 
                return !parent.IsMeasureValid 
                    && !parent.MeasureInProgress; //if parent's measure is in progress, we might have passed this child already
            } 

            internal override void invalidate(UIElement e)
            {
                e.InvalidateMeasureInternal(); 
            }
 
        } 

 
        internal class InternalArrangeQueue: LayoutQueue
        {
            internal override void setRequest(UIElement e, Request r)
            { 
                e.ArrangeRequest = r;
            } 
 
            internal override Request getRequest(UIElement e)
            { 
                return e.ArrangeRequest;
            }

            internal override bool canRelyOnParentRecalc(UIElement parent) 
            {
                return !parent.IsArrangeValid 
                    && !parent.ArrangeInProgress; //if parent's arrange is in progress, we might have passed this child already 
            }
 
            internal override void invalidate(UIElement e)
            {
                e.InvalidateArrangeInternal();
            } 

        } 
 
        // delegate for dispatcher - keep it static so we don't allocate new ones.
        private static DispatcherOperationCallback _updateCallback = new DispatcherOperationCallback(UpdateLayoutCallback); 
        private static object UpdateLayoutCallback(object arg)
        {
            ContextLayoutManager ContextLayoutManager = arg as ContextLayoutManager;
            if(ContextLayoutManager != null) 
                ContextLayoutManager.UpdateLayout();
            return null; 
        } 

        //walks the list, fires events to alive handlers and removes dead ones 
        private void fireLayoutUpdateEvent()
        {
            //no reentrancy. It may happen if one of handlers calls UpdateLayout synchronously
            if(_inFireLayoutUpdated) return; 

            try 
            { 
                _inFireLayoutUpdated = true;
 
                LayoutEventList.ListItem [] copy = LayoutEvents.CopyToArray();

                for(int i=0; i PocketReserve) 
                {
                    _addRequest(e);
                }
                else 
                {
                    //walk up until we are the topmost UIElement in the tree. 
                    //on each step, mark the parent dirty and remove it from the queues 
                    //only leave a single node in the queue - the root of visual tree
                    while(e != null) 
                    {
                        UIElement p = e.GetUIParentWithinLayoutIsland();

                        invalidate(e); //invalidate in any case 

                        if (p != null) //not yet a root 
                        { 
                            Remove(e);
                        } 
                        else //root of visual tree
                        {
                            if (getRequest(e) == null)
                            { 
                                RemoveOrphans(e);
                                _addRequest(e); 
                            } 
                        }
                        e = p; 
                    }
                }

                layoutManager.NeedsRecalc(); 
            }
 
            internal void Remove(UIElement e) 
            {
                Request r = getRequest(e); 
                if(r == null) return;
                _removeRequest(r);
                setRequest(e, null);
            } 

            internal void RemoveOrphans(UIElement parent) 
            { 
                Request r = _head;
                while(r != null) 
                {
                    UIElement child = r.Target;
                    Request next = r.Next;
                    ulong parentTreeLevel = parent.TreeLevel; 

                    if(   (child.TreeLevel == parentTreeLevel + 1) 
                       && (child.GetUIParentWithinLayoutIsland() == parent)) 
                    {
                        _removeRequest(getRequest(child)); 
                        setRequest(child, null);
                    }

                    r = next; 
                }
            } 
 
            internal bool IsEmpty { get { return (_head == null); }}
 
            internal UIElement GetTopMost()
            {
                UIElement found = null;
                ulong treeLevel = ulong.MaxValue; 

                for(Request r = _head; r != null; r = r.Next) 
                { 
                    UIElement t = r.Target;
                    ulong l = t.TreeLevel; 

                    if(l < treeLevel)
                    {
                        treeLevel = l; 
                        found = r.Target;
                    } 
                } 

                return found; 
            }

            private void _removeRequest(Request entry)
            { 
                if(entry.Prev == null) _head = entry.Next;
                else entry.Prev.Next = entry.Next; 
 
                if(entry.Next != null) entry.Next.Prev = entry.Prev;
 
                ReuseRequest(entry);
            }

            private Request _getNewRequest(UIElement e) 
            {
                Request r; 
                if(_pocket != null) 
                {
                    r = _pocket; 
                    _pocket = r.Next;
                    _pocketSize--;
                    r.Next = r.Prev = null;
                } 
                else
                { 
                    ContextLayoutManager lm = ContextLayoutManager.From(e.Dispatcher); 
                    try
                    { 
                        r = new Request();
                    }
                    catch(System.OutOfMemoryException ex)
                    { 
                        if(lm != null)
                            lm.setForceLayout(e); 
                        throw ex; 
                    }
                } 

                r.Target = e;
                return r;
            } 

            private void ReuseRequest(Request r) 
            { 
                r.Target = null; //let target die
 
                if (_pocketSize < PocketCapacity)
                {
                    r.Next = _pocket;
                    _pocket = r; 
                    _pocketSize++;
                } 
            } 

            private Request _head; 
            private Request _pocket;
            private int     _pocketSize;
        }
    } 

    internal class LayoutEventList 
    { 
        //size of the pre-allocated free list
        private const int PocketCapacity = 153; 

        internal class ListItem: WeakReference
        {
            internal ListItem() : base(null) {} 
            internal ListItem Next;
            internal ListItem Prev; 
            internal bool     InUse; 
        }
 
        internal LayoutEventList()
        {
            ListItem t;
            for(int i=0; i

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK