Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / LayoutManager.cs / 1305600 / LayoutManager.cs
/****************************************************************************\ * * File: ContextLayoutManager.cs * * Defines a top-level ContextLayoutManager - a layout dirtiness tracking/clearing system. * * Copyright (C) 2001 by Microsoft Corporation. All rights reserved. * \***************************************************************************/ using System; using System.Windows.Threading; using System.Collections; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Automation.Peers; using MS.Internal; using MS.Utility; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace System.Windows { ////// Top-level ContextLayoutManager object. Manages the layout update and layout dirty state. /// internal sealed class ContextLayoutManager : DispatcherObject { internal ContextLayoutManager() { _shutdownHandler = new EventHandler(this.OnDispatcherShutdown); Dispatcher.ShutdownFinished += _shutdownHandler; } void OnDispatcherShutdown(object sender, EventArgs e) { if(_shutdownHandler != null) Dispatcher.ShutdownFinished -= _shutdownHandler; _shutdownHandler = null; _layoutEvents = null; _measureQueue = null; _arrangeQueue = null; _sizeChangedChain = null; _isDead = true; } ////// The way to obtain ContextLayoutManager associated with particular Dispatcher. /// /// A dispatcher for which ContextLayoutManager is queried. /// There is only one ContextLayoutManager associuated with all elements in a single context ///ContextLayoutManager internal static ContextLayoutManager From(Dispatcher dispatcher) { ContextLayoutManager lm = dispatcher.Reserved3 as ContextLayoutManager; if(lm == null) { if(Dispatcher.CurrentDispatcher != dispatcher) { throw new InvalidOperationException(); } lm = new ContextLayoutManager(); dispatcher.Reserved3 = lm; } return lm; } private void setForceLayout(UIElement e) { _forceLayoutElement = e; } private void markTreeDirty(UIElement e) { //walk up until we are the topmost UIElement in the tree. while(true) { UIElement p = e.GetUIParentNo3DTraversal() as UIElement; if(p == null) break; e = p; } markTreeDirtyHelper(e); MeasureQueue.Add(e); ArrangeQueue.Add(e); } private void markTreeDirtyHelper(Visual v) { //now walk down and mark all UIElements dirty if(v != null) { if(v.CheckFlagsAnd(VisualFlags.IsUIElement)) { UIElement uie = ((UIElement)v); uie.InvalidateMeasureInternal(); uie.InvalidateArrangeInternal(); } //walk children doing the same, don't stop if they are already dirty since there can //be insulated dirty islands below int cnt = v.InternalVisualChildrenCount; for(int i=0; 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; #if DEBUG_CLR_MEM bool clrTracingEnabled = false; // Start over with the Measure and arrange counters for this layout pass int measureCLRPass = 0; int arrangeCLRPass = 0; if (CLRProfilerControl.ProcessIsUnderCLRProfiler) { clrTracingEnabled = true; if (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance) { ++_layoutCLRPass; CLRProfilerControl.CLRLogWriteLine("Begin_Layout_{0}", _layoutCLRPass); } } #endif // DEBUG_CLR_MEM bool etwTracingEnabled = false; long perfElementID = 0; const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf; if (!_isUpdating && EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info)) { etwTracingEnabled = true; perfElementID = PerfService.GetPerfElementID(this); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, perfElementID, EventTrace.LayoutSource.LayoutManager); } int cnt = 0; bool gotException = true; UIElement currentElement = null; try { invalidateTreeIfRecovering(); while(hasDirtiness || _firePostLayoutEvents) { if(++cnt > 153) { //loop detected. Lets go over to background to let input/user to correct the situation. //most frequently, we get such a loop as a result of input detecting a mouse in the "bad spot" //and some event handler oscillating a layout-affecting property depending on hittest result //of the mouse. Going over to background will not break the loopp but will allow user to //move the mouse so that it goes out of the "bad spot". Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); currentElement = null; gotException = false; if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutAbort, etwKeywords, EventTrace.Level.Info, 0, cnt); } return; } //this flag stops posting update requests to MediaContext - we are already in one //note that _isInUpdateLayout is close but different - _isInUpdateLayout is reset //before firing LayoutUpdated so that event handlers could call UpdateLayout but //still could not cause posting of MediaContext work item. Posting MediaContext workitem //causes infinite loop in MediaContext. _isUpdating = true; _isInUpdateLayout = true; #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)) { ++measureCLRPass; CLRProfilerControl.CLRLogWriteLine("Begin_Measure_{0}_{1}", _layoutCLRPass, measureCLRPass); } #endif // DEBUG_CLR_MEM if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, perfElementID); } // Disable processing of the queue during blocking operations to prevent unrelated reentrancy. using(Dispatcher.DisableProcessing()) { //loop for Measure //We limit the number of loops here by time - normally, all layout //calculations should be done by this time, this limit is here for //emergency, "infinite loop" scenarios - yielding in this case will //provide user with ability to continue to interact with the app, even though //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one //and it will be impossible to save results or even close the window. int loopCounter = 0; DateTime loopStartTime = new DateTime(0); while(true) { if(++loopCounter > 153) { loopCounter = 0; //first bunch of iterations is free, then we start count time //this way, we don't call DateTime.Now in most layout updates if(loopStartTime.Ticks == 0) { loopStartTime = DateTime.UtcNow; } else { TimeSpan loopDuration = (DateTime.UtcNow - loopStartTime); if(loopDuration.Milliseconds > 153*2) // 153*2 = magic*science { //loop detected. Lets go over to background to let input work. Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); currentElement = null; gotException = false; if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureAbort, etwKeywords, EventTrace.Level.Info, loopDuration.Milliseconds, loopCounter); } return; } } } currentElement = MeasureQueue.GetTopMost(); if(currentElement == null) break; //exit if no more Measure candidates if (currentElement.PreviousMeasureData != null) { currentElement.MeasureData = currentElement.PreviousMeasureData; // pass along MeasureData so it continues down the tree. } currentElement.Measure(currentElement.PreviousConstraint); //dmitryt, bug 1150880: not clear why this is needed, remove for now //if the parent was just computed, the chidlren should be clean. If they are not clean and in the queue //that means that there is cross-tree dependency and they most likely shodul be updated by themselves. // MeasureQueue.RemoveOrphans(currentElement); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, loopCounter); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, perfElementID); } #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)) { CLRProfilerControl.CLRLogWriteLine("End_Measure_{0}_{1}", _layoutCLRPass, measureCLRPass); ++arrangeCLRPass; CLRProfilerControl.CLRLogWriteLine("Begin_Arrange_{0}_{1}", _layoutCLRPass, arrangeCLRPass); } #endif // DEBUG_CLR_MEM //loop for Arrange //if Arrange dirtied the tree go clean it again //We limit the number of loops here by time - normally, all layout //calculations should be done by this time, this limit is here for //emergency, "infinite loop" scenarios - yielding in this case will //provide user with ability to continue to interact with the app, even though //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one //and it will be impossible to save results or even close the window. loopCounter = 0; loopStartTime = new DateTime(0); while(MeasureQueue.IsEmpty) { if(++loopCounter > 153) { loopCounter = 0; //first bunch of iterations is free, then we start count time //this way, we don't call DateTime.Now in most layout updates if(loopStartTime.Ticks == 0) { loopStartTime = DateTime.UtcNow; } else { TimeSpan loopDuration = (DateTime.UtcNow - loopStartTime); if(loopDuration.Milliseconds > 153*2) // 153*2 = magic*science { //loop detected. Lets go over to background to let input work. Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); currentElement = null; gotException = false; if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeAbort, etwKeywords, EventTrace.Level.Info, loopDuration.Milliseconds, loopCounter); } return; } } } currentElement = ArrangeQueue.GetTopMost(); if(currentElement == null) break; //exit if no more Measure candidates Rect finalRect = getProperArrangeRect(currentElement); currentElement.Arrange(finalRect); //dmitryt, bug 1150880: not clear why this is needed, remove for now //if the parent was just computed, the chidlren should be clean. If they are not clean and in the queue //that means that there is cross-tree dependency and they most likely shodul be updated by themselves. // ArrangeQueue.RemoveOrphans(currentElement); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, loopCounter); } #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)) { CLRProfilerControl.CLRLogWriteLine("End_Arrange_{0}_{1}", _layoutCLRPass, arrangeCLRPass); } #endif // DEBUG_CLR_MEM //if Arrange dirtied the tree go clean it again //it is not neccesary to check ArrangeQueue sicnce we just exited from Arrange loop if(!MeasureQueue.IsEmpty) continue; //let LayoutUpdated handlers to call UpdateLayout //note that it means we can get reentrancy into UpdateLayout past this point, //if any of event handlers call UpdateLayout [....]. Need to protect from reentrancy //in the firing methods below. _isInUpdateLayout = false; } fireSizeChangedEvents(); if(hasDirtiness) continue; fireLayoutUpdateEvent(); if(hasDirtiness) continue; fireAutomationEvents(); } currentElement = null; gotException = false; } finally { _isUpdating = false; _layoutRequestPosted = false; _isInUpdateLayout = false; if(gotException) { if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutException, etwKeywords, EventTrace.Level.Info, PerfService.GetPerfElementID(currentElement)); } //set indicator _gotException = true; _forceLayoutElement = currentElement; //make attempt to request the subsequent layout calc //some exception handler schemas use Idle priorities to //wait until dust settles. Then they correct the issue noted in the exception handler. //We don't want to attempt to re-do the operation on the priority higher then that. Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, _updateLayoutBackground, this); } } MS.Internal.Text.TextInterface.Font.ResetFontFaceCache(); MS.Internal.FontCache.BufferCache.Reset(); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance)) { CLRProfilerControl.CLRLogWriteLine("End_Layout_{0}", _layoutCLRPass); } #endif // DEBUG_CLR_MEM } private Rect getProperArrangeRect(UIElement element) { Rect arrangeRect = element.PreviousArrangeRect; // ELements without a parent (top level) get Arrange at DesiredSize // if they were measured "to content" (as infinity indicates). // If we arrange the element that is temporarily disconnected // so it is not a top-level one, the assumption is that it will be // layout-invalidated and/or recomputed by the parent when reconnected. if (element.GetUIParentNo3DTraversal() == null) { arrangeRect.X = arrangeRect.Y = 0; if (double.IsPositiveInfinity(element.PreviousConstraint.Width)) arrangeRect.Width = element.DesiredSize.Width; if (double.IsPositiveInfinity(element.PreviousConstraint.Height)) arrangeRect.Height = element.DesiredSize.Height; } return arrangeRect; } private void invalidateTreeIfRecovering() { if((_forceLayoutElement != null) || _gotException) { if(_forceLayoutElement != null) { markTreeDirty(_forceLayoutElement); } _forceLayoutElement = null; _gotException = false; } } internal LayoutQueue MeasureQueue { get { if(_measureQueue == null) _measureQueue = new InternalMeasureQueue(); return _measureQueue; } } internal LayoutQueue ArrangeQueue { get { if(_arrangeQueue == null) _arrangeQueue = new InternalArrangeQueue(); return _arrangeQueue; } } internal class InternalMeasureQueue: LayoutQueue { internal override void setRequest(UIElement e, Request r) { e.MeasureRequest = r; } internal override Request getRequest(UIElement e) { return e.MeasureRequest; } internal override bool canRelyOnParentRecalc(UIElement parent) { return !parent.IsMeasureValid && !parent.MeasureInProgress; //if parent's measure is in progress, we might have passed this child already } internal override void invalidate(UIElement e) { e.InvalidateMeasureInternal(); } } internal class InternalArrangeQueue: LayoutQueue { internal override void setRequest(UIElement e, Request r) { e.ArrangeRequest = r; } internal override Request getRequest(UIElement e) { return e.ArrangeRequest; } internal override bool canRelyOnParentRecalc(UIElement parent) { return !parent.IsArrangeValid && !parent.ArrangeInProgress; //if parent's arrange is in progress, we might have passed this child already } internal override void invalidate(UIElement e) { e.InvalidateArrangeInternal(); } } // delegate for dispatcher - keep it static so we don't allocate new ones. private static DispatcherOperationCallback _updateCallback = new DispatcherOperationCallback(UpdateLayoutCallback); private static object UpdateLayoutCallback(object arg) { ContextLayoutManager ContextLayoutManager = arg as ContextLayoutManager; if(ContextLayoutManager != null) ContextLayoutManager.UpdateLayout(); return null; } //walks the list, fires events to alive handlers and removes dead ones private void fireLayoutUpdateEvent() { //no reentrancy. It may happen if one of handlers calls UpdateLayout synchronously if(_inFireLayoutUpdated) return; EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, EventTrace.Event.WClientLayoutFireLayoutUpdatedBegin); try { _inFireLayoutUpdated = true; LayoutEventList.ListItem [] copy = LayoutEvents.CopyToArray(); for(int i=0; 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; #if DEBUG_CLR_MEM bool clrTracingEnabled = false; // Start over with the Measure and arrange counters for this layout pass int measureCLRPass = 0; int arrangeCLRPass = 0; if (CLRProfilerControl.ProcessIsUnderCLRProfiler) { clrTracingEnabled = true; if (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance) { ++_layoutCLRPass; CLRProfilerControl.CLRLogWriteLine("Begin_Layout_{0}", _layoutCLRPass); } } #endif // DEBUG_CLR_MEM bool etwTracingEnabled = false; long perfElementID = 0; const EventTrace.Keyword etwKeywords = EventTrace.Keyword.KeywordLayout | EventTrace.Keyword.KeywordPerf; if (!_isUpdating && EventTrace.IsEnabled(etwKeywords, EventTrace.Level.Info)) { etwTracingEnabled = true; perfElementID = PerfService.GetPerfElementID(this); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutBegin, etwKeywords, EventTrace.Level.Info, perfElementID, EventTrace.LayoutSource.LayoutManager); } int cnt = 0; bool gotException = true; UIElement currentElement = null; try { invalidateTreeIfRecovering(); while(hasDirtiness || _firePostLayoutEvents) { if(++cnt > 153) { //loop detected. Lets go over to background to let input/user to correct the situation. //most frequently, we get such a loop as a result of input detecting a mouse in the "bad spot" //and some event handler oscillating a layout-affecting property depending on hittest result //of the mouse. Going over to background will not break the loopp but will allow user to //move the mouse so that it goes out of the "bad spot". Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); currentElement = null; gotException = false; if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutAbort, etwKeywords, EventTrace.Level.Info, 0, cnt); } return; } //this flag stops posting update requests to MediaContext - we are already in one //note that _isInUpdateLayout is close but different - _isInUpdateLayout is reset //before firing LayoutUpdated so that event handlers could call UpdateLayout but //still could not cause posting of MediaContext work item. Posting MediaContext workitem //causes infinite loop in MediaContext. _isUpdating = true; _isInUpdateLayout = true; #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)) { ++measureCLRPass; CLRProfilerControl.CLRLogWriteLine("Begin_Measure_{0}_{1}", _layoutCLRPass, measureCLRPass); } #endif // DEBUG_CLR_MEM if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureBegin, etwKeywords, EventTrace.Level.Info, perfElementID); } // Disable processing of the queue during blocking operations to prevent unrelated reentrancy. using(Dispatcher.DisableProcessing()) { //loop for Measure //We limit the number of loops here by time - normally, all layout //calculations should be done by this time, this limit is here for //emergency, "infinite loop" scenarios - yielding in this case will //provide user with ability to continue to interact with the app, even though //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one //and it will be impossible to save results or even close the window. int loopCounter = 0; DateTime loopStartTime = new DateTime(0); while(true) { if(++loopCounter > 153) { loopCounter = 0; //first bunch of iterations is free, then we start count time //this way, we don't call DateTime.Now in most layout updates if(loopStartTime.Ticks == 0) { loopStartTime = DateTime.UtcNow; } else { TimeSpan loopDuration = (DateTime.UtcNow - loopStartTime); if(loopDuration.Milliseconds > 153*2) // 153*2 = magic*science { //loop detected. Lets go over to background to let input work. Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); currentElement = null; gotException = false; if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureAbort, etwKeywords, EventTrace.Level.Info, loopDuration.Milliseconds, loopCounter); } return; } } } currentElement = MeasureQueue.GetTopMost(); if(currentElement == null) break; //exit if no more Measure candidates if (currentElement.PreviousMeasureData != null) { currentElement.MeasureData = currentElement.PreviousMeasureData; // pass along MeasureData so it continues down the tree. } currentElement.Measure(currentElement.PreviousConstraint); //dmitryt, bug 1150880: not clear why this is needed, remove for now //if the parent was just computed, the chidlren should be clean. If they are not clean and in the queue //that means that there is cross-tree dependency and they most likely shodul be updated by themselves. // MeasureQueue.RemoveOrphans(currentElement); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureEnd, etwKeywords, EventTrace.Level.Info, loopCounter); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeBegin, etwKeywords, EventTrace.Level.Info, perfElementID); } #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)) { CLRProfilerControl.CLRLogWriteLine("End_Measure_{0}_{1}", _layoutCLRPass, measureCLRPass); ++arrangeCLRPass; CLRProfilerControl.CLRLogWriteLine("Begin_Arrange_{0}_{1}", _layoutCLRPass, arrangeCLRPass); } #endif // DEBUG_CLR_MEM //loop for Arrange //if Arrange dirtied the tree go clean it again //We limit the number of loops here by time - normally, all layout //calculations should be done by this time, this limit is here for //emergency, "infinite loop" scenarios - yielding in this case will //provide user with ability to continue to interact with the app, even though //it will be sluggish. If we don't yield here, the loop is goign to be a deadly one //and it will be impossible to save results or even close the window. loopCounter = 0; loopStartTime = new DateTime(0); while(MeasureQueue.IsEmpty) { if(++loopCounter > 153) { loopCounter = 0; //first bunch of iterations is free, then we start count time //this way, we don't call DateTime.Now in most layout updates if(loopStartTime.Ticks == 0) { loopStartTime = DateTime.UtcNow; } else { TimeSpan loopDuration = (DateTime.UtcNow - loopStartTime); if(loopDuration.Milliseconds > 153*2) // 153*2 = magic*science { //loop detected. Lets go over to background to let input work. Dispatcher.BeginInvoke(DispatcherPriority.Background, _updateLayoutBackground, this); currentElement = null; gotException = false; if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeAbort, etwKeywords, EventTrace.Level.Info, loopDuration.Milliseconds, loopCounter); } return; } } } currentElement = ArrangeQueue.GetTopMost(); if(currentElement == null) break; //exit if no more Measure candidates Rect finalRect = getProperArrangeRect(currentElement); currentElement.Arrange(finalRect); //dmitryt, bug 1150880: not clear why this is needed, remove for now //if the parent was just computed, the chidlren should be clean. If they are not clean and in the queue //that means that there is cross-tree dependency and they most likely shodul be updated by themselves. // ArrangeQueue.RemoveOrphans(currentElement); } if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeEnd, etwKeywords, EventTrace.Level.Info, loopCounter); } #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Verbose)) { CLRProfilerControl.CLRLogWriteLine("End_Arrange_{0}_{1}", _layoutCLRPass, arrangeCLRPass); } #endif // DEBUG_CLR_MEM //if Arrange dirtied the tree go clean it again //it is not neccesary to check ArrangeQueue sicnce we just exited from Arrange loop if(!MeasureQueue.IsEmpty) continue; //let LayoutUpdated handlers to call UpdateLayout //note that it means we can get reentrancy into UpdateLayout past this point, //if any of event handlers call UpdateLayout [....]. Need to protect from reentrancy //in the firing methods below. _isInUpdateLayout = false; } fireSizeChangedEvents(); if(hasDirtiness) continue; fireLayoutUpdateEvent(); if(hasDirtiness) continue; fireAutomationEvents(); } currentElement = null; gotException = false; } finally { _isUpdating = false; _layoutRequestPosted = false; _isInUpdateLayout = false; if(gotException) { if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutException, etwKeywords, EventTrace.Level.Info, PerfService.GetPerfElementID(currentElement)); } //set indicator _gotException = true; _forceLayoutElement = currentElement; //make attempt to request the subsequent layout calc //some exception handler schemas use Idle priorities to //wait until dust settles. Then they correct the issue noted in the exception handler. //We don't want to attempt to re-do the operation on the priority higher then that. Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, _updateLayoutBackground, this); } } MS.Internal.Text.TextInterface.Font.ResetFontFaceCache(); MS.Internal.FontCache.BufferCache.Reset(); if (etwTracingEnabled) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutEnd, etwKeywords, EventTrace.Level.Info); } #if DEBUG_CLR_MEM if (clrTracingEnabled && (CLRProfilerControl.CLRLoggingLevel >= CLRProfilerControl.CLRLogState.Performance)) { CLRProfilerControl.CLRLogWriteLine("End_Layout_{0}", _layoutCLRPass); } #endif // DEBUG_CLR_MEM } private Rect getProperArrangeRect(UIElement element) { Rect arrangeRect = element.PreviousArrangeRect; // ELements without a parent (top level) get Arrange at DesiredSize // if they were measured "to content" (as infinity indicates). // If we arrange the element that is temporarily disconnected // so it is not a top-level one, the assumption is that it will be // layout-invalidated and/or recomputed by the parent when reconnected. if (element.GetUIParentNo3DTraversal() == null) { arrangeRect.X = arrangeRect.Y = 0; if (double.IsPositiveInfinity(element.PreviousConstraint.Width)) arrangeRect.Width = element.DesiredSize.Width; if (double.IsPositiveInfinity(element.PreviousConstraint.Height)) arrangeRect.Height = element.DesiredSize.Height; } return arrangeRect; } private void invalidateTreeIfRecovering() { if((_forceLayoutElement != null) || _gotException) { if(_forceLayoutElement != null) { markTreeDirty(_forceLayoutElement); } _forceLayoutElement = null; _gotException = false; } } internal LayoutQueue MeasureQueue { get { if(_measureQueue == null) _measureQueue = new InternalMeasureQueue(); return _measureQueue; } } internal LayoutQueue ArrangeQueue { get { if(_arrangeQueue == null) _arrangeQueue = new InternalArrangeQueue(); return _arrangeQueue; } } internal class InternalMeasureQueue: LayoutQueue { internal override void setRequest(UIElement e, Request r) { e.MeasureRequest = r; } internal override Request getRequest(UIElement e) { return e.MeasureRequest; } internal override bool canRelyOnParentRecalc(UIElement parent) { return !parent.IsMeasureValid && !parent.MeasureInProgress; //if parent's measure is in progress, we might have passed this child already } internal override void invalidate(UIElement e) { e.InvalidateMeasureInternal(); } } internal class InternalArrangeQueue: LayoutQueue { internal override void setRequest(UIElement e, Request r) { e.ArrangeRequest = r; } internal override Request getRequest(UIElement e) { return e.ArrangeRequest; } internal override bool canRelyOnParentRecalc(UIElement parent) { return !parent.IsArrangeValid && !parent.ArrangeInProgress; //if parent's arrange is in progress, we might have passed this child already } internal override void invalidate(UIElement e) { e.InvalidateArrangeInternal(); } } // delegate for dispatcher - keep it static so we don't allocate new ones. private static DispatcherOperationCallback _updateCallback = new DispatcherOperationCallback(UpdateLayoutCallback); private static object UpdateLayoutCallback(object arg) { ContextLayoutManager ContextLayoutManager = arg as ContextLayoutManager; if(ContextLayoutManager != null) ContextLayoutManager.UpdateLayout(); return null; } //walks the list, fires events to alive handlers and removes dead ones private void fireLayoutUpdateEvent() { //no reentrancy. It may happen if one of handlers calls UpdateLayout synchronously if(_inFireLayoutUpdated) return; EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, EventTrace.Event.WClientLayoutFireLayoutUpdatedBegin); try { _inFireLayoutUpdated = true; LayoutEventList.ListItem [] copy = LayoutEvents.CopyToArray(); for(int i=0; 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
- GridErrorDlg.cs
- _SSPISessionCache.cs
- ProbeDuplex11AsyncResult.cs
- ElementAction.cs
- PerspectiveCamera.cs
- BindValidator.cs
- Number.cs
- KnownTypes.cs
- ExtendedPropertyDescriptor.cs
- PeerCredential.cs
- EditorPart.cs
- XomlCompilerHelpers.cs
- SqlBulkCopyColumnMapping.cs
- XmlDomTextWriter.cs
- ParameterElement.cs
- EarlyBoundInfo.cs
- DbDataSourceEnumerator.cs
- FontFaceLayoutInfo.cs
- XmlWellformedWriter.cs
- Profiler.cs
- SecurityMessageProperty.cs
- TableItemPattern.cs
- DataGridBoolColumn.cs
- UpdateEventArgs.cs
- GlobalItem.cs
- ChannelParameterCollection.cs
- PaperSize.cs
- WebDisplayNameAttribute.cs
- sqlcontext.cs
- FacetValueContainer.cs
- ItemMap.cs
- LabelAutomationPeer.cs
- ObjectStateFormatter.cs
- MsmqIntegrationSecurityMode.cs
- MemberDomainMap.cs
- SID.cs
- COSERVERINFO.cs
- ActivityTypeResolver.xaml.cs
- HtmlInputReset.cs
- GroupByQueryOperator.cs
- CodeObject.cs
- ErrorEventArgs.cs
- BamlLocalizerErrorNotifyEventArgs.cs
- Rijndael.cs
- EventListenerClientSide.cs
- SystemColors.cs
- StorageAssociationSetMapping.cs
- MethodRental.cs
- ToolStripItemTextRenderEventArgs.cs
- GestureRecognizer.cs
- EventDescriptor.cs
- ZipIOLocalFileHeader.cs
- ObjectConverter.cs
- BuildManagerHost.cs
- SchemaImporterExtensionElement.cs
- ZoneLinkButton.cs
- SizeAnimationClockResource.cs
- SQLInt16.cs
- XmlSchemaSimpleType.cs
- CommandManager.cs
- XmlRootAttribute.cs
- XmlReflectionImporter.cs
- GridItem.cs
- QueryReaderSettings.cs
- AnimationClockResource.cs
- BooleanToVisibilityConverter.cs
- EncoderReplacementFallback.cs
- RijndaelManaged.cs
- StructuralObject.cs
- WorkItem.cs
- SystemInfo.cs
- DataGridBeginningEditEventArgs.cs
- CurrencyManager.cs
- KeyPressEvent.cs
- Container.cs
- ThreadExceptionDialog.cs
- BigInt.cs
- COM2FontConverter.cs
- ConfigXmlDocument.cs
- unsafenativemethodsother.cs
- XmlSchemaCompilationSettings.cs
- XmlSchemaIdentityConstraint.cs
- TextProviderWrapper.cs
- ConfigXmlWhitespace.cs
- PageThemeParser.cs
- CustomAssemblyResolver.cs
- ClassHandlersStore.cs
- ServiceBusyException.cs
- InputMethodStateChangeEventArgs.cs
- objectresult_tresulttype.cs
- ScriptReferenceBase.cs
- CellQuery.cs
- Size3D.cs
- AsyncCompletedEventArgs.cs
- EpmSyndicationContentSerializer.cs
- SymbolType.cs
- LayoutTable.cs
- CodeCatchClauseCollection.cs
- HashCryptoHandle.cs
- TracedNativeMethods.cs