/// <summary> /// Получает для указанного контрола full render buffer и применяет его последовательно /// ко всем родительским элементам управления, вплоть до изображения на экране. /// Возвращает прямоугольник, необходимый для ревалидации на экране (affected rect). /// Учитывает Z-Order контролов-соседей (если родительский контрол имеет несколько дочерних, они могут перекрывать /// друг друга). /// Первый вызов производится с affectedRect = control.RenderSize. /// </summary> /// <returns>Affected rectangle in canvas should be copyied to console screen.</returns> private Rect applyChangesToCanvas(Control control, Rect affectedRect) { // если системой лайаута были определены размеры дочернего контрола, превышающие размеры слота // (такое может произойти, если дочерний контрол игнорирует переданные аргументы в MeasureOverride // и ArrangeOverride), то в этом месте может прийти affectedRect, выходящий за рамки // текущего RenderSize контрола, и мы должны выполнить intersection для корректного наложения affectedRect.Intersect(new Rect(new Point(0, 0), control.RenderSize)); RenderingBuffer fullBuffer = getOrCreateFullBufferForControl(control); if (control.Parent != null) { RenderingBuffer fullParentBuffer = getOrCreateFullBufferForControl(control.Parent); // если буфер контрола содержит opacity пиксели в affectedRect, то мы вынуждены переинициализировать // буфер парента целиком (не вызывая Render, конечно, но переналожением буферов дочерних элементов) if (fullBuffer.ContainsOpacity(affectedRect)) { fullParentBuffer.Clear(); fullParentBuffer.CopyFrom(getOrCreateBufferForControl(control.Parent)); foreach (Control child in control.Parent.Children) { if (child.Visibility == Visibility.Visible) { RenderingBuffer childBuffer = getOrCreateFullBufferForControl(child); fullParentBuffer.ApplyChild(childBuffer, child.ActualOffset, child.RenderSize, child.RenderSlotRect, child.LayoutClip); } } } if (control.Visibility == Visibility.Visible) { if (affectedRect == new Rect(new Point(0, 0), control.RenderSize)) { fullParentBuffer.ApplyChild(fullBuffer, control.ActualOffset, control.RenderSize, control.RenderSlotRect, control.LayoutClip); } else { fullParentBuffer.ApplyChild(fullBuffer, control.ActualOffset, control.RenderSize, control.RenderSlotRect, control.LayoutClip, affectedRect); } } // определим соседей контрола, которые могут перекрывать его IList <Control> neighbors = control.Parent.GetChildrenOrderedByZIndex(); // восстанавливаем изображение поверх обновленного контрола, если // имеются контролы, лежащие выше по z-order int controlIndex = neighbors.IndexOf(control); // начиная с controlIndex + 1 в списке лежат контролы с z-index больше чем z-index текущего контрола for (int i = controlIndex + 1; i < neighbors.Count; i++) { Control neighbor = neighbors[i]; fullParentBuffer.ApplyChild(getOrCreateFullBufferForControl(neighbor), neighbor.ActualOffset, neighbor.RenderSize, neighbor.RenderSlotRect, neighbor.LayoutClip); } Rect parentAffectedRect = control.RenderSlotRect; parentAffectedRect.Intersect(new Rect(affectedRect.x + control.ActualOffset.x, affectedRect.y + control.ActualOffset.y, affectedRect.width, affectedRect.height)); // нет смысла продолжать подъем вверх по дереву, если контрола точно уже не видно if (parentAffectedRect.IsEmpty) { return(Rect.Empty); } return(applyChangesToCanvas(control.Parent, parentAffectedRect)); } else { if (control != RootElement) { throw new InvalidOperationException("Assertion failed."); } // мы добрались до экрана консоли fullBuffer.CopyToPhysicalCanvas(Canvas, affectedRect, RootElementRect.TopLeft); return(affectedRect); } }