Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / LayoutManager.cs / 6 / 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 currentElement.Measure(currentElement.PreviousConstraint); //[....], 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); //[....], 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 sync. 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
- DrawingImage.cs
- TextEndOfLine.cs
- fixedPageContentExtractor.cs
- TextContainer.cs
- BlockUIContainer.cs
- CategoryList.cs
- MenuItemCollectionEditorDialog.cs
- RangeValuePatternIdentifiers.cs
- TypeToStringValueConverter.cs
- TableRowCollection.cs
- Mutex.cs
- XmlByteStreamWriter.cs
- TypeUsageBuilder.cs
- HtmlUtf8RawTextWriter.cs
- GenericArgumentsUpdater.cs
- BlobPersonalizationState.cs
- _NetRes.cs
- InheritablePropertyChangeInfo.cs
- ImageCodecInfo.cs
- AlphabetConverter.cs
- WorkflowMarkupSerializationManager.cs
- GregorianCalendarHelper.cs
- XmlNamespaceDeclarationsAttribute.cs
- LogicalExpressionEditor.cs
- OrthographicCamera.cs
- InvokeGenerator.cs
- ClipboardData.cs
- ListViewDeletedEventArgs.cs
- CallbackException.cs
- Stack.cs
- XmlReader.cs
- ObjectViewEntityCollectionData.cs
- DbTransaction.cs
- HMACSHA512.cs
- DataBinding.cs
- ImageListStreamer.cs
- MulticastIPAddressInformationCollection.cs
- MethodRental.cs
- FlowDocumentPaginator.cs
- Types.cs
- TableProviderWrapper.cs
- ItemCheckedEvent.cs
- validation.cs
- PreservationFileWriter.cs
- ListDictionaryInternal.cs
- AutoResetEvent.cs
- RectValueSerializer.cs
- SQLUtility.cs
- SerializationEventsCache.cs
- SqlCacheDependencyDatabase.cs
- mansign.cs
- SizeValueSerializer.cs
- StorageInfo.cs
- Blend.cs
- Registry.cs
- SliderAutomationPeer.cs
- path.cs
- QilReplaceVisitor.cs
- PhotoPrintingIntent.cs
- LinqDataView.cs
- TextBlock.cs
- CanonicalFontFamilyReference.cs
- RightsManagementEncryptionTransform.cs
- ReflectionHelper.cs
- InvalidWorkflowException.cs
- Light.cs
- KeyProperty.cs
- HtmlInputImage.cs
- StreamAsIStream.cs
- XmlNotation.cs
- XslTransform.cs
- GridSplitterAutomationPeer.cs
- ZoneButton.cs
- ImageField.cs
- PropertyChangeTracker.cs
- DropTarget.cs
- XmlSerializerFactory.cs
- SafeRegistryKey.cs
- ButtonColumn.cs
- DesignTimeHTMLTextWriter.cs
- DiscoveryClientDuplexChannel.cs
- DataView.cs
- ProtectedProviderSettings.cs
- OdbcConnectionHandle.cs
- DataGridViewRowPrePaintEventArgs.cs
- ParagraphResult.cs
- TypeElement.cs
- KeyInstance.cs
- Array.cs
- Base64Encoder.cs
- WebPageTraceListener.cs
- HtmlValidationSummaryAdapter.cs
- AnonymousIdentificationModule.cs
- WsatTransactionFormatter.cs
- PenCursorManager.cs
- PublisherIdentityPermission.cs
- DragAssistanceManager.cs
- ResolveNextArgumentWorkItem.cs
- XmlAtomicValue.cs
- CryptoApi.cs