/// <summary>
        /// Enqueues any elements within the specified tree that have invalid measure, arrange, or styles, but
        /// were previously removed from the queues as a result of being orphaned by a destroyed window.
        /// </summary>
        /// <param name="root">The root of the tree to restore.</param>
        internal void RestoreToQueues(DependencyObject root)
        {
            Contract.Require(root, nameof(root));

            if (root is UIElement uiElement)
            {
                if (!uiElement.IsMeasureValid)
                {
                    MeasureQueue.Enqueue(uiElement);
                }

                if (!uiElement.IsArrangeValid)
                {
                    ArrangeQueue.Enqueue(uiElement);
                }

                if (!uiElement.IsStyleValid)
                {
                    StyleQueue.Enqueue(uiElement);
                }
            }

            VisualTreeHelper.ForEachChild(root, this, (child, state) =>
            {
                ((PresentationFoundation)state).RestoreToQueues(child);
            });
        }
        /// <summary>
        /// Removes all elements associated with the specified view from the Foundation's processing queues.
        /// </summary>
        /// <param name="view">The view to remove from the queues.</param>
        internal void RemoveFromQueues(PresentationFoundationView view)
        {
            Contract.Require(view, nameof(view));

            StyleQueue.Remove(view);
            MeasureQueue.Remove(view);
            ArrangeQueue.Remove(view);
        }
        /// <summary>
        /// Removes the specified UI element from all of the Foundation's processing queues.
        /// </summary>
        /// <param name="element">The element to remove from the queues.</param>
        internal void RemoveFromQueues(UIElement element)
        {
            Contract.Require(element, nameof(element));

            StyleQueue.Remove(element);
            MeasureQueue.Remove(element);
            ArrangeQueue.Remove(element);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        /// <summary>
        /// Tells ContextLayoutManager to finalize possibly async update.
        /// Used before accessing services off Visual.
        /// </summary>

        //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking
        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
                            }
                            currentElement.Measure(currentElement.PreviousConstraint);
//dmitryt,
                        }

                        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,
                        }

                        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 sync. Need to protect from reentrancy
                        //in the firing methods below.
                        _isInUpdateLayout = false;
                    }

                    fireSizeChangedEvents();
                    if (hasDirtiness)
                    {
                        continue;
                    }
                    fireLayoutUpdateEvent();
                    if (hasDirtiness)
                    {
                        continue;
                    }
                    fireAutomationEvents();
                    if (hasDirtiness)
                    {
                        continue;
                    }
                    fireSizeChangedEvents(); // if nothing is dirty, one last chance for any size changes to announce.
                }

                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
        }