Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / 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; is_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; iPocketReserve) { _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; is_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; iPocketReserve) { _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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SID.cs
- Utils.cs
- TextDocumentView.cs
- InteropBitmapSource.cs
- StreamMarshaler.cs
- DelayDesigner.cs
- Rect.cs
- XPathParser.cs
- TitleStyle.cs
- ServiceOperation.cs
- InstanceOwner.cs
- PipeStream.cs
- FormViewUpdatedEventArgs.cs
- ArrayTypeMismatchException.cs
- DataGridViewColumnEventArgs.cs
- SafeCryptoHandles.cs
- EntityWrapper.cs
- DispatchChannelSink.cs
- StrokeNodeOperations.cs
- CannotUnloadAppDomainException.cs
- IntSumAggregationOperator.cs
- DrawListViewItemEventArgs.cs
- AxHost.cs
- XpsResource.cs
- StrokeCollection.cs
- DocumentReference.cs
- FixedPage.cs
- keycontainerpermission.cs
- SharedStatics.cs
- SeparatorAutomationPeer.cs
- SHA256CryptoServiceProvider.cs
- ProfessionalColorTable.cs
- ConstraintStruct.cs
- InteropEnvironment.cs
- LookupTables.cs
- DirectoryObjectSecurity.cs
- categoryentry.cs
- ClientTarget.cs
- XmlSchemaGroupRef.cs
- ToolStripCollectionEditor.cs
- RayMeshGeometry3DHitTestResult.cs
- ResourceDisplayNameAttribute.cs
- TextMarkerSource.cs
- XmlCharType.cs
- ManagementClass.cs
- ValidationSummary.cs
- HwndAppCommandInputProvider.cs
- PropertyBuilder.cs
- PropertySegmentSerializationProvider.cs
- WebPartPersonalization.cs
- FigureHelper.cs
- CqlParser.cs
- EnumBuilder.cs
- CollectionDataContract.cs
- EventProviderBase.cs
- ScrollViewerAutomationPeer.cs
- ControlAdapter.cs
- DesignerActionGlyph.cs
- ProxyGenerationError.cs
- DynamicAttribute.cs
- PageOutputQuality.cs
- SqlTransaction.cs
- DesignerProperties.cs
- TemplateBuilder.cs
- DataGridViewSelectedRowCollection.cs
- ConnectionsZoneAutoFormat.cs
- BooleanExpr.cs
- UnsafeNativeMethods.cs
- ContextMenuStrip.cs
- ComplexObject.cs
- SctClaimDictionary.cs
- ResourceDisplayNameAttribute.cs
- DbParameterCollectionHelper.cs
- SplineKeyFrames.cs
- PageRequestManager.cs
- FormatterConverter.cs
- DataRowCollection.cs
- SocketInformation.cs
- ComplexLine.cs
- QuadraticBezierSegment.cs
- CopyCodeAction.cs
- WebPartCollection.cs
- BaseUriWithWildcard.cs
- brushes.cs
- UserControlAutomationPeer.cs
- ConnectionStringSettingsCollection.cs
- SerializationTrace.cs
- IRCollection.cs
- ConnectionsZone.cs
- StatusBar.cs
- PublisherIdentityPermission.cs
- ComNativeDescriptor.cs
- FixedTextBuilder.cs
- PolyQuadraticBezierSegment.cs
- TextStore.cs
- XsdBuildProvider.cs
- GreenMethods.cs
- DataControlFieldCollection.cs
- DataServiceQueryException.cs
- DataDocumentXPathNavigator.cs