// Writes a collection of attributes representing properties with local values set on them. // If the value cannot be serialized as a string, adds the property to a collection of complexProperties. private static void WriteLocallySetProperties(Type elementTypeStandardized, ITextPointer context, XmlWriter xmlWriter, DependencyObject complexProperties) { TextPointer textPointer = context as TextPointer; if (textPointer == null) { // We can't have custom properties if we're not a TextPointer return; } LocalValueEnumerator locallySetProperties = context.GetLocalValueEnumerator(); DependencyProperty[] inheritableProperties = TextSchema.GetInheritableProperties(elementTypeStandardized); DependencyProperty[] nonInheritableProperties = TextSchema.GetNoninheritableProperties(elementTypeStandardized); while (locallySetProperties.MoveNext()) { DependencyProperty locallySetProperty = (DependencyProperty)locallySetProperties.Current.Property; // Don't serialize read-only properties, or any properties registered or owned by a // a class in the framework (we only want to serialize custom properties), to be // consistent with our behavior for non-custom inlines. // if (!locallySetProperty.ReadOnly && !IsPropertyKnown(locallySetProperty, inheritableProperties, nonInheritableProperties) && !TextSchema.IsKnownType(locallySetProperty.OwnerType)) { object propertyValue = context.ReadLocalValue(locallySetProperty); string stringValue = DPTypeDescriptorContext.GetStringValue(locallySetProperty, propertyValue); if (stringValue != null) { stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, locallySetProperty.PropertyType); string propertyName = GetPropertyNameForElement(locallySetProperty, elementTypeStandardized, /*forceComplexName:*/false); xmlWriter.WriteAttributeString(propertyName, stringValue); } else { complexProperties.SetValue(locallySetProperty, propertyValue); } } } // *** WE NEED TO BETTER UNDERSTAND THE IMPLICATIONS OF SERIALIZING NON-DP CLR PROPERTIES, SO THE REST OF // *** THIS METHOD IS DISABLED UNTIL WE DECIDE THE BEST WAY TO HANDLE THEM. // *** CLRTypeDescriptorContext is essentially the same as DPTypeDescriptorContext. #if false // Check all CLR properties // Note that this is partially redundant. TypeDescriptor.GetProperties, when called on a // DependencyObject, will return all properties that are set-- including all those already // serialized as Inheritable, NonInheritable, or LocallySet properties. A potential // optimization, therefore, is to remove those serialization methods and simply use this one // for everything when we've opted into custom element serialization. PropertyDescriptorCollection descriptorCollection = TypeDescriptor.GetProperties(textPointer.Parent); IEnumerator descriptors = descriptorCollection.GetEnumerator(); while (descriptors.MoveNext()) { PropertyDescriptor current = (PropertyDescriptor)descriptors.Current; // ShouldSerializeValue() will return true for readonly properties that have explicitly // been told to serialize, such as Span.Inlines. If we serialize a read-only property, // however, the parser will throw an exception when we try to deserialize. So we // explicitly skip all read-only properties, and all DPs. if (!current.ShouldSerializeValue(textPointer.Parent) || current.IsReadOnly || current is MS.Internal.ComponentModel.DependencyObjectPropertyDescriptor) { continue; } // Serialize the property object propertyValue = current.GetValue(textPointer.Parent); if (propertyValue != null) { string stringValue = CLRTypeDescriptorContext.GetStringValue(current, propertyValue); if (stringValue != null) { stringValue = FilterNaNStringValueForDoublePropertyType(stringValue, current.PropertyType); xmlWriter.WriteAttributeString(current.Name, stringValue); } else { // } } } #endif }
// Writes a collection of attributes representing non-inheritable properties // whose values are set inline on the given element instance. // When we read properties fromContext we want all values including defaults; from text elements we only want only affected private static void WriteNoninheritableProperties(Type elementTypeStandardized, ITextPointer context, XmlWriter xmlWriter, bool onlyAffected, DependencyObject complexProperties) { DependencyProperty[] elementProperties = TextSchema.GetNoninheritableProperties(elementTypeStandardized); // We'll need a pointer to walk the tree up when onlyAffected=false ITextPointer parentContext = onlyAffected ? null : context.CreatePointer(); for (int i = 0; i < elementProperties.Length; i++) { DependencyProperty property = elementProperties[i]; Type propertyOwnerType = context.ParentType; object propertyValue; if (onlyAffected) { // propertyValue = context.GetValue(property); } else { // This is request for contextual properties - use "manual" inheritance to collect values Invariant.Assert(elementTypeStandardized == typeof(Span), "Request for contextual properties is expected for Span wrapper only"); // Get property value from this element or from one of its ancestors (the latter in case of !onlyAffeted) propertyValue = context.GetValue(property); // Get property value from its ancestors if the property is not set. // TextDecorationCollection is special-cased as its default is empty collection, // and its value source cannot be distinguished from ITextPointer. if (propertyValue == null || TextDecorationCollection.Empty.ValueEquals(propertyValue as TextDecorationCollection)) { if (property == Inline.BaselineAlignmentProperty || property == TextElement.TextEffectsProperty) { // These properties do not make sense as contextual; do not include them into context. continue; } parentContext.MoveToPosition(context); while ((propertyValue == null || TextDecorationCollection.Empty.ValueEquals(propertyValue as TextDecorationCollection)) && typeof(Inline).IsAssignableFrom(parentContext.ParentType)) { parentContext.MoveToElementEdge(ElementEdge.BeforeStart); propertyValue = parentContext.GetValue(property); propertyOwnerType = parentContext.ParentType; } } } // if ((property == Block.MarginProperty && (typeof(Paragraph).IsAssignableFrom(propertyOwnerType) || typeof(List).IsAssignableFrom(propertyOwnerType))) || (property == Block.PaddingProperty) && typeof(List).IsAssignableFrom(propertyOwnerType)) { Thickness thickness = (Thickness)propertyValue; if (Paragraph.IsMarginAuto(thickness)) { continue; } } // Write the property as attribute string or add it to a list of complex properties. WriteNoninheritableProperty(xmlWriter, property, propertyValue, propertyOwnerType, onlyAffected, complexProperties, context.ReadLocalValue(property)); } }