LayoutManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / LayoutManager.cs / 1305600 / 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 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;

#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

            bool etwTracingEnabled = false;
            long perfElementID = 0; 
            const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf;
            if (!_isUpdating && EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info)) 
            { 
                etwTracingEnabled = true;
                perfElementID = PerfService.GetPerfElementID(this); 
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info,
                        perfElementID, EventTrace.LayoutSource.LayoutManager);
            }
 
            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; 
                        if (etwTracingEnabled)
                        { 
                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutAbort, etwKeywords, EventTrace.Level.Info, 0, cnt);
                        }
                        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.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, perfElementID); 
                    }
 
                    // 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.UtcNow;
                                } 
                                else 
                                {
                                    TimeSpan loopDuration = (DateTime.UtcNow - 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; 
                                        if (etwTracingEnabled) 
                                        {
                                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureAbort, etwKeywords, EventTrace.Level.Info, 
                                                   loopDuration.Milliseconds, loopCounter);
                                        }
                                        return;
                                    } 
                                }
                            } 
 
                            currentElement = MeasureQueue.GetTopMost();
 
                            if(currentElement == null) break; //exit if no more Measure candidates

                            if (currentElement.PreviousMeasureData != null)
                            { 
                                currentElement.MeasureData = currentElement.PreviousMeasureData;    // pass along MeasureData so it continues down the tree.
                            } 
                            currentElement.Measure(currentElement.PreviousConstraint); 
//dmitryt, 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.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, loopCounter); 
                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, perfElementID);
                        } 


#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.UtcNow;
                                } 
                                else 
                                {
                                    TimeSpan loopDuration = (DateTime.UtcNow - 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; 
                                        if (etwTracingEnabled) 
                                        {
                                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeAbort, etwKeywords, EventTrace.Level.Info, 
                                                   loopDuration.Milliseconds, loopCounter);
                                        }
                                        return;
                                    } 
                                }
                            } 
 
                            currentElement = ArrangeQueue.GetTopMost();
 
                            if(currentElement == null) break; //exit if no more Measure candidates

                            Rect finalRect = getProperArrangeRect(currentElement);
 
                            currentElement.Arrange(finalRect);
//dmitryt, 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.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, loopCounter);
                        } 
 
#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 [....]. 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)
                { 
                    if (etwTracingEnabled)
                    {
                        EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutException, etwKeywords, EventTrace.Level.Info, PerfService.GetPerfElementID(currentElement));
                    } 

                    //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);
                } 
            } 

            MS.Internal.Text.TextInterface.Font.ResetFontFaceCache(); 
            MS.Internal.FontCache.BufferCache.Reset();

            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info);
            } 
 
#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; 

            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, EventTrace.Event.WClientLayoutFireLayoutUpdatedBegin); 
            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
    /// 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;

#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

            bool etwTracingEnabled = false;
            long perfElementID = 0; 
            const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf;
            if (!_isUpdating && EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info)) 
            { 
                etwTracingEnabled = true;
                perfElementID = PerfService.GetPerfElementID(this); 
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info,
                        perfElementID, EventTrace.LayoutSource.LayoutManager);
            }
 
            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; 
                        if (etwTracingEnabled)
                        { 
                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutAbort, etwKeywords, EventTrace.Level.Info, 0, cnt);
                        }
                        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.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, perfElementID); 
                    }
 
                    // 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.UtcNow;
                                } 
                                else 
                                {
                                    TimeSpan loopDuration = (DateTime.UtcNow - 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; 
                                        if (etwTracingEnabled) 
                                        {
                                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureAbort, etwKeywords, EventTrace.Level.Info, 
                                                   loopDuration.Milliseconds, loopCounter);
                                        }
                                        return;
                                    } 
                                }
                            } 
 
                            currentElement = MeasureQueue.GetTopMost();
 
                            if(currentElement == null) break; //exit if no more Measure candidates

                            if (currentElement.PreviousMeasureData != null)
                            { 
                                currentElement.MeasureData = currentElement.PreviousMeasureData;    // pass along MeasureData so it continues down the tree.
                            } 
                            currentElement.Measure(currentElement.PreviousConstraint); 
//dmitryt, 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.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, loopCounter); 
                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, perfElementID);
                        } 


#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.UtcNow;
                                } 
                                else 
                                {
                                    TimeSpan loopDuration = (DateTime.UtcNow - 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; 
                                        if (etwTracingEnabled) 
                                        {
                                            EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeAbort, etwKeywords, EventTrace.Level.Info, 
                                                   loopDuration.Milliseconds, loopCounter);
                                        }
                                        return;
                                    } 
                                }
                            } 
 
                            currentElement = ArrangeQueue.GetTopMost();
 
                            if(currentElement == null) break; //exit if no more Measure candidates

                            Rect finalRect = getProperArrangeRect(currentElement);
 
                            currentElement.Arrange(finalRect);
//dmitryt, 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.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, loopCounter);
                        } 
 
#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 [....]. 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)
                { 
                    if (etwTracingEnabled)
                    {
                        EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutException, etwKeywords, EventTrace.Level.Info, PerfService.GetPerfElementID(currentElement));
                    } 

                    //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);
                } 
            } 

            MS.Internal.Text.TextInterface.Font.ResetFontFaceCache(); 
            MS.Internal.FontCache.BufferCache.Reset();

            if (etwTracingEnabled)
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info);
            } 
 
#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; 

            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, EventTrace.Event.WClientLayoutFireLayoutUpdatedBegin); 
            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