static void AttachVisualChild_Private_MainSteps(UIElement child, UIElement parent, bool doesParentRequireToCreateAWrapperForEachChild, object innerDivOfWrapperForChild, object domElementWhereToPlaceChildStuff, object wrapperForChild) { #if REWORKLOADED if (INTERNAL_VisualTreeOperation.Current.Root == null) { INTERNAL_VisualTreeOperation.Current.Root = parent; } #endif //#if CSHTML5BLAZOR && DEBUG // string childIndentity = child + (child != null ? " (" + child.GetHashCode() + ")" : ""); // string parentIndentity = parent + (parent != null ? " (" + parent.GetHashCode() + ")" : ""); // Console.WriteLine("OPEN SILVER DEBUG: VisualTreeManager : AttachVisualChild_Private_FinalStepsOnlyIfControlIsVisible: " + childIndentity + " attached to " + parentIndentity); //#endif //-------------------------------------------------------- // CREATE THE DIV FOR THE MARGINS (OPTIONAL): //-------------------------------------------------------- #if PERFSTAT var t1 = Performance.now(); #endif // Determine if an additional DIV for handling margins is needed: object additionalOutsideDivForMargins = null; var margin = ((FrameworkElement)child).Margin; bool containsNegativeMargins = (margin.Left < 0d || margin.Top < 0d || margin.Right < 0d || margin.Bottom < 0d); #if ONLY_ADD_DIV_FOR_MARGINS_WHEN_MARGINS_NOT_ZERO bool isADivForMarginsNeeded = !(parent is Canvas) && !(child is Inline) && child is FrameworkElement; // Note: In a Canvas, we don't want to add the additional DIV because there are no margins and we don't want to interfere with the pointer events by creating an additional DIV. if (isADivForMarginsNeeded) { var horizontalAlign = ((FrameworkElement)child).HorizontalAlignment; var verticalAlign = ((FrameworkElement)child).VerticalAlignment; isADivForMarginsNeeded = !(margin.Left == 0 && margin.Top == 0 && margin.Right == 0 && margin.Bottom == 0 && horizontalAlign == HorizontalAlignment.Stretch && verticalAlign == VerticalAlignment.Stretch); } #else bool isADivForMarginsNeeded = !(parent is Canvas) && // Note: In a Canvas, we don't want to add the additional DIV because there are no margins and we don't want to interfere with the pointer events by creating an additional DIV. !(child is Inline); // Note: inside a TextBlock we do not want the HTML DIV because we want to create HTML SPAN elements only (otherwise there would be unwanted line returns). #endif if (isADivForMarginsNeeded) { // Determine where to place it: object whereToPlaceDivForMargins = (doesParentRequireToCreateAWrapperForEachChild ? innerDivOfWrapperForChild : domElementWhereToPlaceChildStuff); // Create and append the DIV for handling margins and append: additionalOutsideDivForMargins = INTERNAL_HtmlDomManager.CreateDomElementAndAppendIt("div", whereToPlaceDivForMargins, parent); //todo: check if the third parameter should be the child or the parent (make something with margins and put a mouseenter in the parent then see if the event is triggered). // Style the DIV for handling margins: var style = INTERNAL_HtmlDomManager.GetDomElementStyleForModification(additionalOutsideDivForMargins); style.boxSizing = "border-box"; if (child is FrameworkElement && (((FrameworkElement)child).HorizontalAlignment == HorizontalAlignment.Stretch && double.IsNaN(((FrameworkElement)child).Width))) { if (!containsNegativeMargins) { style.width = "100%"; } } if (child is FrameworkElement && (((FrameworkElement)child).VerticalAlignment == VerticalAlignment.Stretch && double.IsNaN(((FrameworkElement)child).Height))) { style.height = "100%"; } } #if PERFSTAT Performance.Counter("VisualTreeManager: Create the DIV for the margin", t1); #endif //-------------------------------------------------------- // PREPARE THE CHILD: //-------------------------------------------------------- #if PERFSTAT var t2 = Performance.now(); #endif // Determine where to place the child: object whereToPlaceTheChild = (isADivForMarginsNeeded ? additionalOutsideDivForMargins : (doesParentRequireToCreateAWrapperForEachChild ? innerDivOfWrapperForChild : domElementWhereToPlaceChildStuff)); // Set the "Parent" property of the Child (IMPORTANT: we need to do that before child.CreateDomElement because the type of the parent is used to display the child correctly): child.INTERNAL_VisualParent = parent; // Set the "ParentWindow" property so that the element knows where to display popups: child.INTERNAL_ParentWindow = parent.INTERNAL_ParentWindow; // Create and append the DOM structure of the Child: object domElementWhereToPlaceGrandChildren = null; object outerDomElement; bool isChildAControl = child is Control; if (child.INTERNAL_HtmlRepresentation == null) { bool hasTemplate = isChildAControl && ((Control)child).HasTemplate; if (hasTemplate) { outerDomElement = ((Control)child).CreateDomElementForControlTemplate(whereToPlaceTheChild, out domElementWhereToPlaceGrandChildren); } else { outerDomElement = child.CreateDomElement(whereToPlaceTheChild, out domElementWhereToPlaceGrandChildren); } } else { outerDomElement = INTERNAL_HtmlDomManager.CreateDomFromStringAndAppendIt(child.INTERNAL_HtmlRepresentation, whereToPlaceTheChild, child); } // Initialize the "Width" and "Height" of the child DOM structure: if (child is FrameworkElement) { FrameworkElement.INTERNAL_InitializeOuterDomElementWidthAndHeight(((FrameworkElement)child), outerDomElement); } // Update the DOM structure of the Child (for example, if the child is a Grid, this will render its rows and columns): child.INTERNAL_UpdateDomStructureIfNecessary(); // For debugging purposes (to better read the output html), add a class to the outer DIV that tells us the corresponding type of the element (Border, StackPanel, etc.): INTERNAL_HtmlDomManager.SetDomElementAttribute(outerDomElement, "class", child.GetType().ToString()); #if PERFSTAT Performance.Counter("VisualTreeManager: Prepare the child", t2); #endif //-------------------------------------------------------- // REMEMBER ALL INFORMATION FOR FUTURE USE: //-------------------------------------------------------- #if PERFSTAT var t3 = Performance.now(); #endif // Remember the DIVs: child.INTERNAL_OuterDomElement = outerDomElement; child.INTERNAL_InnerDomElement = domElementWhereToPlaceGrandChildren; child.INTERNAL_AdditionalOutsideDivForMargins = additionalOutsideDivForMargins ?? outerDomElement; child.INTERNAL_InnerDivOfTheChildWrapperOfTheParentIfAny = doesParentRequireToCreateAWrapperForEachChild ? innerDivOfWrapperForChild : null; // Remember the information about the "VisualChildren": if (parent.INTERNAL_VisualChildrenInformation == null) { parent.INTERNAL_VisualChildrenInformation = new Dictionary <UIElement, INTERNAL_VisualChildInformation>(); } parent.INTERNAL_VisualChildrenInformation.Add(child, new INTERNAL_VisualChildInformation() { INTERNAL_UIElement = child, INTERNAL_OptionalChildWrapper_OuterDomElement = wrapperForChild, INTERNAL_OptionalChildWrapper_ChildWrapperInnerDomElement = innerDivOfWrapperForChild }); #if PERFSTAT Performance.Counter("VisualTreeManager: Remember all info for future use", t3); #endif //-------------------------------------------------------- // HANDLE SPECIAL CASES: //-------------------------------------------------------- #if PERFSTAT var t4 = Performance.now(); #endif // If we are inside a canvas, we set the position to "absolute": if (parent is Canvas) { INTERNAL_HtmlDomManager.GetDomElementStyleForModification(outerDomElement).position = "absolute"; //todo: test if this works properly } UIElement.SynchronizeForceInheritProperties(child, parent); #if REVAMPPOINTEREVENTS UIElement.INTERNAL_UpdateCssPointerEvents(child); #else // If the current element is inside a Grid, we need to explicitly set its CSS property "PointerEvents=auto" because its parent child wrapper has "PointerEvents=none" in order to prevent its child wrappers from overlapping each other and thus preventing clicks on some children. if (parent is Grid && child is FrameworkElement) //todo: generalize this code so that we have no hard-reference on the Grid control, and third-party controls can use the same mechanics. { var frameworkElement = ((FrameworkElement)child); FrameworkElement.INTERNAL_UpdateCssPointerEventsPropertyBasedOnIsHitTestVisibleAndIsEnabled(frameworkElement, frameworkElement.IsHitTestVisible, frameworkElement.IsEnabled); } #endif // Reset the flag that tells if we have already applied the RenderTransformOrigin (this is useful to ensure that the default RenderTransformOrigin is (0,0) like in normal XAML, instead of (0.5,0.5) like in CSS): child.INTERNAL_RenderTransformOriginHasBeenApplied = false; #if PERFSTAT Performance.Counter("VisualTreeManager: Handle special cases", t4); #endif //-------------------------------------------------------- // HANDLE EVENTS: //-------------------------------------------------------- #if PERFSTAT var t5 = Performance.now(); #endif // Register DOM events if any: child.INTERNAL_AttachToDomEvents(); #if PERFSTAT Performance.Counter("VisualTreeManager: Handle events", t5); #endif //-------------------------------------------------------- // HANDLE INHERITED PROPERTIES: //-------------------------------------------------------- #if PERFSTAT var t6 = Performance.now(); #endif // Get the Inherited properties and pass them to the direct children: foreach (DependencyProperty dependencyProperty in #if BRIDGE INTERNAL_BridgeWorkarounds.GetDictionaryKeys_SimulatorCompatible(parent.INTERNAL_AllInheritedProperties) #else parent.INTERNAL_AllInheritedProperties.Keys #endif ) { bool recursively = false; // We don't want a recursion here because the "Attach" method is already recursive due to the fact that we raise property changed on the Children property, which causes to reattach the subtree. INTERNAL_PropertyStorage storage = parent.INTERNAL_AllInheritedProperties[dependencyProperty]; child.SetInheritedValue(dependencyProperty, INTERNAL_PropertyStore.GetEffectiveValue(storage), recursively); } #if PERFSTAT Performance.Counter("Handle inherited properties", t6); #endif //-------------------------------------------------------- // SET "ISLOADED" PROPERTY AND CALL "ONATTACHED" EVENT: //-------------------------------------------------------- // Tell the control that it is now present into the visual tree: child._isLoaded = true; // Raise the "OnAttached" event: child.INTERNAL_OnAttachedToVisualTree(); // IMPORTANT: Must be done BEFORE "RaiseChangedEventOnAllDependencyProperties" (for example, the ItemsControl uses this to initialize its visual) //-------------------------------------------------------- // RENDER THE ELEMENTS BY APPLYING THE CSS PROPERTIES: //-------------------------------------------------------- // Defer rendering when the control is not visible to when becomes visible (note: when this option is enabled, we do not apply the CSS properties of the UI elements that are not visible. Those property are applied later, when the control becomes visible. This option results in improved performance.) bool enableDeferredRenderingOfCollapsedControls = EnableOptimizationWhereCollapsedControlsAreNotRendered || EnableOptimizationWhereCollapsedControlsAreLoadedLast || EnableOptimizationWhereCollapsedControlsAreNotLoaded; if (enableDeferredRenderingOfCollapsedControls && !child.IsVisible) { child.INTERNAL_DeferredRenderingWhenControlBecomesVisible = () => { RenderElementsAndRaiseChangedEventOnAllDependencyProperties(child); }; } else { RenderElementsAndRaiseChangedEventOnAllDependencyProperties(child); } //-------------------------------------------------------- // HANDLE BINDING: //-------------------------------------------------------- #if PERFSTAT var t9 = Performance.now(); #endif child.INTERNAL_UpdateBindingsSource(); #if PERFSTAT Performance.Counter("VisualTreeManager: Handle binding", t9); #endif //-------------------------------------------------------- // HANDLE TABINDEX: //-------------------------------------------------------- // For GotFocus and LostFocus to work, the DIV specified by "INTERNAL_OptionalSpecifyDomElementConcernedByFocus" // (or the OuterDomElement otherwise) needs to have the "tabIndex" attribute set. Therefore we need to always set // it (unless IsTabStop is False) to its current value (default is Int32.MaxValue). At the time when this code was // written, there was no way to automatically call the "OnChanged" on a dependency property if no value was set. #if !REWORKLOADED // IMPORTANT: This needs to be done AFTER the "OnApplyTemplate" (for example, the TextBox sets the "INTERNAL_OptionalSpecifyDomElementConcernedByFocus" in the "OnApplyTemplate"). if (isChildAControl) { if (!(child is ContentPresenter) && !(child is TextBlock)) //ContentPresenter should not count in tabbing, as well as TextBlock (especially since TextBlock is not supposed to be a Control). { Control.TabIndexProperty_MethodToUpdateDom(child, ((Control)child).TabIndex); } } #endif //-------------------------------------------------------- // APPLY THE VISIBILITY: //-------------------------------------------------------- Visibility childVisibility = child.Visibility; if (childVisibility == Visibility.Collapsed) { UIElement.INTERNAL_ApplyVisibility(child, childVisibility); } //-------------------------------------------------------- // RAISE THE "SIZECHANGED" EVENT: //-------------------------------------------------------- #if !REWORKLOADED #if PERFSTAT var t10 = Performance.now(); #endif // Raise the "SizeChanged" event: (note: in XAML, the "SizeChanged" event is called before the "Loaded" event) // IMPORTANT: This event needs to be raised AFTER the "OnApplyTemplate" and AFTER the "IsLoaded=true" (for example, it is used in the ScrollBar implementation). if (child is FrameworkElement) { ((FrameworkElement)child).INTERNAL_SizeChangedWhenAttachedToVisualTree(); } #if PERFSTAT Performance.Counter("VisualTreeManager: Raise size changed event", t10); #endif //-------------------------------------------------------- // RAISE THE "LOADED" EVENT: //-------------------------------------------------------- #if PERFSTAT var t11 = Performance.now(); #endif // Raise the "Loaded" event: (note: in XAML, the "loaded" event of the children is called before the "loaded" event of the parent) if (child is FrameworkElement) { ((FrameworkElement)child).INTERNAL_RaiseLoadedEvent(); } child.StartManagingPointerPositionForPointerExitedEventIfNeeded(); #if PERFSTAT Performance.Counter("VisualTreeManager: Raise Loaded event", t11); #endif #else INTERNAL_VisualTreeOperation.Current.Enqueue(child); #endif }
static void AttachVisualChild_Private(UIElement child, UIElement parent) { // // THIS IS WHAT THE FINAL STRUCTURE IN THE DOM TREE WILL LOOK LIKE: // // domElementWhereToPlaceChildStuff // --- [wrapperForChild] // --- --- [innerDivOfWrapperForChild] // --- --- --- [additionalOutsideDivForMargins, aka BoxSizing] // --- --- --- --- outerDomElement // //-------------------------------------------------------- // APPLY IMPLICIT AND DEFAULT STYLES: //-------------------------------------------------------- #if PERFSTAT var t = Performance.now(); #endif //See if the child has a Style, if not, try to find an implicitStyle for its Type. FrameworkElement childAsFrameworkElement = child as FrameworkElement; if (childAsFrameworkElement != null) { if (childAsFrameworkElement.Style == null) { //----------------------------- // APPLY IMPLICIT STYLE IF ANY: //----------------------------- Style style = null; //Note if anyone wonders: if type B inherits from type A, an Implicit Style defined for type A is ignored by elements of type B (tested in an SL project). if (UpdateElementInheritedImplicitStyles(childAsFrameworkElement, (FrameworkElement)parent)) { Type childType = child.GetType(); foreach (ResourceDictionary resourceDictionary in childAsFrameworkElement.INTERNAL_InheritedImplicitStyles) // Note: "INTERNAL_InheritedImplicitStyles" is not null because the method above returned True. { if (resourceDictionary.ContainsKey(childType)) { style = resourceDictionary[childType] as Style; break; } } } if (style != null) { childAsFrameworkElement.Style = style; childAsFrameworkElement.INTERNAL_IsImplicitStyle = true; //Note: it is important that we do this AFTER setting the Style property to the ImplicitStyle because the setter of the Style Property sets it to false to avoid removing styles that are not implicit. } } //Add the effect of the setters of the default Style on the child: Style childDefaultStyle = childAsFrameworkElement.INTERNAL_defaultStyle; if (childDefaultStyle != null) { //---------------------------- // APPLY DEFAULT STYLE IF ANY: //---------------------------- //get the list of setters of the currently applied style on the element and of the default style: Dictionary <DependencyProperty, Setter> normalStyleDictionary = null; bool childHasStyle = childAsFrameworkElement.Style != null; if (childHasStyle) { normalStyleDictionary = childAsFrameworkElement.Style.GetDictionaryOfSettersFromStyle(); } Dictionary <DependencyProperty, Setter> defaultStyleDictionary = childDefaultStyle.GetDictionaryOfSettersFromStyle(); //for all the setters the default style has, that are not on properties already affected by the normal style, we apply them like a normal style (we basically do the same as what we do on each setter in FrameworkElement.Style_Changed): foreach (DependencyProperty prop in #if BRIDGE INTERNAL_BridgeWorkarounds.GetDictionaryKeys_SimulatorCompatible(defaultStyleDictionary) #else defaultStyleDictionary.Keys #endif ) { if (!childHasStyle || !normalStyleDictionary.ContainsKey(prop)) { Setter setter = defaultStyleDictionary[prop]; childDefaultStyle.SetterValueChanged -= childAsFrameworkElement.StyleSetterValueChanged; childDefaultStyle.SetterValueChanged += childAsFrameworkElement.StyleSetterValueChanged; INTERNAL_PropertyStorage storage = INTERNAL_PropertyStore.GetStorageOrCreateNewIfNotExists(childAsFrameworkElement, setter.Property); INTERNAL_PropertyStore.SetLocalStyleValue(storage, setter.Value); } } } } #if PERFSTAT Performance.Counter("VisualTreeManager: Apply implicit and default styles", t); #endif //-------------------------------------------------------- // PREPARE THE PARENT: //-------------------------------------------------------- #if PERFSTAT var t0 = Performance.now(); #endif // Prepare the parent DOM structure so that it is ready to contain the child (for example, in case of a grid, we need to (re)create the rows and columns where to place the elements). //parent.INTERNAL_UpdateDomStructureIfNecessary(); object domElementWhereToPlaceChildStuff = (parent.GetDomElementWhereToPlaceChild(child) ?? parent.INTERNAL_InnerDomElement); // A "wrapper for child" is sometimes needed between the child and the parent (for example in case of a grid). It is usually one or more DIVs that fit in-between the child and the parent, and that are used to position the child within the parent. object innerDivOfWrapperForChild; object wrapperForChild = parent.CreateDomChildWrapper(domElementWhereToPlaceChildStuff, out innerDivOfWrapperForChild); bool comparison1 = (wrapperForChild == null); // Note: we need due to a bug of JSIL where translation fails if we do not use this temp variable. bool comparison2 = (innerDivOfWrapperForChild == null); // Note: we need due to a bug of JSIL where translation fails if we do not use this temp variable. bool doesParentRequireToCreateAWrapperForEachChild = (!comparison1 && !comparison2); // Note: The result is "True" for complex structures such as tables, false otherwise (cf. documentation in "INTERNAL_VisualChildInformation" class). #if PERFSTAT Performance.Counter("VisualTreeManager: Prepare the parent", t0); #endif //-------------------------------------------------------- // CONTINUE WITH THE OTHER STEPS (OR DEFER TO WHEN THE ELEMENT IS VISIBLE, IF THE OPTIMIZATION TO NOT LOAD COLLAPSED CONTROLS IS ENABLED): //-------------------------------------------------------- if (!EnableOptimizationWhereCollapsedControlsAreNotLoaded || child.Visibility != Visibility.Collapsed) { AttachVisualChild_Private_FinalStepsOnlyIfControlIsVisible( child, parent, doesParentRequireToCreateAWrapperForEachChild, innerDivOfWrapperForChild, domElementWhereToPlaceChildStuff, wrapperForChild); } else { child.INTERNAL_DeferredLoadingWhenControlBecomesVisible = () => { AttachVisualChild_Private_FinalStepsOnlyIfControlIsVisible( child, parent, doesParentRequireToCreateAWrapperForEachChild, innerDivOfWrapperForChild, domElementWhereToPlaceChildStuff, wrapperForChild); }; } }