static void RenderElementsAndRaiseChangedEventOnAllDependencyProperties(DependencyObject dependencyObject) { //#if CSHTML5BLAZOR && DEBUG // string prettyPrintDependencyObject = dependencyObject + (dependencyObject != null ? "(" + dependencyObject.GetHashCode().ToString() +")" : ""); // Console.WriteLine("OPENSILVER DEBUG: VisualTreeManager: RenderElementsAndRaiseChangedEventOnAllDependencyProperties: dependencyObject:" + prettyPrintDependencyObject); //#endif // To prevent raising the property multiple times, we keep track of the properties for which we already raised the "PropertyChanged" event: //Dictionary<DependencyProperty, DependencyProperty> propertiesForWhichTheEventHasAlreadyBeenRaised = new Dictionary<DependencyProperty, DependencyProperty>(); //todo: replace with HashSet<DependencyProperty> when available. //------------------------------------------------------------------------------------------------------------------ // RAISE "PROPERTYCHANGED" FOR ALL THE PROPERTIES THAT HAVE A VALUE THAT HAS BEEN SET, INCLUDING ATTACHED PROPERTIES, AND CALL THE "METHOD TO UPDATE DOM": //------------------------------------------------------------------------------------------------------------------ // This is used to force a redraw of all the properties that are set on the object (including Attached Properties!). For example, if a Border has a colored background, this is the moment when that color will be applied. Properties that have no value set by the user are not concerned (their default state is rendered elsewhere). if (dependencyObject.INTERNAL_PropertyStorageDictionary != null) { #if PERFSTAT var t0 = Performance.now(); #endif var list = dependencyObject.INTERNAL_PropertyStorageDictionary.ToList <KeyValuePair <DependencyProperty, INTERNAL_PropertyStorage> >(); //we copy the Dictionary so that the foreach doesn't break when we modify a DependencyProperty inside the Changed of another one (which causes it to be added to the Dictionary). #if PERFSTAT Performance.Counter("VisualTreeManager: Copy list of properties", t0); #endif foreach (KeyValuePair <DependencyProperty, INTERNAL_PropertyStorage> propertiesAndTheirStorage in list) { // Read the value: DependencyProperty property = propertiesAndTheirStorage.Key; #if PERFSTAT var t1 = Performance.now(); #endif PropertyMetadata propertyMetadata = property.GetTypeMetaData(dependencyObject.GetType()); //#if CSHTML5BLAZOR && DEBUG // string prettyPrintProperty = property.Name + (property != null ? "(" + property.GetHashCode().ToString() + ")" : ""); // Console.WriteLine("OPENSILVER DEBUG: VisualTreeManager: RenderElementsAndRaiseChangedEventOnAllDependencyProperties:" // + " dependencyObject:" + prettyPrintDependencyObject // + " property:" + prettyPrintProperty // + " MSG: " + (propertyMetadata != null ? "has" : "has not") + " a propertyMetadata"); //#endif if (propertyMetadata != null) { INTERNAL_PropertyStorage storage = propertiesAndTheirStorage.Value; object value = null; bool valueWasRetrieved = false; //-------------------- // Call "Apply CSS", which uses "GetCSSEquivalent/s": //-------------------- if (propertyMetadata.GetCSSEquivalent != null || propertyMetadata.GetCSSEquivalents != null) { if (!valueWasRetrieved) { value = INTERNAL_PropertyStore.GetValue(storage, propertyMetadata); valueWasRetrieved = true; } INTERNAL_PropertyStore.ApplyCssChanges(value, value, propertyMetadata, storage.Owner); } //-------------------- // Call "MethodToUpdateDom": //-------------------- if (propertyMetadata.MethodToUpdateDom != null) { if (!valueWasRetrieved) { value = INTERNAL_PropertyStore.GetValue(storage, propertyMetadata); valueWasRetrieved = true; } // Call the "Method to update DOM": propertyMetadata.MethodToUpdateDom(storage.Owner, value); } //-------------------- // Call PropertyChanged: //-------------------- //#if CSHTML5BLAZOR && DEBUG // string prettyPrintPropertyMetadata = propertyMetadata + (propertyMetadata != null ? propertyMetadata.GetHashCode().ToString() : ""); // Console.WriteLine("OPENSILVER DEBUG: VisualTreeManager: RenderElementsAndRaiseChangedEventOnAllDependencyProperties:" // + " dependencyObject:" + prettyPrintDependencyObject // + " property:" + prettyPrintProperty // + " propertyMetadata:" + propertyMetadata // + " MSG: " + (propertyMetadata.PropertyChangedCallback != null ? "has" : "has not") + " a PropertyChangedCallback" ); //#endif if (propertyMetadata.PropertyChangedCallback != null && propertyMetadata.CallPropertyChangedWhenLoadedIntoVisualTree != WhenToCallPropertyChangedEnum.Never) { if (!valueWasRetrieved) { value = INTERNAL_PropertyStore.GetValue(storage, propertyMetadata); valueWasRetrieved = true; } //#if CSHTML5BLAZOR && DEBUG // Console.WriteLine("OPENSILVER DEBUG: VisualTreeManager: RenderElementsAndRaiseChangedEventOnAllDependencyProperties:" // + " dependencyObject:" + prettyPrintDependencyObject // + " property:" + prettyPrintProperty // + " propertyMetadata:" + propertyMetadata // + " MSG: is raising PropertyChangedCallback)"); //#endif // Raise the "PropertyChanged" event: propertyMetadata.PropertyChangedCallback(storage.Owner, new DependencyPropertyChangedEventArgs(value, value, property)); // Remember that we raised the event, to avoid raising it again in the code below: //propertiesForWhichTheEventHasAlreadyBeenRaised.Add(property, property); //commented since the "code below" is commented } } #if PERFSTAT Performance.Counter("VisualTreeManager: RaisePropertyChanged for property '" + property.Name + "'", t1); #endif } } /* * //------------------------------------------------------------------------------------ * // RAISE "PROPERTYCHANGED" FOR ALL THE OTHER PROPERTIES THAT REQUIRE IT * //------------------------------------------------------------------------------------ * * // When a dependency property is not set, we usually do not raise the "PropertyChanged" event, unless the property has its "CallPropertyChangedWhenLoadedIntoVisualTree" property set to "Always" in its TypeMetadata: * var type = dependencyObject.GetType(); * // Iterate through all the inheritance parents of the type (we use a Stack to revert the order): * Stack<Type> typeAndItsInheritanceParents = new Stack<Type>(); * while (type != null) * { * typeAndItsInheritanceParents.Push(type); * type = type.BaseType; * } * while (typeAndItsInheritanceParents.Count > 0) * { * type = typeAndItsInheritanceParents.Pop(); * if (INTERNAL_TypeToDependencyPropertiesThatRequirePropertyChanged.TypeToDependencyPropertiesThatRequirePropertyChanged.ContainsKey(type)) * { * // Raise the "PropertyChanged" for all the DependencyProperties of the object that require it: * foreach (DependencyProperty dependencyProperty in INTERNAL_TypeToDependencyPropertiesThatRequirePropertyChanged.TypeToDependencyPropertiesThatRequirePropertyChanged[type]) * { * if (!propertiesForWhichTheEventHasAlreadyBeenRaised.ContainsKey(dependencyProperty) * && dependencyProperty.TypeMetadata.PropertyChangedCallback != null) * { * // Get the value: * object value; * if (dependencyObject.INTERNAL_PropertyStorageDictionary.ContainsKey(dependencyProperty)) * value = dependencyObject.GetValue(dependencyProperty); * else if (dependencyProperty.TypeMetadata != null) * value = dependencyProperty.TypeMetadata.DefaultValue; * else * value = null; * * // Raise the "PropertyChanged": * dependencyProperty.TypeMetadata.PropertyChangedCallback(dependencyObject, new DependencyPropertyChangedEventArgs(value, value, dependencyProperty)); * } * } * } * } */ }
static void AttachVisualChild_Private_FinalStepsOnlyIfControlIsVisible( UIElement child, UIElement parent, bool doesParentRequireToCreateAWrapperForEachChild, object innerDivOfWrapperForChild, object domElementWhereToPlaceChildStuff, object wrapperForChild ) { //#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 } #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: if (parent.INTERNAL_AllInheritedProperties != null) { 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]; PropertyMetadata typeMetadata = dependencyProperty.GetTypeMetaData(storage.Owner.GetType()); child.SetInheritedValue(dependencyProperty, INTERNAL_PropertyStore.GetValue(storage, typeMetadata), 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) //-------------------------------------------------------- // RECURSIVELY ATTACH THE CHILDREN, AND RENDER THE ELEMENTS BY RAISING PROPERTYCHANGED EVENT ON ALL THE DEPENDENCY PROPERTIES THAT ARE SET: //-------------------------------------------------------- RenderElementsAndRaiseChangedEventOnAllDependencyProperties(child); // Note: this causes a recursion of the AttachVisualChild method because the "Children" properties of the controls get refreshed. //element.INTERNAL_Render(); //-------------------------------------------------------- // 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. // 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); } } //-------------------------------------------------------- // RAISE THE "SIZECHANGED" EVENT: //-------------------------------------------------------- #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 }