private void Btn_test_att_Click(object sender, RoutedEventArgs e) { Human human = new Human(); School.SetGrade(human, 6); int grad = School.GetGrade(human); Msg($"School的GlobalIndex:{School.GradeProperty.GlobalIndex}"); Msg($"human的LocalValueEnumerator的HashCode:{human.GetLocalValueEnumerator().GetHashCode()}"); Msg($"human附加School的Grad属性值:{grad}"); Msg($"human附加School的依赖属性个数:{human.GetLocalValueEnumerator().Count}"); //遍历依赖属性 LocalValueEnumerator loc = human.GetLocalValueEnumerator(); while (loc.MoveNext()) { Msg($"property: {loc.Current.Property.ToString()}"); Msg($"Value: {loc.Current.Value.ToString()}"); Msg($"GetHashCode: {loc.Current.GetHashCode().ToString()}"); Msg($"GetType: {loc.Current.Property.GetType().ToString()}"); } }
private void UpdateActiveElementBindings() { if (Keyboard.FocusedElement != null && Keyboard.FocusedElement is DependencyObject) { DependencyObject element = (DependencyObject)Keyboard.FocusedElement; LocalValueEnumerator localValueEnumerator = element.GetLocalValueEnumerator(); while (localValueEnumerator.MoveNext()) { BindingExpressionBase bindingExpression = BindingOperations.GetBindingExpressionBase(element, localValueEnumerator .Current .Property); if (bindingExpression != null) { bindingExpression.UpdateSource(); bindingExpression.UpdateTarget(); } } } }
public static bool IsValid(DependencyObject parent) { // Validate all the bindings on the parent bool valid = true; LocalValueEnumerator localValues = parent.GetLocalValueEnumerator(); while (localValues.MoveNext()) { LocalValueEntry entry = localValues.Current; if (BindingOperations.IsDataBound(parent, entry.Property)) { Binding binding = BindingOperations.GetBinding(parent, entry.Property); foreach (ValidationRule rule in binding.ValidationRules) { ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null); if (!result.IsValid) { BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null)); valid = false; } } } } // Validate all the bindings on the children for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i) { DependencyObject child = VisualTreeHelper.GetChild(parent, i); if (!IsValid(child)) { valid = false; } } return(valid); }
GetTypeDependencyPropertiesCacheItem( Object serializableObject ) { if (serializableObject == null) { throw new ArgumentNullException("serializableObject"); } Type type = serializableObject.GetType(); TypeDependencyPropertiesCacheItem cachedItem = (TypeDependencyPropertiesCacheItem)_typesDependencyPropertiesCacheTable[type]; if (cachedItem == null) { // // This means that the type was not seen before // We have to create a new entry to that type // DependencyObject objectAsDependencyObject = serializableObject as DependencyObject; if (objectAsDependencyObject != null) { // // First we have to figure out if this dependency // object has any dependency properties that can be // serializable and this has to happen before creating // any cache // DependencyPropertyList list = new DependencyPropertyList(1); for (LocalValueEnumerator localValues = objectAsDependencyObject.GetLocalValueEnumerator(); localValues.MoveNext();) { DependencyProperty dependencyProperty = localValues.Current.Property; list.Add(dependencyProperty); } if (list.Count > 0) { int numOfSerializableDependencyProperties = 0; TypeDependencyPropertyCache[] dependencyPropertiesCache = new TypeDependencyPropertyCache[list.Count]; for (int indexInDependencyPropertyList = 0; indexInDependencyPropertyList < list.Count; indexInDependencyPropertyList++) { DependencyProperty dependencyProperty = list.List[indexInDependencyPropertyList]; DesignerSerializationVisibility visibility = DesignerSerializationVisibility.Visible; Type serializerTypeForProperty = null; TypeConverter typeConverterForProperty = null; DefaultValueAttribute defaultValueAttr = null; DesignerSerializationOptionsAttribute designerSerializationFlagsAttr = null; Type propertyType = dependencyProperty.PropertyType; // // Get the static setter member for the DependencyProperty // MemberInfo memberInfo = dependencyProperty. OwnerType. GetMethod("Get" + dependencyProperty.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy); // Note: This is because the IService model does not abide // by this pattern of declaring the DependencyProperty on // the OwnerType. That is the only exception case. if (memberInfo == null) { // // Create a PropertyInfo // PropertyInfo propertyInfo = null; PropertyInfo[] properties = dependencyProperty.OwnerType.GetProperties(); String name = dependencyProperty.Name; for (int i = 0; i < properties.Length && propertyInfo == null; i++) { if (properties[i].Name == name) { propertyInfo = properties[i]; } } if (propertyInfo != null) { Debug.Assert(propertyInfo.PropertyType == dependencyProperty.PropertyType, "The property type of the CLR wrapper must match that of the DependencyProperty itself."); memberInfo = propertyInfo; // // We have to special case Print Tickets here. // Print Tickets are defined on as dependency properties on // fixed objects of types: // o FixedDocumentSequence // o FixedDocument // o FixedPage // and in order to eliminate the dependency between // PresentationFramework and System.printing assemblies, // those dependency properties are defined as of type "object" // and hence if we are here and we have a property of name // "PrintTicket" and owned by one of the above mentioned types // we try to get the serializer for the PrintTicket object // if (propertyInfo.Name == XpsNamedProperties.PrintTicketProperty && ((dependencyProperty.OwnerType == typeof(System.Windows.Documents.FixedPage)) || (dependencyProperty.OwnerType == typeof(System.Windows.Documents.FixedDocument)) || (dependencyProperty.OwnerType == typeof(System.Windows.Documents.FixedDocumentSequence)))) { propertyType = typeof(PrintTicket); } } } if (memberInfo != null && TypeDependencyPropertyCache. CanSerializeProperty(memberInfo, this, out visibility, out serializerTypeForProperty, out typeConverterForProperty, out defaultValueAttr, out designerSerializationFlagsAttr) == true) { TypeCacheItem typeCacheItem = GetTypeCacheItem(propertyType); serializerTypeForProperty = serializerTypeForProperty == null ? typeCacheItem.SerializerType : serializerTypeForProperty; typeConverterForProperty = typeConverterForProperty == null ? typeCacheItem.TypeConverter : typeConverterForProperty; TypeDependencyPropertyCache dependencyPropertyCache = new TypeDependencyPropertyCache(memberInfo, dependencyProperty, visibility, serializerTypeForProperty, typeConverterForProperty, defaultValueAttr, designerSerializationFlagsAttr); dependencyPropertiesCache[numOfSerializableDependencyProperties++] = dependencyPropertyCache; } } if (numOfSerializableDependencyProperties > 0) { TypeDependencyPropertyCache[] serializableDependencyPropertiesCache = new TypeDependencyPropertyCache[numOfSerializableDependencyProperties]; for (int indexInSerializableProperties = 0; indexInSerializableProperties < numOfSerializableDependencyProperties; indexInSerializableProperties++) { serializableDependencyPropertiesCache[indexInSerializableProperties] = dependencyPropertiesCache[indexInSerializableProperties]; } cachedItem = new TypeDependencyPropertiesCacheItem(type, serializableDependencyPropertiesCache); _typesDependencyPropertiesCacheTable[type] = cachedItem; } } } } return(cachedItem); }
/// <summary> /// Returns a collection of properties for our object. We first rely on base /// CLR properties and then we attempt to match these with dependency properties. /// </summary> public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { // Because attached properties can come and go at any time, // the set of properties we have here always needs to be rebuilt. // We have two code paths here based on filtered attributes. An attribute // filter is just a notificaiton of a filter, it doesn't actually perform // the filter. Because the default PropertyFilterAttribute is PropertyFilter.All, // it acts as a nice "don't care" in later filtering stages that TypeDescriptor // may apply. That means that regardless of the filter value, we don't have // to fiddle with adding the attribute to the property descriptor. PropertyFilterOptions filter = PropertyFilterOptions.Valid | PropertyFilterOptions.SetValues; if (attributes != null) { foreach (Attribute attr in attributes) { PropertyFilterAttribute filterAttr = attr as PropertyFilterAttribute; if (filterAttr != null) { filter = filterAttr.Filter; break; } } } if (filter == PropertyFilterOptions.None) { return(PropertyDescriptorCollection.Empty); } // First, get the set of all known registered properties in the // app domain. GetRegisteredProperties caches its results and // will automatically re-fetch if new properties have been // registered DependencyProperty[] registeredProperties = GetRegisteredProperties(); Type instanceType = _instance.GetType(); // Next, walk through them and see which ones can be attached to this // object. If our filter is specifically SetValues, we can // greatly shortcut the entire process by using the local value // enumerator. List <PropertyDescriptor> filteredProps; if (filter == PropertyFilterOptions.SetValues) { LocalValueEnumerator localEnum = _instance.GetLocalValueEnumerator(); filteredProps = new List <PropertyDescriptor>(localEnum.Count); while (localEnum.MoveNext()) { DependencyProperty dp = localEnum.Current.Property; DependencyPropertyKind kind = DependencyObjectProvider.GetDependencyPropertyKind(dp, instanceType); // For locally set values, we just want to exclude direct and internal properties. if (!kind.IsDirect && !kind.IsInternal) { DependencyObjectPropertyDescriptor dpProp = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, instanceType); filteredProps.Add(dpProp); } } } else { filteredProps = new List <PropertyDescriptor>(registeredProperties.Length); foreach (DependencyProperty dp in registeredProperties) { bool addProp = false; DependencyPropertyKind kind = DependencyObjectProvider.GetDependencyPropertyKind(dp, instanceType); if (kind.IsAttached) { // Check bit combinations that would yield true in // any case. For non-attached properties, they're all valid, so if // the valid bit is set, we're done. PropertyFilterOptions anySet = PropertyFilterOptions.SetValues | PropertyFilterOptions.UnsetValues; PropertyFilterOptions anyValid = PropertyFilterOptions.Valid | PropertyFilterOptions.Invalid; if ((filter & anySet) == anySet || (filter & anyValid) == anyValid) { addProp = true; } if (!addProp && (filter & anyValid) != 0) { bool canAttach = CanAttachProperty(dp, _instance); addProp = canAttach ^ ((filter & anyValid) == PropertyFilterOptions.Invalid); } if (!addProp && (filter & anySet) != 0) { bool shouldSerialize = _instance.ContainsValue(dp); addProp = shouldSerialize ^ ((filter & anySet) == PropertyFilterOptions.UnsetValues); } } else if ((filter & PropertyFilterOptions.SetValues) != 0 && _instance.ContainsValue(dp) && !kind.IsDirect && !kind.IsInternal) { // The property is not attached. However, it isn't an internal DP and the user // has requested set values. See if the property is set on the object and include // it if it is. addProp = true; } if (addProp) { DependencyObjectPropertyDescriptor dpProp = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, instanceType); filteredProps.Add(dpProp); } } } PropertyDescriptorCollection properties; properties = new PropertyDescriptorCollection(filteredProps.ToArray(), true); return(properties); }
private ArrayList SaveSubStreams(UIElement element) { ArrayList subStreams = null; #pragma warning disable 618 if ((element != null) && (element.PersistId != 0)) #pragma warning restore 618 { LocalValueEnumerator dpEnumerator = element.GetLocalValueEnumerator(); while (dpEnumerator.MoveNext()) { LocalValueEntry localValueEntry = (LocalValueEntry)dpEnumerator.Current; FrameworkPropertyMetadata metadata = localValueEntry.Property.GetMetadata(element.DependencyObjectType) as FrameworkPropertyMetadata; if (metadata == null) { continue; } // To be saved, a DP should have the correct metadata and NOT be an expression or data bound. // Since Bind inherits from Expression, the test for Expression will suffice. // NOTE: we do not journal expression. So we should let parser restore it in BamlRecordReader.SetDependencyValue. // Please see Windows OS if (metadata.Journal && (!(localValueEntry.Value is Expression))) { // These properties should not be journaled. // if (object.ReferenceEquals(localValueEntry.Property, Frame.SourceProperty)) { continue; } if (subStreams == null) { subStreams = new ArrayList(3); } object currentValue = element.GetValue(localValueEntry.Property); byte[] bytes = null; if ((currentValue != null) && !(currentValue is Uri)) { // Convert the value of the DP into a byte array MemoryStream byteStream = new MemoryStream(); new SecurityPermission(SecurityPermissionFlag.SerializationFormatter).Assert(); try { this.Formatter.Serialize(byteStream, currentValue); } finally { SecurityPermission.RevertAssert(); } bytes = byteStream.ToArray(); // Dispose the stream ((IDisposable)byteStream).Dispose( ); } // Save the byte array by the property name subStreams.Add(new SubStream(localValueEntry.Property.Name, bytes)); } } } return(subStreams); }
private void GenerateBehaviorActions(ActionCollection actionCollection, CodeTypeDeclaration classType, CodeMemberMethod method, CodeVariableReferenceExpression behaviorVarRef, string behaviorName) { for (int i = 0; i < actionCollection.Count; i++) { var action = actionCollection[i]; string actionName = behaviorName + "_ACT_" + i; Type type = action.GetType(); CodeVariableDeclarationStatement variable = new CodeVariableDeclarationStatement(type.Name, actionName, new CodeObjectCreateExpression(type.Name)); method.Statements.Add(variable); var actionVarRef = new CodeVariableReferenceExpression(actionName); method.Statements.Add(new CodeMethodInvokeExpression( behaviorVarRef, "Actions.Add", actionVarRef)); ValueGenerator valueGenerator = new ValueGenerator(); MethodInfo generateFieldMethod = typeof(CodeComHelper).GetMethod("GenerateField"); LocalValueEnumerator enumerator = action.GetLocalValueEnumerator(); while (enumerator.MoveNext()) { LocalValueEntry entry = enumerator.Current; DependencyProperty property = entry.Property; Type valueType = entry.Value.GetType(); if (CodeComHelper.IsValidForFieldGenerator(entry.Value)) { if (valueGenerator.Generators.ContainsKey(property.PropertyType) || valueGenerator.Generators.ContainsKey(valueType)) { CodeExpression propValue = valueGenerator.ProcessGenerators(classType, method, entry.Value, actionName); if (propValue != null) { method.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(actionVarRef, property.Name), propValue)); } } else if (entry.Value is PropertyPath) { PropertyPath path = entry.Value as PropertyPath; method.Statements.Add(new CodeAssignStatement( new CodeFieldReferenceExpression(actionVarRef, property.Name), new CodeObjectCreateExpression("PropertyPath", new CodePrimitiveExpression(path.Path)))); } else if (entry.Value is SoundSource) { var soundSource = CodeComHelper.GenerateSoundSource(method, entry.Value as SoundSource); method.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(actionVarRef, property.Name), soundSource)); } else { MethodInfo generic = generateFieldMethod.MakeGenericMethod(property.PropertyType); if (generic == null) { throw new NullReferenceException("Generic method not created for type - " + property.PropertyType); } generic.Invoke(null, new object[] { method, actionVarRef, action, property }); } } } CodeComHelper.GenerateBindings(method, actionVarRef, action, actionName, behaviorVarRef); //CodeComHelper.GenerateResourceReferences(method, actionVarRef, action); } }
/// <summary> /// Event handler for KeyDown event to auto-detect hyperlinks on space, enter and backspace keys. /// </summary> private static void OnKeyDown(object sender, KeyEventArgs e) { var myRichTextBox = (MyRichTextBox)sender; if (e.Key != Key.Space && e.Key != Key.Back && e.Key != Key.Return) { return; } if (!myRichTextBox.Selection.IsEmpty) { myRichTextBox.Selection.Text = String.Empty; } TextPointer caretPosition = myRichTextBox.Selection.Start; if (e.Key == Key.Space || e.Key == Key.Return) { TextPointer wordStartPosition; string word = GetPreceedingWordInParagraph(caretPosition, out wordStartPosition); if (word == "www.microsoft.com") // A real app would need a more sophisticated RegEx match expression for hyperlinks. { // Insert hyperlink element at word boundaries. var start = wordStartPosition.GetPositionAtOffset(0, LogicalDirection.Backward); var end = caretPosition.GetPositionAtOffset(0, LogicalDirection.Forward); if (start != null) { if (end != null) { new Hyperlink(start , end); } } // No need to update RichTextBox caret position, // since we only inserted a Hyperlink ElementEnd following current caretPosition. // Subsequent handling of space input by base RichTextBox will update selection. } } else // Key.Back { TextPointer backspacePosition = caretPosition.GetNextInsertionPosition(LogicalDirection.Backward); Hyperlink hyperlink; if (backspacePosition != null && IsHyperlinkBoundaryCrossed(caretPosition, backspacePosition, out hyperlink)) { // Remember caretPosition with forward gravity. This is necessary since we are going to delete // the hyperlink element preceeding caretPosition and after deletion current caretPosition // (with backward gravity) will follow content preceeding the hyperlink. // We want to remember content following the hyperlink to set new caret position at. TextPointer newCaretPosition = caretPosition.GetPositionAtOffset(0, LogicalDirection.Forward); // Deleting the hyperlink is done using logic below. // 1. Copy its children Inline to a temporary array. InlineCollection hyperlinkChildren = hyperlink.Inlines; var inlines = new Inline[hyperlinkChildren.Count]; hyperlinkChildren.CopyTo(inlines, 0); // 2. Remove each child from parent hyperlink element and insert it after the hyperlink. for (int i = inlines.Length - 1; i >= 0; i--) { hyperlinkChildren.Remove(inlines[i]); if (hyperlink.SiblingInlines != null) { hyperlink.SiblingInlines.InsertAfter(hyperlink, inlines[i]); } } // 3. Apply hyperlink's local formatting properties to inlines (which are now outside hyperlink scope). LocalValueEnumerator localProperties = hyperlink.GetLocalValueEnumerator(); var inlineRange = new TextRange(inlines[0].ContentStart, inlines[inlines.Length - 1].ContentEnd); while (localProperties.MoveNext()) { LocalValueEntry property = localProperties.Current; DependencyProperty dp = property.Property; object value = property.Value; if (!dp.ReadOnly && dp != Inline.TextDecorationsProperty && // Ignore hyperlink defaults. dp != TextElement.ForegroundProperty && !IsHyperlinkProperty(dp)) { inlineRange.ApplyPropertyValue(dp, value); } } // 4. Delete the (empty) hyperlink element. if (hyperlink.SiblingInlines != null) { hyperlink.SiblingInlines.Remove(hyperlink); } // 5. Update selection, since we deleted Hyperlink element and caretPosition was at that Hyperlink's end boundary. if (newCaretPosition != null) { myRichTextBox.Selection.Select(newCaretPosition, newCaretPosition); } } } }
// Copies a LocalValueEnumerator properties into an array of DependencyProperty. // This method is useful because LocalValueEnumerator does not support // setting or clearing local values while enumerating. private DependencyProperty[] LocalValueEnumeratorToArray(LocalValueEnumerator valuesEnumerator) { DependencyProperty[] properties; int count; properties = new DependencyProperty[valuesEnumerator.Count]; count = 0; valuesEnumerator.Reset(); while (valuesEnumerator.MoveNext()) { properties[count++] = valuesEnumerator.Current.Property; } return properties; }
/// <summary> /// Sets local values on the text element scoping position. /// </summary> /// <param name="position"> /// A position scoped by the element on which to set the property. /// </param> /// <param name="values"> /// Values to set. /// </param> /// <exception cref="System.InvalidOperationException"> /// Throws InvalidOperationException if position is not scoped by a /// text element or if a property value may not be applied on the /// scoping text element. /// </exception> internal void SetValues(TextPointer position, LocalValueEnumerator values) { TextElement textElement; LocalValueEntry property; // VerifyAccess(); if (position == null) { throw new ArgumentNullException("position"); } // LocalValueEnumerator is a struct. // if (values == null) // { // throw new ArgumentNullException("values"); // } EmptyDeadPositionList(); ValidateSetValue(position); BeginChange(); try { textElement = position.Parent as TextElement; Invariant.Assert(textElement != null); values.Reset(); while (values.MoveNext()) { property = values.Current; // if (property.Property.Name == "CachedSource") { continue; } // If the property is readonly on the text element, then we shouldn't // try to copy the property value. if (property.Property.ReadOnly) { continue; } // Don't copy over Run.Text. This will get automatically invalidated by TextContainer // when the Run's text content is set. Setting this property now will cause TextContainer // changes that get us into trouble in the middle of undo. if (property.Property == Run.TextProperty) { continue; } BindingExpressionBase expr = property.Value as BindingExpressionBase; if (expr != null) { // We can't duplicate a binding so copy over the current value instead. textElement.SetValue(property.Property, expr.Value); } else { textElement.SetValue(property.Property, property.Value); } } } finally { EndChange(); } }
/// <summary> /// Validates a WPF tree to determine if binding errors exist /// </summary> /// <param name="parent">Parent DependencyObject</param> /// <returns>True if no errors exist</returns> public static bool IsValid(DependencyObject parent) { if (parent == null) { throw new ArgumentNullException("parent"); } // note that this goes wrong in a data template // http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/d88f3926-68e5-488d-9fb1-a9f94a5ffd11 // http://stackoverflow.com/questions/338522/getlocalvalueenumerator-not-returning-all-properties bool valid = true; // validate parent LocalValueEnumerator localValueEnumerator = parent.GetLocalValueEnumerator(); while (localValueEnumerator.MoveNext()) { LocalValueEntry entry = localValueEnumerator.Current; if (BindingOperations.IsDataBound(parent, entry.Property)) { Binding binding = BindingOperations.GetBinding(parent, entry.Property); if (binding.Path.Path.Contains("Search")) { Debug.WriteLine("Search"); } if ((binding.Mode == BindingMode.TwoWay) || (binding.Mode == BindingMode.OneWayToSource)) { BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); expression.UpdateSource(); if (expression.HasError) { valid = false; } } } } // validate logical children bool foundLogicalChildren = false; IEnumerable children = LogicalTreeHelper.GetChildren(parent); foreach (object obj in children) { foundLogicalChildren = true; DependencyObject child = obj as DependencyObject; if (child != null) { if (!IsValid(child)) { valid = false; } } } // if no logical children found try looking for visual children if ((!foundLogicalChildren) && (parent is Visual)) { int visualChildrenCount = VisualTreeHelper.GetChildrenCount(parent); if (visualChildrenCount > 0) { for (int child = 0; child < visualChildrenCount; child++) { if (!IsValid(VisualTreeHelper.GetChild(parent, child))) { valid = false; } } } } return(valid); }
private void RemoveHyperlinkFormat() { TextPointer caretPosition = Selection.Start; TextPointer backspacePosition = caretPosition.GetNextInsertionPosition(LogicalDirection.Backward); Hyperlink hyperlink = default(Hyperlink); try { if (backspacePosition != null && IsHyperlinkBoundaryCrossed(caretPosition, backspacePosition, ref hyperlink)) { // Remember caretPosition with forward gravity. This is necessary since we are going to delete // the hyperlink element preceding caretPosition and after deletion current caretPosition // (with backward gravity) will follow content preceding the hyperlink. // We want to remember content following the hyperlink to set new caret position at. TextPointer newCaretPosition = caretPosition.GetPositionAtOffset(0, LogicalDirection.Forward); // 1. Copy its children Inline to a temporary array InlineCollection hyperlinkChildren = hyperlink.Inlines; Inline[] inlines = new Inline[hyperlinkChildren.Count]; hyperlinkChildren.CopyTo(inlines, 0); // 2. Remove each child from parent hyperlink element and insert it after the hyperlink for (int i = inlines.Length - 1; i >= 0; i--) { hyperlinkChildren.Remove(inlines[i]); if (hyperlink.SiblingInlines != null) { hyperlink.SiblingInlines.InsertAfter(hyperlink, inlines[i]); } } // 3. Apply hyperlink local formatting properties to inlines (which are now outside hyperlink scope) LocalValueEnumerator localProperties = hyperlink.GetLocalValueEnumerator(); TextRange inlineRange = new TextRange(inlines[0].ContentStart, inlines[inlines.Length - 1].ContentEnd); while (localProperties.MoveNext()) { LocalValueEntry property = localProperties.Current; DependencyProperty dependencyProperty = property.Property; object value = property.Value; // Ignore hyperlink defaults if (dependencyProperty.ReadOnly == false && dependencyProperty.Equals(Inline.TextDecorationsProperty) == false && dependencyProperty.Equals(TextElement.ForegroundProperty) == false && dependencyProperty.Equals(BaseUriHelper.BaseUriProperty) == false && IsHyperlinkProperty(dependencyProperty) == false && dependencyProperty.Name.Equals("IsEnabled") == false) { inlineRange.ApplyPropertyValue(dependencyProperty, value); } } // 4. Delete the (empty) hyperlink element if (hyperlink.SiblingInlines != null) { hyperlink.RequestNavigate -= OnRequestNavigate; hyperlink.SiblingInlines.Remove(hyperlink); } // 5. Update selection, since we deleted Hyperlink element and caretPosition was at that hyperlink's end boundary Selection.Select(newCaretPosition, newCaretPosition); } } catch (Exception ex) { Log.Error("Error while removing hyperlink format: {EX}", ex); } }