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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SecurityTokenException.cs
- InvalidDataException.cs
- Int32RectValueSerializer.cs
- CmsInterop.cs
- ToolStripDropDownButton.cs
- Clipboard.cs
- TableStyle.cs
- ExpressionBuilderCollection.cs
- RangeEnumerable.cs
- HtmlGenericControl.cs
- X509IssuerSerialKeyIdentifierClause.cs
- ItemsPanelTemplate.cs
- StickyNoteContentControl.cs
- HttpCapabilitiesBase.cs
- XmlUtil.cs
- ThumbButtonInfoCollection.cs
- CheckBoxRenderer.cs
- ButtonColumn.cs
- DesignerWebPartChrome.cs
- AspNetSynchronizationContext.cs
- QueuePropertyVariants.cs
- WorkflowFormatterBehavior.cs
- HtmlControl.cs
- Frame.cs
- PropertyDescriptor.cs
- TemplatedAdorner.cs
- SafeTokenHandle.cs
- ArrayElementGridEntry.cs
- XmlObjectSerializerReadContext.cs
- WebPartMenu.cs
- ObfuscationAttribute.cs
- DBSqlParserTableCollection.cs
- TrackPoint.cs
- NetworkInformationException.cs
- XmlStringTable.cs
- IdentityHolder.cs
- HashSetEqualityComparer.cs
- Int32CAMarshaler.cs
- ListBoxItem.cs
- MemberAccessException.cs
- Enlistment.cs
- AddingNewEventArgs.cs
- QueryOutputWriter.cs
- IsolatedStorageFileStream.cs
- NamedPipeProcessProtocolHandler.cs
- TimeSpanConverter.cs
- SkipQueryOptionExpression.cs
- MimeTypeMapper.cs
- XmlnsPrefixAttribute.cs
- InputEventArgs.cs
- OracleTransaction.cs
- EncodingDataItem.cs
- WindowHideOrCloseTracker.cs
- StrokeFIndices.cs
- TreePrinter.cs
- TrustSection.cs
- MeasurementDCInfo.cs
- EqualityArray.cs
- DoubleCollectionValueSerializer.cs
- RoutingBehavior.cs
- XamlStyleSerializer.cs
- MergeLocalizationDirectives.cs
- CoreSwitches.cs
- SqlCacheDependencyDatabase.cs
- TokenizerHelper.cs
- ExpressionConverter.cs
- QilPatternVisitor.cs
- DatePickerDateValidationErrorEventArgs.cs
- Page.cs
- SQLMoney.cs
- RemoteDebugger.cs
- GroupBoxAutomationPeer.cs
- NegotiateStream.cs
- XmlWriter.cs
- SqlCommandSet.cs
- Rect.cs
- DbException.cs
- ISAPIWorkerRequest.cs
- RuntimeVariableList.cs
- TextDecorationCollection.cs
- VirtualDirectoryMapping.cs
- NetworkStream.cs
- LinqToSqlWrapper.cs
- LinqDataSourceSelectEventArgs.cs
- PcmConverter.cs
- FrameDimension.cs
- Listbox.cs
- FontNamesConverter.cs
- SelectionUIHandler.cs
- Zone.cs
- FactoryMaker.cs
- DiagnosticTraceSource.cs
- ToggleProviderWrapper.cs
- SchemaElementLookUpTableEnumerator.cs
- CqlErrorHelper.cs
- WindowsSysHeader.cs
- TraceHandler.cs
- TimeZoneInfo.cs
- ErrorHandler.cs
- UriExt.cs