LayoutManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / System / Windows / LayoutManager.cs / 1 / 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 

                            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.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);
//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.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 [....]. 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
    /// 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 

                            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.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);
//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.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 [....]. 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