/// <summary> /// Handler for TextContainer.Changed event. /// </summary> internal override void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e) { bool resetText = false; string newTextValue = null; try { // if there are re-entrant changes, only raise public events // after the outermost change completes _changeEventNestingCount++; // Ignore property changes that originate from OnTextPropertyChange. if (!_isInsideTextContentChange) { _isInsideTextContentChange = true; // Use a DeferredTextReference instead of calculating the new // value now for better performance. Most of the time no // one cares what the new value is, and loading our content into a // string can be extremely expensive. DeferredTextReference dtr = new DeferredTextReference(this.TextContainer); _newTextValue = dtr; SetCurrentDeferredValue(TextProperty, dtr); } } finally { _changeEventNestingCount--; if (_changeEventNestingCount == 0) { // when Text is data-bound, _newTextValue is converted from a // deferred reference to a string. The binding writes the string // back to the source, then computes a new value for Text (which // may be different, either because the source normalizes the value // or because of conversion and formatting). Usually this raises // a change notification for Text, which brings the Text property and // the text container into [....]. But this doesn't happen in one // case: when the normalized value is the same as the original // value for Text. The property engine thinks that Text hasn't // changed, and doesn't raise the notification. It's true that // Text hasn't changed, but we still need to update the text container, // which now displays the wrong value. // We detect that case by checking whether _newTextValue (the // text container value) agrees with Text. if (FrameworkCompatibilityPreferences.GetKeepTextBoxDisplaySynchronizedWithTextProperty()) { newTextValue = _newTextValue as String; resetText = (newTextValue != null && newTextValue != Text); } _isInsideTextContentChange = false; _newTextValue = DependencyProperty.UnsetValue; } } if (resetText) { // The text container holds a new value which round-trips to the // old value of Text. We need to bring the text container into [....]. try { _newTextValue = newTextValue; _isInsideTextContentChange = true; ++ _changeEventNestingCount; OnTextPropertyChanged(newTextValue, Text); } finally { -- _changeEventNestingCount; _isInsideTextContentChange = false; _newTextValue = DependencyProperty.UnsetValue; } } if (_changeEventNestingCount == 0) { // Let base raise the public TextBoxBase.TextChanged event. base.OnTextContainerChanged(sender, e); } }
// if the DeferredTextReference is resolved to a string during the previous // method, track that here internal void OnDeferredTextReferenceResolved(DeferredTextReference dtr, string s) { if (dtr == _newTextValue) { _newTextValue = s; } }
/// <summary> /// Handler for TextContainer.Changed event. /// </summary> internal override void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e) { try { // if there are re-entrant changes, only raise public events // after the outermost change completes _changeEventNestingCount++; // Ignore property changes that originate from OnTextPropertyChange. if (!_isInsideTextContentChange) { _isInsideTextContentChange = true; // Use a DeferredTextReference instead of calculating the new // value now for better performance. Most of the time no // one cares what the new value is, and loading our content into a // string can be extremely expensive. DeferredTextReference dtr = new DeferredTextReference(this.TextContainer); _newTextValue = dtr; SetCurrentDeferredValue(TextProperty, dtr); } } finally { _changeEventNestingCount--; if (_changeEventNestingCount == 0) { _isInsideTextContentChange = false; _newTextValue = DependencyProperty.UnsetValue; } } if (_changeEventNestingCount == 0) { // Let base raise the public TextBoxBase.TextChanged event. base.OnTextContainerChanged(sender, e); } }