Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Controls / TextBox.cs / 1 / TextBox.cs
//---------------------------------------------------------------------------- // // File: TextBox.cs // // Copyright (C) Microsoft Corporation. All rights reserved. // // Description: The stock plain text editing control. // //--------------------------------------------------------------------------- namespace System.Windows.Controls { using MS.Internal; using System.Threading; using System.Collections; // IEnumerator using System.ComponentModel; // DefaultValue using System.Globalization; using System.Windows; using System.Windows.Media; using System.Windows.Data; // Binding using System.Windows.Documents; using System.Windows.Automation.Peers; using System.Windows.Input; // CanExecuteRoutedEventArgs, ExecuteRoutedEventArgs using System.Windows.Controls.Primitives; // TextBoxBase using System.Windows.Navigation; using System.Windows.Markup; // IAddChild, XamlDesignerSerializer, ContentPropertyAttribute using MS.Utility; using MS.Internal.Text; using MS.Internal.Automation; // TextAdaptor using MS.Internal.Documents; // Undo using MS.Internal.Commands; // CommandHelpers ////// The stock text editing control. /// [Localizability(LocalizationCategory.Text)] [ContentProperty("Text")] public class TextBox : TextBoxBase, IAddChild, ITextBoxViewHost { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Static constructor for TextBox. /// static TextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(typeof(TextBox))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(TextBox)); // Add handlers for height properties so we can manage min/maxLines PropertyChangedCallback callback = new PropertyChangedCallback(OnMinMaxChanged); HeightProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); MinHeightProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); MaxHeightProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); FontFamilyProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); FontSizeProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); // Registering typography properties metadata PropertyChangedCallback onTypographyChanged = new PropertyChangedCallback(OnTypographyChanged); DependencyProperty[] typographyProperties = Typography.TypographyPropertiesList; for (int i = 0; i < typographyProperties.Length; i++) { typographyProperties[i].OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(onTypographyChanged)); } HorizontalScrollBarVisibilityProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata( ScrollBarVisibility.Hidden, new PropertyChangedCallback(OnScrollViewerPropertyChanged), // PropertyChangedCallback new CoerceValueCallback(CoerceHorizontalScrollBarVisibility))); } ////// Constructor /// public TextBox() : base() { // Register static editing command handlers. // This only has an effect that first time we make the call. // We don't use the static ctor because there are cases // where another control will want to alias our properties // but doesn't need this overhead. TextEditor.RegisterCommandHandlers(typeof(TextBox), /*acceptsRichContent:*/false, /*readOnly*/false, /*registerEventListeners*/false); // Create TextContainer and TextEditor associated with it TextContainer container = new TextContainer(this, true /* plainTextOnly */); container.CollectTextChanges = true; InitializeTextContainer(container); // TextBox only accepts plain text, so change TextEditor's default to that. this.TextEditor.AcceptsRichContent = false; } #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods // ------------------------------------------------------------ // // IAddChild interface // // ------------------------------------------------------------ ////// Called to Add the object as a Child. /// /// /// Object to add as a child /// ////// This method will always throw InvalidOperationException because /// the TextBox only accepts plain text. /// void IAddChild.AddChild(Object value) { if (value == null) { throw new ArgumentNullException("value"); } // TextBox only accepts plain text, via IAddChild.AddText. throw new InvalidOperationException(SR.Get(SRID.TextBoxInvalidChild, value.ToString())); } ////// Called when text appears under the tag in markup. /// /// /// Text to Add to the Object /// void IAddChild.AddText(string text) { if (text == null) { throw new ArgumentNullException("text"); } this.TextContainer.End.InsertTextInRun(text); } ////// Select the text in the given position and length. /// public void Select(int start, int length) { if (start < 0) { throw new ArgumentOutOfRangeException("start", SR.Get(SRID.ParameterCannotBeNegative)); } if (length < 0) { throw new ArgumentOutOfRangeException("length", SR.Get(SRID.ParameterCannotBeNegative)); } // Identify new position for selection Start int maxStart = TextContainer.SymbolCount; if (start > maxStart) { start = maxStart; } TextPointer newStart = this.TextContainer.CreatePointerAtOffset(start, LogicalDirection.Forward); // Normalize new start in some particular direction, to exclude ambiguity on surrogates bounndaries // and to start counting length from appropriate position. newStart = newStart.GetInsertionPosition(LogicalDirection.Forward); // Identify new position for selection End int maxLength = newStart.GetOffsetToPosition(TextContainer.End); if (length > maxLength) { length = maxLength; } TextPointer newEnd = new TextPointer(newStart, length, LogicalDirection.Forward); // Normalize end in some particular direction to exclude ambiguity on surrogate boundaries newEnd = newEnd.GetInsertionPosition(LogicalDirection.Forward); // Set new selection TextSelectionInternal.Select(newStart, newEnd); } ////// Clear all the content in the TextBox control. /// public void Clear() { using (this.TextSelectionInternal.DeclareChangeBlock()) { this.TextContainer.DeleteContentInternal((TextPointer)this.TextContainer.Start, (TextPointer)this.TextContainer.End); TextSelectionInternal.Select(this.TextContainer.Start, this.TextContainer.Start); } } ////// Return the 0-based character index of the given point. If there is no character /// at that point and snapToText is false, return -1. /// /// Point in TextBox coordinate space /// if true and there is no character at the given point, will return the nearest character ///Character index at the given point, or -1 public int GetCharacterIndexFromPoint(Point point, bool snapToText) { if (this.RenderScope == null) { return -1; } TextPointer textPointer = GetTextPositionFromPointInternal(point, snapToText); if (textPointer != null) { // offset corresponds to insertion position int offset = textPointer.Offset; // return character index based on orientation of TextPointer return (textPointer.LogicalDirection == LogicalDirection.Backward) ? offset - 1 : offset; } else { return -1; } } ////// Return the 0-based character index of the first character of lineIndex. /// /// 0-based index of the line for which we want the first character index ///0-based index of the first character of lineIndex, or -1 if no layout information is available. public int GetCharacterIndexFromLineIndex(int lineIndex) { if (this.RenderScope == null) { return -1; } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } TextPointer textPointer = GetStartPositionOfLine(lineIndex); // textPointer will be null if there is no layout available. return (textPointer == null) ? 0 : textPointer.Offset; } ////// Return the 0-based index of the line containing the given character index. /// /// index of the character for which a line index is to be returned ////// 0-based index of the line containing the character at charIndex, or -1 if no /// layout information is available /// public int GetLineIndexFromCharacterIndex(int charIndex) { if (this.RenderScope == null) { return -1; } if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } Rect rect; int line; TextPointer textPointer = this.TextContainer.CreatePointerAtOffset(charIndex, LogicalDirection.Forward); if (GetRectangleFromTextPositionInternal(textPointer, /*relativeToTextBox*/false, out rect)) { rect.Y += this.VerticalOffset; line = (int)((rect.Top + rect.Height / 2) / GetLineHeight()); } else { line = -1; } return line; } ////// Return the number of characters in the given line. /// /// 0-based line index ///number of characters in the given line, or -1 if no layout information is available public int GetLineLength(int lineIndex) { if (this.RenderScope == null) { return -1; } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } TextPointer textPointerStart = GetStartPositionOfLine(lineIndex); TextPointer textPointerEnd = GetEndPositionOfLine(lineIndex); int length; if (textPointerStart == null || textPointerEnd == null) { // No layout available. length = -1; } else { length = textPointerStart.GetOffsetToPosition(textPointerEnd); } return length; } ////// Return the index of the first line that is currently visible in the TextBox. /// ///0-based index of the first visible line, or -1 if no layout information is available public int GetFirstVisibleLineIndex() { if (this.RenderScope == null) { return -1; } // Include an epsilon in the calculation below to account for floating // point rounding error. Example: suppose we're looking for line 10. // Because of rounding error, we calculate line 9.9999, take the // Floor, and get the previous line. const double epsilon = 0.0001; double lineHeight = GetLineHeight(); return (int)Math.Floor((this.VerticalOffset / lineHeight) + epsilon); } ////// Return the index of the last line that is currently visible in the TextBox. /// ///0-based index of the last visible line, or -1 if no layout information is available public int GetLastVisibleLineIndex() { double height; if (this.RenderScope == null) { return -1; } height = ((IScrollInfo)this.RenderScope).ExtentHeight; if (this.VerticalOffset + this.ViewportHeight >= height) { return this.LineCount - 1; } else { return (int)Math.Floor((this.VerticalOffset + this.ViewportHeight - 1) / GetLineHeight()); } } ////// Scroll the minimal amount necessary to bring the given line into full view. /// /// line to scroll into view public void ScrollToLine(int lineIndex) { if (this.RenderScope == null) { return; } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } TextPointer textPointer = GetStartPositionOfLine(lineIndex); Rect rect; if (GetRectangleFromTextPositionInternal(textPointer, false, out rect)) { this.RenderScope.BringIntoView(rect); } } ////// Get the text displayed at the given line. /// /// 0-based index of the desired line ///String containing a copy of the text at the given line index, or null if no layout information /// is available public String GetLineText(int lineIndex) { string text; TextPointer startOfLine; TextPointer endOfLine; if (this.RenderScope == null) { return null; // sentinel value } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } startOfLine = GetStartPositionOfLine(lineIndex); endOfLine = GetEndPositionOfLine(lineIndex); // startOfLine/endOfLine will be null if no layout is available. if (startOfLine != null && endOfLine != null) { text = TextRangeBase.GetTextInternal(startOfLine, endOfLine); } else { text = this.Text; } return text; } ////// Get the rectangle for the leading edge of the character at the given index. /// /// index of the desired character ///leading edge rectangle of the given character, or Rect.Empty if no layout information is available. public Rect GetRectFromCharacterIndex(int charIndex) { return GetRectFromCharacterIndex(charIndex, /*trailingEdge*/false); } ////// Get the rectangle for an edge of the character at the given index. /// /// index of the desired character /// specifies an edge of the character bounding box ///leading or trailing edge rectangle of the given character, or Rect.Empty if no layout information is available. public Rect GetRectFromCharacterIndex(int charIndex, bool trailingEdge) { if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } // Start by moving to an insertion position in backward direction. // This ensures that when the character at charIndex is part of a surrogate pair or multi-byte character, // we handle leading/trailing edge correctly. TextPointer textPointer = TextContainer.CreatePointerAtOffset(charIndex, LogicalDirection.Backward); textPointer = textPointer.GetInsertionPosition(LogicalDirection.Backward); if (trailingEdge && charIndex < this.TextContainer.SymbolCount) { // Get next insertion position textPointer = textPointer.GetNextInsertionPosition(LogicalDirection.Forward); Invariant.Assert(textPointer != null); // Backward gravity for trailing edge textPointer = textPointer.GetPositionAtOffset(0, LogicalDirection.Backward); } else { // Forward gravity for leading edge textPointer = textPointer.GetPositionAtOffset(0, LogicalDirection.Forward); } // NB: rect will be Rect.Empty if no layout is available. Rect rect; GetRectangleFromTextPositionInternal(textPointer, /*relativeToTextBox*/true, out rect); return rect; } ////// Returns the associated IndexedSpellingError at a specified character index. /// /// /// Index of text to query. /// ////// The charIndex paramter specifies a character to query. /// If the specificed character is not part of a misspelled word (or if /// IsSpellCheckEnabled == false) then this method will return null. /// public SpellingError GetSpellingError(int charIndex) { if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } TextPointer position = this.TextContainer.CreatePointerAtOffset(charIndex, LogicalDirection.Forward); SpellingError spellingError = this.TextEditor.GetSpellingErrorAtPosition(position, LogicalDirection.Forward); if (spellingError == null && charIndex < this.TextContainer.SymbolCount - 1) { position = this.TextContainer.CreatePointerAtOffset(charIndex + 1, LogicalDirection.Forward); spellingError = this.TextEditor.GetSpellingErrorAtPosition(position, LogicalDirection.Backward); } return spellingError; } ////// Returns the start index of the first character of a spelling error /// containing a specified char. /// /// /// Index of the character to query. /// ////// Start index of the spelling error containing a specified char, or -1 if /// the char is not part of a spelling error. /// public int GetSpellingErrorStart(int charIndex) { SpellingError spellingError = GetSpellingError(charIndex); return (spellingError == null) ? -1 : spellingError.Start.Offset; } ////// Returns the length of the spelling error containing a specified char. /// /// /// Index of the character to query. /// ////// Length of the spelling error containing a specified char, or 0 if /// the char is not part of a spelling error. /// public int GetSpellingErrorLength(int charIndex) { SpellingError spellingError = GetSpellingError(charIndex); return (spellingError == null) ? 0 : spellingError.End.Offset - spellingError.Start.Offset; } ////// Returns the index of the next character in a specificed direction /// that is the start of a misspelled word. /// /// /// Index of text to query. /// /// /// Direction to query. /// ////// The charIndex paramter specifies a character at which to start the query. /// When direction == LogicalDirection.Forward, the search includes the /// spelling error containing charIndex (if any). /// When direction == LogicalDirection.Backward, the search does not /// include the error containing charIndex (if any). /// /// If no misspelled word is encountered, the method returns -1. /// public int GetNextSpellingErrorCharacterIndex(int charIndex, LogicalDirection direction) { if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } if (this.TextContainer.SymbolCount == 0) { // Early out on an empty doc to keep logic simpler below. return -1; } ITextPointer position = this.TextContainer.CreatePointerAtOffset(charIndex, direction); position = this.TextEditor.GetNextSpellingErrorPosition(position, direction); return (position == null) ? -1 : position.Offset; } #endregion Public Methods //----------------------------------------------------- // // Public Properties // //------------------------------------------------------ #region Public Properties ////// DependencyProperty for public static readonly DependencyProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner( typeof(TextBox), new FrameworkPropertyMetadata( TextWrapping.NoWrap, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnTextWrappingChanged))); ///property. /// /// The TextWrapping property controls whether or not text wraps /// when it reaches the flow edge of its containing block box. /// public TextWrapping TextWrapping { get { return (TextWrapping)GetValue(TextWrappingProperty); } set { SetValue(TextWrappingProperty, value); } } ////// Dependency ID for the MinLines property /// Default value: 1 /// public static readonly DependencyProperty MinLinesProperty = DependencyProperty.Register( "MinLines", // Property name typeof(int), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata( 1, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnMinMaxChanged)), new ValidateValueCallback(MinLinesValidateValue)); ////// Minimum number of lines to size to. /// [DefaultValue(1)] public int MinLines { get { return (int) GetValue(MinLinesProperty); } set { SetValue(MinLinesProperty, value); } } ////// Dependency ID for the MaxLines property /// Default value: MaxInt /// public static readonly DependencyProperty MaxLinesProperty = DependencyProperty.Register( "MaxLines", // Property name typeof(int), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata( Int32.MaxValue, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnMinMaxChanged)), new ValidateValueCallback(MaxLinesValidateValue)); ////// Minimum number of lines to size to. /// [DefaultValue(Int32.MaxValue)] public int MaxLines { get { return (int) GetValue(MaxLinesProperty); } set { SetValue(MaxLinesProperty, value); } } ////// The DependencyID for the Text property. /// Default Value: "" /// public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", // Property name typeof(string), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata( // Property metadata string.Empty, // default value FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | // Flags FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnTextPropertyChanged), // property changed callback new CoerceValueCallback(CoerceText), true, // IsAnimationProhibited UpdateSourceTrigger.LostFocus // DefaultUpdateSourceTrigger )); ////// Contents of the TextBox. /// [DefaultValue("")] [Localizability(LocalizationCategory.Text)] public string Text { get { return (string) GetValue(TextProperty); } set { SetValue(TextProperty, value); } } ////// The DependencyID for the CharacterCasing property. /// Controls whether or not input text is converted to upper or lower case /// Flags: Can be used in style rules /// Default Value: CharacterCasing.Normal /// public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.Register( "CharacterCasing", // Property name typeof(CharacterCasing), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata(CharacterCasing.Normal /*default value*/), new ValidateValueCallback(CharacterCasingValidateValue) /*validation callback*/); ////// Character casing of the TextBox /// public CharacterCasing CharacterCasing { get { return (CharacterCasing) GetValue(CharacterCasingProperty); } set { SetValue(CharacterCasingProperty, value); } } ////// The limit number of characters that the textbox or other editable controls can contain. /// if it is 0, means no-limitation. /// User can set this value for some simple single line textbox to restrict the text number. /// RichTextBox doesn't have this limitation. /// By default it is 0. /// ////// When this property is set to zero, the maximum length of the text that can be entered /// in the control is limited only by available memory. You can use this property to restrict /// the length of text entered in the control for values such as postal codes and telephone numbers. /// You can also use this property to restrict the length of text entered when the data is to be entered /// in a database. /// You can limit the text entered into the control to the maximum length of the corresponding field in the database. /// Note: In code, you can set the value of the Text property to a value that is larger than /// the value specified by the MaxLength property. /// This property only affects text entered into the control at runtime. /// public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register( "MaxLength", // Property name typeof(int), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata(0), /*default value*/ new ValidateValueCallback(MaxLengthValidateValue)); ////// Maximum number of characters the TextBox can accept /// [DefaultValue((int)0)] [Localizability(LocalizationCategory.None, Modifiability = Modifiability.Unmodifiable)] // cannot be modified by localizer public int MaxLength { get { return (int) GetValue(MaxLengthProperty); } set { SetValue(MaxLengthProperty, value); } } ////// DependencyProperty for public static readonly DependencyProperty TextAlignmentProperty = Block.TextAlignmentProperty.AddOwner(typeof(TextBox)); ///property. /// /// The TextAlignment property specifies horizontal alignment of the content. /// public TextAlignment TextAlignment { get { return (TextAlignment)GetValue(TextAlignmentProperty); } set { SetValue(TextAlignmentProperty, value); } } ////// Selected Text /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string SelectedText { get { return TextSelectionInternal.Text; } set { using (this.TextSelectionInternal.DeclareChangeBlock()) { TextSelectionInternal.Text = value; } } } ////// Character number of the selected text /// ////// Length is calculated as unicode count, so it counts /// eacn \r\n combination as 2 - even though it is actially /// one caret position, and it would be illegal to insert /// any characters between them or expect selection ends /// to stay between them. /// Because of that after setting SelectionLength to some value /// it can be automatically corrected (by adding 1) /// if selection end happens to be between \r and \n. /// [DefaultValue((int)0)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionLength { get { return TextSelectionInternal.Start.GetOffsetToPosition(TextSelectionInternal.End); } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.ParameterCannotBeNegative)); } // Identify new position for selection end int maxLength = TextSelectionInternal.Start.GetOffsetToPosition(TextContainer.End); if (value > maxLength) { value = maxLength; } TextPointer newEnd = new TextPointer(TextSelectionInternal.Start, value, LogicalDirection.Forward); // Normalize end in some particular direction to exclude ambiguity on surrogate boundaries newEnd = newEnd.GetInsertionPosition(LogicalDirection.Forward); // Set new selection TextSelectionInternal.Select(TextSelectionInternal.Start, newEnd); } } ////// The start position of the selection. /// ////// Index is calculated as unicode offset, so it counts /// eacn \r\n combination as 2 - even though it is actially /// one caret position, and it would be illegal to insert /// any characters between them or expect selection ends /// to stay between them. /// Because of that after setting SelectionStart to some value /// it can be automatically corrected (by adding 1) /// if it happens to be between \r and \n. /// [DefaultValue((int)0)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionStart { get { return this.TextSelectionInternal.Start.Offset; } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.ParameterCannotBeNegative)); } // Store current length of the selection int selectionLength = TextSelectionInternal.Start.GetOffsetToPosition(TextSelectionInternal.End); // Identify new position for selection Start int maxStart = TextContainer.SymbolCount; if (value > maxStart) { value = maxStart; } TextPointer newStart = TextContainer.CreatePointerAtOffset(value, LogicalDirection.Forward); // Normalize new start in some particular direction, to exclude ambiguity on surrogates bounndaries // and to start counting length from appropriate position. newStart = newStart.GetInsertionPosition(LogicalDirection.Forward); // Identify new position for selection End int maxLength = newStart.GetOffsetToPosition(TextContainer.End); if (selectionLength > maxLength) { selectionLength = maxLength; } TextPointer newEnd = new TextPointer(newStart, selectionLength, LogicalDirection.Forward); // Normalize end in some particular direction to exclude ambiguity on surrogate boundaries newEnd = newEnd.GetInsertionPosition(LogicalDirection.Forward); // Set new selection TextSelectionInternal.Select(newStart, newEnd); } } ////// Position of the caret. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int CaretIndex { get { return SelectionStart; } set { Select(value, 0); } } ////// Number of lines in the TextBox. /// ///number of lines in the TextBox, or -1 if no layout information is available ////// If Wrap == true, changing the width of the TextBox may change this value. /// The value returned is the number of lines in the entire TextBox, regardless of how many are /// currently in view. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int LineCount { get { if (this.RenderScope == null) { return -1; } return GetLineIndexFromCharacterIndex(this.TextContainer.SymbolCount) + 1; } } ////// DependencyProperty for the TextDecorations property. /// public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner( typeof(TextBox), new FrameworkPropertyMetadata( new FreezableDefaultValueFactory(TextDecorationCollection.Empty), FrameworkPropertyMetadataOptions.AffectsRender)); ////// Property used to apply decorations such as underline to content. /// public TextDecorationCollection TextDecorations { get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); } set { SetValue(TextDecorationsProperty, value); } } ////// Access to all text typography properties. /// public Typography Typography { get { return new Typography(this); } } #endregion Public Properties //----------------------------------------------------- // // Protected Methods // //----------------------------------------------------- #region Protected Methods ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new TextBoxAutomationPeer(this); } /// ///) /// /// protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { // always call base.OnPropertyChanged, otherwise Property Engine will not work. base.OnPropertyChanged(e); if (this.RenderScope != null) { FrameworkPropertyMetadata fmetadata = e.Property.GetMetadata(typeof(TextBox)) as FrameworkPropertyMetadata; if (fmetadata != null) { if (e.IsAValueChange || e.IsASubPropertyChange) { if (fmetadata.AffectsMeasure || fmetadata.AffectsArrange || fmetadata.AffectsParentMeasure || fmetadata.AffectsParentArrange || e.Property == Control.HorizontalContentAlignmentProperty || e.Property == Control.VerticalContentAlignmentProperty) { ((TextBoxView)this.RenderScope).Remeasure(); } else if (fmetadata.AffectsRender && (e.IsAValueChange || !fmetadata.SubPropertiesDoNotAffectRender)) { ((TextBoxView)this.RenderScope).Rerender(); } if (Speller.IsSpellerAffectingProperty(e.Property) && this.TextEditor.Speller != null) { this.TextEditor.Speller.ResetErrors(); } } } } TextBoxAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as TextBoxAutomationPeer; if (peer != null) { if (e.Property == TextProperty) { peer.RaiseValuePropertyChangedEvent((string)e.OldValue, (string)e.NewValue); } if (e.Property == IsReadOnlyProperty) { peer.RaiseIsReadOnlyPropertyChangedEvent((bool)e.OldValue, (bool)e.NewValue); } } } /// /// Returns enumerator to logical children. /// protected internal override IEnumerator LogicalChildren { get { // return new RangeContentEnumerator((TextPointer)this.TextContainer.Start, (TextPointer)this.TextContainer.End); } } ////// Measurement override. Implement your size-to-content logic here. /// /// /// Sizing constraint. /// protected override Size MeasureOverride(Size constraint) { if (MinLines > 1 && MaxLines < MinLines) { throw new Exception(SR.Get(SRID.TextBoxMinMaxLinesMismatch)); } Size size = base.MeasureOverride(constraint); if (_minmaxChanged) { // If there is a scrollViewer, we'll listen to the ScrollChanged event and // handle min/maxLines there. if (this.ScrollViewer == null) { SetRenderScopeMinMaxHeight(); } else { SetScrollViewerMinMaxHeight(); } _minmaxChanged = false; } return size; } // Called every time after Wrap property gets new value internal void OnTextWrappingChanged() { CoerceValue(HorizontalScrollBarVisibilityProperty); } // Allocates the initial render scope for this control. internal override FrameworkElement CreateRenderScope() { return new TextBoxView(this); } #endregion Protected Methods //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Detaches the editor from old visual tree and attaches it to a new one /// internal override void AttachToVisualTree() { base.AttachToVisualTree(); if (this.RenderScope == null) { return; } // Set TextWrapping property for the new renderScope OnTextWrappingChanged(); // We need to recalculate our min/max story. _minmaxChanged = true; } ////// Gives a string representation of this object. /// internal override string GetPlainText() { return this.Text; } /// // Returns the DependencyObjectType for the registered ThemeStyleKey's default // value. Controls will override this method to return approriate types. internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } ////// Scroll content by one line to the top. /// internal override void DoLineUp() { if (this.ScrollViewer != null) { ScrollViewer.ScrollToVerticalOffset(VerticalOffset - GetLineHeight()); } } ////// Scroll content by one line to the bottom. /// internal override void DoLineDown() { if (this.ScrollViewer != null) { ScrollViewer.ScrollToVerticalOffset(VerticalOffset + GetLineHeight()); } } ////// Handler for TextContainer.Changed event. /// internal override void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e) { // Ignore property changes that originate from OnTextPropertyChange. if (!_isInsideTextContentChange) { _isInsideTextContentChange = true; try { // Use a DeferredTextReference instead of calculating the new // value now for better performance. Most of the time no // one cares what the new is, and loading our content into a // string can be extremely expensive. SetDeferredValue(TextProperty, new DeferredTextReference(this.TextContainer)); } finally { _isInsideTextContentChange = false; } } // Let base raise the public TextBoxBase.TextChanged event. base.OnTextContainerChanged(sender, e); } ////// Handler for ScrollViewer's OnScrollChanged event. /// internal override void OnScrollChanged(object sender, ScrollChangedEventArgs e) { base.OnScrollChanged(sender, e); if (e.ViewportHeightChange != 0) { SetScrollViewerMinMaxHeight(); } } // // This property // 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject // 2. This is a performance optimization // internal override int EffectiveValuesInitialSize { get { return 42; } } #endregion Internal methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties ////// Text Selection (readonly) /// internal TextSelection Selection { get { return (TextSelection)TextSelectionInternal; } } ////// TextPointer where the TextBox's text begins (readonly) /// internal TextPointer StartPosition { get { return (TextPointer)this.TextContainer.Start; } } ////// TextPointer where the TextBox's text ends (readonly) /// internal TextPointer EndPosition { get { return (TextPointer)this.TextContainer.End; } } ////// IsTypographyDefaultValue /// internal bool IsTypographyDefaultValue { get { return !_isTypographySet; } } // ITextContainer holding the Control content. ITextContainer ITextBoxViewHost.TextContainer { get { return this.TextContainer; } } // Set true when typography property values are all default values. bool ITextBoxViewHost.IsTypographyDefaultValue { get { return this.IsTypographyDefaultValue; } } #endregion Internal Properties //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #region Private Methods // Returns false if no layout is available. private bool GetRectangleFromTextPositionInternal(TextPointer position, bool relativeToTextBox, out Rect rect) { if (this.RenderScope == null) { rect = Rect.Empty; return false; } if (position.ValidateLayout()) { rect = TextPointerBase.GetCharacterRect(position, position.LogicalDirection, relativeToTextBox); } else { rect = Rect.Empty; } return rect != Rect.Empty; } // Returns null if no layout is available. private TextPointer GetStartPositionOfLine(int lineIndex) { if (this.RenderScope == null) { return null; } Point point = new Point(); // all lines in TextBox are the same height, so get the line height and multiply... double lineHeight = GetLineHeight(); point.Y = lineHeight * lineIndex + (lineHeight / 2) - VerticalOffset; // use a point in the middle of the line, to be safe point.X = -HorizontalOffset; TextPointer textPointer; if (TextEditor.GetTextView(this.RenderScope).Validate(point)) { textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetTextPositionFromPoint(point, /* snap to text */ true); textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetLineRange(textPointer).Start.CreatePointer(textPointer.LogicalDirection); } else { textPointer = null; } return textPointer; } private TextPointer GetEndPositionOfLine(int lineIndex) { if (this.RenderScope == null) { return null; } // all lines in TextBox are the same height, so get the line height and multiply... Point point = new Point(); double lineHeight = GetLineHeight(); point.Y = lineHeight * lineIndex + (lineHeight / 2) - VerticalOffset; // use a point in the middle of the line, to be safe point.X = 0; TextPointer textPointer; if (TextEditor.GetTextView(this.RenderScope).Validate(point)) { textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetTextPositionFromPoint(point, /* snap to text */ true); textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetLineRange(textPointer).End.CreatePointer(textPointer.LogicalDirection); // Hit testing ignores line breaks, so the position returned will be between the last visible character // and the line break, if any. We want the position AFTER the line break. if (TextPointerBase.IsNextToPlainLineBreak(textPointer, LogicalDirection.Forward)) { textPointer.MoveToNextInsertionPosition(LogicalDirection.Forward); } } else { textPointer = null; } return textPointer; } private static object CoerceHorizontalScrollBarVisibility(DependencyObject d, object value) { TextBox textBox = d as TextBox; if (textBox != null && (textBox.TextWrapping == TextWrapping.Wrap || textBox.TextWrapping == TextWrapping.WrapWithOverflow)) { return ScrollBarVisibility.Disabled; } return value; } ////// private static bool MaxLengthValidateValue(object value) { return ((int)value) >= 0; } ////// /// private static bool CharacterCasingValidateValue(object value) { return (CharacterCasing.Normal <= (CharacterCasing)value && (CharacterCasing)value <= CharacterCasing.Upper); } ////// /// private static bool MinLinesValidateValue(object value) { return ((int)value > 0); } ////// /// private static bool MaxLinesValidateValue(object value) { return ((int)value > 0); } ////// /// Callback for changes to the MinLines and MaxLines property /// private static void OnMinMaxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox textBox = (TextBox)d; textBox._minmaxChanged = true; } ////// Callback for changes to the Text property /// private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox textBox = (TextBox)d; if (textBox._isInsideTextContentChange) { // Ignore property changes that originate from OnTextContainerChanged. return; } // CoerceText will have already converted null -> String.Empty, // but our default CoerceValueCallback could be overridden by a // derived class. So check again here. string newText = (string)e.NewValue; if (newText == null) { newText = String.Empty; } textBox._isInsideTextContentChange = true; try { using (textBox.TextSelectionInternal.DeclareChangeBlock()) { // Update the text content with new TextProperty value. textBox.TextContainer.DeleteContentInternal((TextPointer)textBox.TextContainer.Start, (TextPointer)textBox.TextContainer.End); textBox.TextContainer.End.InsertTextInRun(newText); // Collapse selection to the beginning of a text box textBox.Select(0, 0); } } finally { // textBox._isInsideTextContentChange = false; } // We need to clear undo stack in case when the value comes from // databinding or some other expression. if (textBox.HasExpression(textBox.LookupEntry(TextBox.TextProperty.GlobalIndex), TextBox.TextProperty)) { UndoManager undoManager = textBox.TextEditor._GetUndoManager(); undoManager.Clear(); } } ////// Callback for changes to the TextWrapping property /// private static void OnTextWrappingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TextBox) { ((TextBox)d).OnTextWrappingChanged(); } } ////// Update the value of ScrollViewer.MinHeight/MaxHeight /// private void SetScrollViewerMinMaxHeight() { if (this.RenderScope == null) { return; } if (ReadLocalValue(HeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MaxHeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MinHeightProperty) != DependencyProperty.UnsetValue) { // scrub ScrollViewer's min/max height if any height values are set on TextBox this.ScrollViewer.ClearValue(MinHeightProperty); this.ScrollViewer.ClearValue(MaxHeightProperty); return; } double chrome = this.ScrollViewer.ActualHeight - ViewportHeight; double lineHeight = GetLineHeight(); double value = chrome + (lineHeight * MinLines); if (MinLines > 1 && this.ScrollViewer.MinHeight != value) { this.ScrollViewer.MinHeight = value; } value = chrome + (lineHeight * MaxLines); if (MaxLines < Int32.MaxValue && this.ScrollViewer.MaxHeight != value) { this.ScrollViewer.MaxHeight = value; } } ////// Update the value of RenderScope.MinHeight/MaxHeight /// private void SetRenderScopeMinMaxHeight() { if (this.RenderScope == null) { return; } if (ReadLocalValue(HeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MaxHeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MinHeightProperty) != DependencyProperty.UnsetValue) { RenderScope.ClearValue(MinHeightProperty); RenderScope.ClearValue(MaxHeightProperty); } else { double lineHeight = GetLineHeight(); double value = lineHeight * MinLines; if (MinLines > 1 && RenderScope.MinHeight != value) { RenderScope.MinHeight = value; } value = lineHeight * MaxLines; if (MaxLines < Int32.MaxValue && RenderScope.MaxHeight != value) { RenderScope.MaxHeight = value; } } } // // Called by MeasureOverride to get the height of one line of text in the current font. // private double GetLineHeight() { // change Text height based on line size FontFamily fontFamily = (FontFamily)this.GetValue(FontFamilyProperty); double fontSize = (double)this.GetValue(TextElement.FontSizeProperty); // If Ps Task 25254 is completed (not likely in V1), LineStackingStrategy // won't be constant and we'll need to call some sort of CalcLineAdvance method. return fontFamily.LineSpacing * fontSize; } // // Only serialize Text when not using the XamlTextHostSerializer // ////// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeText(XamlDesignerSerializationManager manager) { return manager.XmlWriter == null; } // // Callback for command system to verify that the LineUp / LineDown commands should be enabled. // ScrollViewer always returns true, so we follow suit. // private static void OnQueryScrollCommand(object target, CanExecuteRoutedEventArgs args) { args.CanExecute = true; } // Callback from the property system, after a new value is set to the TextProperty. // Note we cannot assume value is a string here -- it may be a DeferredTextReference. private static object CoerceText(DependencyObject d, object value) { if (value == null) { return String.Empty; } return value; } // typography properties changed, no cache for this, just reset the flag private static void OnTypographyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox textbox = (TextBox)d; textbox._isTypographySet = true; } #endregion Private methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private static DependencyObjectType _dType; // // This flag is set when the MinLines or MaxLines properties are invalidated, and // checked in MeasureOverride. When true, MeasureOverride calls SetMinMaxHeight to // make sure the change in min/max height happens immediately. private bool _minmaxChanged; // Flag used to prevent reentrancy between nested // OnTextPropertyChanged/OnTextContainerChanged callbacks. private bool _isInsideTextContentChange; // Flag used to indicate that Typography properties are not at default values private bool _isTypographySet; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // File: TextBox.cs // // Copyright (C) Microsoft Corporation. All rights reserved. // // Description: The stock plain text editing control. // //--------------------------------------------------------------------------- namespace System.Windows.Controls { using MS.Internal; using System.Threading; using System.Collections; // IEnumerator using System.ComponentModel; // DefaultValue using System.Globalization; using System.Windows; using System.Windows.Media; using System.Windows.Data; // Binding using System.Windows.Documents; using System.Windows.Automation.Peers; using System.Windows.Input; // CanExecuteRoutedEventArgs, ExecuteRoutedEventArgs using System.Windows.Controls.Primitives; // TextBoxBase using System.Windows.Navigation; using System.Windows.Markup; // IAddChild, XamlDesignerSerializer, ContentPropertyAttribute using MS.Utility; using MS.Internal.Text; using MS.Internal.Automation; // TextAdaptor using MS.Internal.Documents; // Undo using MS.Internal.Commands; // CommandHelpers ////// The stock text editing control. /// [Localizability(LocalizationCategory.Text)] [ContentProperty("Text")] public class TextBox : TextBoxBase, IAddChild, ITextBoxViewHost { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Static constructor for TextBox. /// static TextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(typeof(TextBox))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(TextBox)); // Add handlers for height properties so we can manage min/maxLines PropertyChangedCallback callback = new PropertyChangedCallback(OnMinMaxChanged); HeightProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); MinHeightProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); MaxHeightProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); FontFamilyProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); FontSizeProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(callback)); // Registering typography properties metadata PropertyChangedCallback onTypographyChanged = new PropertyChangedCallback(OnTypographyChanged); DependencyProperty[] typographyProperties = Typography.TypographyPropertiesList; for (int i = 0; i < typographyProperties.Length; i++) { typographyProperties[i].OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(onTypographyChanged)); } HorizontalScrollBarVisibilityProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata( ScrollBarVisibility.Hidden, new PropertyChangedCallback(OnScrollViewerPropertyChanged), // PropertyChangedCallback new CoerceValueCallback(CoerceHorizontalScrollBarVisibility))); } ////// Constructor /// public TextBox() : base() { // Register static editing command handlers. // This only has an effect that first time we make the call. // We don't use the static ctor because there are cases // where another control will want to alias our properties // but doesn't need this overhead. TextEditor.RegisterCommandHandlers(typeof(TextBox), /*acceptsRichContent:*/false, /*readOnly*/false, /*registerEventListeners*/false); // Create TextContainer and TextEditor associated with it TextContainer container = new TextContainer(this, true /* plainTextOnly */); container.CollectTextChanges = true; InitializeTextContainer(container); // TextBox only accepts plain text, so change TextEditor's default to that. this.TextEditor.AcceptsRichContent = false; } #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods // ------------------------------------------------------------ // // IAddChild interface // // ------------------------------------------------------------ ////// Called to Add the object as a Child. /// /// /// Object to add as a child /// ////// This method will always throw InvalidOperationException because /// the TextBox only accepts plain text. /// void IAddChild.AddChild(Object value) { if (value == null) { throw new ArgumentNullException("value"); } // TextBox only accepts plain text, via IAddChild.AddText. throw new InvalidOperationException(SR.Get(SRID.TextBoxInvalidChild, value.ToString())); } ////// Called when text appears under the tag in markup. /// /// /// Text to Add to the Object /// void IAddChild.AddText(string text) { if (text == null) { throw new ArgumentNullException("text"); } this.TextContainer.End.InsertTextInRun(text); } ////// Select the text in the given position and length. /// public void Select(int start, int length) { if (start < 0) { throw new ArgumentOutOfRangeException("start", SR.Get(SRID.ParameterCannotBeNegative)); } if (length < 0) { throw new ArgumentOutOfRangeException("length", SR.Get(SRID.ParameterCannotBeNegative)); } // Identify new position for selection Start int maxStart = TextContainer.SymbolCount; if (start > maxStart) { start = maxStart; } TextPointer newStart = this.TextContainer.CreatePointerAtOffset(start, LogicalDirection.Forward); // Normalize new start in some particular direction, to exclude ambiguity on surrogates bounndaries // and to start counting length from appropriate position. newStart = newStart.GetInsertionPosition(LogicalDirection.Forward); // Identify new position for selection End int maxLength = newStart.GetOffsetToPosition(TextContainer.End); if (length > maxLength) { length = maxLength; } TextPointer newEnd = new TextPointer(newStart, length, LogicalDirection.Forward); // Normalize end in some particular direction to exclude ambiguity on surrogate boundaries newEnd = newEnd.GetInsertionPosition(LogicalDirection.Forward); // Set new selection TextSelectionInternal.Select(newStart, newEnd); } ////// Clear all the content in the TextBox control. /// public void Clear() { using (this.TextSelectionInternal.DeclareChangeBlock()) { this.TextContainer.DeleteContentInternal((TextPointer)this.TextContainer.Start, (TextPointer)this.TextContainer.End); TextSelectionInternal.Select(this.TextContainer.Start, this.TextContainer.Start); } } ////// Return the 0-based character index of the given point. If there is no character /// at that point and snapToText is false, return -1. /// /// Point in TextBox coordinate space /// if true and there is no character at the given point, will return the nearest character ///Character index at the given point, or -1 public int GetCharacterIndexFromPoint(Point point, bool snapToText) { if (this.RenderScope == null) { return -1; } TextPointer textPointer = GetTextPositionFromPointInternal(point, snapToText); if (textPointer != null) { // offset corresponds to insertion position int offset = textPointer.Offset; // return character index based on orientation of TextPointer return (textPointer.LogicalDirection == LogicalDirection.Backward) ? offset - 1 : offset; } else { return -1; } } ////// Return the 0-based character index of the first character of lineIndex. /// /// 0-based index of the line for which we want the first character index ///0-based index of the first character of lineIndex, or -1 if no layout information is available. public int GetCharacterIndexFromLineIndex(int lineIndex) { if (this.RenderScope == null) { return -1; } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } TextPointer textPointer = GetStartPositionOfLine(lineIndex); // textPointer will be null if there is no layout available. return (textPointer == null) ? 0 : textPointer.Offset; } ////// Return the 0-based index of the line containing the given character index. /// /// index of the character for which a line index is to be returned ////// 0-based index of the line containing the character at charIndex, or -1 if no /// layout information is available /// public int GetLineIndexFromCharacterIndex(int charIndex) { if (this.RenderScope == null) { return -1; } if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } Rect rect; int line; TextPointer textPointer = this.TextContainer.CreatePointerAtOffset(charIndex, LogicalDirection.Forward); if (GetRectangleFromTextPositionInternal(textPointer, /*relativeToTextBox*/false, out rect)) { rect.Y += this.VerticalOffset; line = (int)((rect.Top + rect.Height / 2) / GetLineHeight()); } else { line = -1; } return line; } ////// Return the number of characters in the given line. /// /// 0-based line index ///number of characters in the given line, or -1 if no layout information is available public int GetLineLength(int lineIndex) { if (this.RenderScope == null) { return -1; } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } TextPointer textPointerStart = GetStartPositionOfLine(lineIndex); TextPointer textPointerEnd = GetEndPositionOfLine(lineIndex); int length; if (textPointerStart == null || textPointerEnd == null) { // No layout available. length = -1; } else { length = textPointerStart.GetOffsetToPosition(textPointerEnd); } return length; } ////// Return the index of the first line that is currently visible in the TextBox. /// ///0-based index of the first visible line, or -1 if no layout information is available public int GetFirstVisibleLineIndex() { if (this.RenderScope == null) { return -1; } // Include an epsilon in the calculation below to account for floating // point rounding error. Example: suppose we're looking for line 10. // Because of rounding error, we calculate line 9.9999, take the // Floor, and get the previous line. const double epsilon = 0.0001; double lineHeight = GetLineHeight(); return (int)Math.Floor((this.VerticalOffset / lineHeight) + epsilon); } ////// Return the index of the last line that is currently visible in the TextBox. /// ///0-based index of the last visible line, or -1 if no layout information is available public int GetLastVisibleLineIndex() { double height; if (this.RenderScope == null) { return -1; } height = ((IScrollInfo)this.RenderScope).ExtentHeight; if (this.VerticalOffset + this.ViewportHeight >= height) { return this.LineCount - 1; } else { return (int)Math.Floor((this.VerticalOffset + this.ViewportHeight - 1) / GetLineHeight()); } } ////// Scroll the minimal amount necessary to bring the given line into full view. /// /// line to scroll into view public void ScrollToLine(int lineIndex) { if (this.RenderScope == null) { return; } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } TextPointer textPointer = GetStartPositionOfLine(lineIndex); Rect rect; if (GetRectangleFromTextPositionInternal(textPointer, false, out rect)) { this.RenderScope.BringIntoView(rect); } } ////// Get the text displayed at the given line. /// /// 0-based index of the desired line ///String containing a copy of the text at the given line index, or null if no layout information /// is available public String GetLineText(int lineIndex) { string text; TextPointer startOfLine; TextPointer endOfLine; if (this.RenderScope == null) { return null; // sentinel value } if (lineIndex < 0 || lineIndex >= LineCount) { throw new ArgumentOutOfRangeException("lineIndex"); } startOfLine = GetStartPositionOfLine(lineIndex); endOfLine = GetEndPositionOfLine(lineIndex); // startOfLine/endOfLine will be null if no layout is available. if (startOfLine != null && endOfLine != null) { text = TextRangeBase.GetTextInternal(startOfLine, endOfLine); } else { text = this.Text; } return text; } ////// Get the rectangle for the leading edge of the character at the given index. /// /// index of the desired character ///leading edge rectangle of the given character, or Rect.Empty if no layout information is available. public Rect GetRectFromCharacterIndex(int charIndex) { return GetRectFromCharacterIndex(charIndex, /*trailingEdge*/false); } ////// Get the rectangle for an edge of the character at the given index. /// /// index of the desired character /// specifies an edge of the character bounding box ///leading or trailing edge rectangle of the given character, or Rect.Empty if no layout information is available. public Rect GetRectFromCharacterIndex(int charIndex, bool trailingEdge) { if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } // Start by moving to an insertion position in backward direction. // This ensures that when the character at charIndex is part of a surrogate pair or multi-byte character, // we handle leading/trailing edge correctly. TextPointer textPointer = TextContainer.CreatePointerAtOffset(charIndex, LogicalDirection.Backward); textPointer = textPointer.GetInsertionPosition(LogicalDirection.Backward); if (trailingEdge && charIndex < this.TextContainer.SymbolCount) { // Get next insertion position textPointer = textPointer.GetNextInsertionPosition(LogicalDirection.Forward); Invariant.Assert(textPointer != null); // Backward gravity for trailing edge textPointer = textPointer.GetPositionAtOffset(0, LogicalDirection.Backward); } else { // Forward gravity for leading edge textPointer = textPointer.GetPositionAtOffset(0, LogicalDirection.Forward); } // NB: rect will be Rect.Empty if no layout is available. Rect rect; GetRectangleFromTextPositionInternal(textPointer, /*relativeToTextBox*/true, out rect); return rect; } ////// Returns the associated IndexedSpellingError at a specified character index. /// /// /// Index of text to query. /// ////// The charIndex paramter specifies a character to query. /// If the specificed character is not part of a misspelled word (or if /// IsSpellCheckEnabled == false) then this method will return null. /// public SpellingError GetSpellingError(int charIndex) { if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } TextPointer position = this.TextContainer.CreatePointerAtOffset(charIndex, LogicalDirection.Forward); SpellingError spellingError = this.TextEditor.GetSpellingErrorAtPosition(position, LogicalDirection.Forward); if (spellingError == null && charIndex < this.TextContainer.SymbolCount - 1) { position = this.TextContainer.CreatePointerAtOffset(charIndex + 1, LogicalDirection.Forward); spellingError = this.TextEditor.GetSpellingErrorAtPosition(position, LogicalDirection.Backward); } return spellingError; } ////// Returns the start index of the first character of a spelling error /// containing a specified char. /// /// /// Index of the character to query. /// ////// Start index of the spelling error containing a specified char, or -1 if /// the char is not part of a spelling error. /// public int GetSpellingErrorStart(int charIndex) { SpellingError spellingError = GetSpellingError(charIndex); return (spellingError == null) ? -1 : spellingError.Start.Offset; } ////// Returns the length of the spelling error containing a specified char. /// /// /// Index of the character to query. /// ////// Length of the spelling error containing a specified char, or 0 if /// the char is not part of a spelling error. /// public int GetSpellingErrorLength(int charIndex) { SpellingError spellingError = GetSpellingError(charIndex); return (spellingError == null) ? 0 : spellingError.End.Offset - spellingError.Start.Offset; } ////// Returns the index of the next character in a specificed direction /// that is the start of a misspelled word. /// /// /// Index of text to query. /// /// /// Direction to query. /// ////// The charIndex paramter specifies a character at which to start the query. /// When direction == LogicalDirection.Forward, the search includes the /// spelling error containing charIndex (if any). /// When direction == LogicalDirection.Backward, the search does not /// include the error containing charIndex (if any). /// /// If no misspelled word is encountered, the method returns -1. /// public int GetNextSpellingErrorCharacterIndex(int charIndex, LogicalDirection direction) { if (charIndex < 0 || charIndex > this.TextContainer.SymbolCount) { throw new ArgumentOutOfRangeException("charIndex"); } if (this.TextContainer.SymbolCount == 0) { // Early out on an empty doc to keep logic simpler below. return -1; } ITextPointer position = this.TextContainer.CreatePointerAtOffset(charIndex, direction); position = this.TextEditor.GetNextSpellingErrorPosition(position, direction); return (position == null) ? -1 : position.Offset; } #endregion Public Methods //----------------------------------------------------- // // Public Properties // //------------------------------------------------------ #region Public Properties ////// DependencyProperty for public static readonly DependencyProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner( typeof(TextBox), new FrameworkPropertyMetadata( TextWrapping.NoWrap, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnTextWrappingChanged))); ///property. /// /// The TextWrapping property controls whether or not text wraps /// when it reaches the flow edge of its containing block box. /// public TextWrapping TextWrapping { get { return (TextWrapping)GetValue(TextWrappingProperty); } set { SetValue(TextWrappingProperty, value); } } ////// Dependency ID for the MinLines property /// Default value: 1 /// public static readonly DependencyProperty MinLinesProperty = DependencyProperty.Register( "MinLines", // Property name typeof(int), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata( 1, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnMinMaxChanged)), new ValidateValueCallback(MinLinesValidateValue)); ////// Minimum number of lines to size to. /// [DefaultValue(1)] public int MinLines { get { return (int) GetValue(MinLinesProperty); } set { SetValue(MinLinesProperty, value); } } ////// Dependency ID for the MaxLines property /// Default value: MaxInt /// public static readonly DependencyProperty MaxLinesProperty = DependencyProperty.Register( "MaxLines", // Property name typeof(int), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata( Int32.MaxValue, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnMinMaxChanged)), new ValidateValueCallback(MaxLinesValidateValue)); ////// Minimum number of lines to size to. /// [DefaultValue(Int32.MaxValue)] public int MaxLines { get { return (int) GetValue(MaxLinesProperty); } set { SetValue(MaxLinesProperty, value); } } ////// The DependencyID for the Text property. /// Default Value: "" /// public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", // Property name typeof(string), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata( // Property metadata string.Empty, // default value FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | // Flags FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnTextPropertyChanged), // property changed callback new CoerceValueCallback(CoerceText), true, // IsAnimationProhibited UpdateSourceTrigger.LostFocus // DefaultUpdateSourceTrigger )); ////// Contents of the TextBox. /// [DefaultValue("")] [Localizability(LocalizationCategory.Text)] public string Text { get { return (string) GetValue(TextProperty); } set { SetValue(TextProperty, value); } } ////// The DependencyID for the CharacterCasing property. /// Controls whether or not input text is converted to upper or lower case /// Flags: Can be used in style rules /// Default Value: CharacterCasing.Normal /// public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.Register( "CharacterCasing", // Property name typeof(CharacterCasing), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata(CharacterCasing.Normal /*default value*/), new ValidateValueCallback(CharacterCasingValidateValue) /*validation callback*/); ////// Character casing of the TextBox /// public CharacterCasing CharacterCasing { get { return (CharacterCasing) GetValue(CharacterCasingProperty); } set { SetValue(CharacterCasingProperty, value); } } ////// The limit number of characters that the textbox or other editable controls can contain. /// if it is 0, means no-limitation. /// User can set this value for some simple single line textbox to restrict the text number. /// RichTextBox doesn't have this limitation. /// By default it is 0. /// ////// When this property is set to zero, the maximum length of the text that can be entered /// in the control is limited only by available memory. You can use this property to restrict /// the length of text entered in the control for values such as postal codes and telephone numbers. /// You can also use this property to restrict the length of text entered when the data is to be entered /// in a database. /// You can limit the text entered into the control to the maximum length of the corresponding field in the database. /// Note: In code, you can set the value of the Text property to a value that is larger than /// the value specified by the MaxLength property. /// This property only affects text entered into the control at runtime. /// public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register( "MaxLength", // Property name typeof(int), // Property type typeof(TextBox), // Property owner new FrameworkPropertyMetadata(0), /*default value*/ new ValidateValueCallback(MaxLengthValidateValue)); ////// Maximum number of characters the TextBox can accept /// [DefaultValue((int)0)] [Localizability(LocalizationCategory.None, Modifiability = Modifiability.Unmodifiable)] // cannot be modified by localizer public int MaxLength { get { return (int) GetValue(MaxLengthProperty); } set { SetValue(MaxLengthProperty, value); } } ////// DependencyProperty for public static readonly DependencyProperty TextAlignmentProperty = Block.TextAlignmentProperty.AddOwner(typeof(TextBox)); ///property. /// /// The TextAlignment property specifies horizontal alignment of the content. /// public TextAlignment TextAlignment { get { return (TextAlignment)GetValue(TextAlignmentProperty); } set { SetValue(TextAlignmentProperty, value); } } ////// Selected Text /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string SelectedText { get { return TextSelectionInternal.Text; } set { using (this.TextSelectionInternal.DeclareChangeBlock()) { TextSelectionInternal.Text = value; } } } ////// Character number of the selected text /// ////// Length is calculated as unicode count, so it counts /// eacn \r\n combination as 2 - even though it is actially /// one caret position, and it would be illegal to insert /// any characters between them or expect selection ends /// to stay between them. /// Because of that after setting SelectionLength to some value /// it can be automatically corrected (by adding 1) /// if selection end happens to be between \r and \n. /// [DefaultValue((int)0)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionLength { get { return TextSelectionInternal.Start.GetOffsetToPosition(TextSelectionInternal.End); } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.ParameterCannotBeNegative)); } // Identify new position for selection end int maxLength = TextSelectionInternal.Start.GetOffsetToPosition(TextContainer.End); if (value > maxLength) { value = maxLength; } TextPointer newEnd = new TextPointer(TextSelectionInternal.Start, value, LogicalDirection.Forward); // Normalize end in some particular direction to exclude ambiguity on surrogate boundaries newEnd = newEnd.GetInsertionPosition(LogicalDirection.Forward); // Set new selection TextSelectionInternal.Select(TextSelectionInternal.Start, newEnd); } } ////// The start position of the selection. /// ////// Index is calculated as unicode offset, so it counts /// eacn \r\n combination as 2 - even though it is actially /// one caret position, and it would be illegal to insert /// any characters between them or expect selection ends /// to stay between them. /// Because of that after setting SelectionStart to some value /// it can be automatically corrected (by adding 1) /// if it happens to be between \r and \n. /// [DefaultValue((int)0)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionStart { get { return this.TextSelectionInternal.Start.Offset; } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", SR.Get(SRID.ParameterCannotBeNegative)); } // Store current length of the selection int selectionLength = TextSelectionInternal.Start.GetOffsetToPosition(TextSelectionInternal.End); // Identify new position for selection Start int maxStart = TextContainer.SymbolCount; if (value > maxStart) { value = maxStart; } TextPointer newStart = TextContainer.CreatePointerAtOffset(value, LogicalDirection.Forward); // Normalize new start in some particular direction, to exclude ambiguity on surrogates bounndaries // and to start counting length from appropriate position. newStart = newStart.GetInsertionPosition(LogicalDirection.Forward); // Identify new position for selection End int maxLength = newStart.GetOffsetToPosition(TextContainer.End); if (selectionLength > maxLength) { selectionLength = maxLength; } TextPointer newEnd = new TextPointer(newStart, selectionLength, LogicalDirection.Forward); // Normalize end in some particular direction to exclude ambiguity on surrogate boundaries newEnd = newEnd.GetInsertionPosition(LogicalDirection.Forward); // Set new selection TextSelectionInternal.Select(newStart, newEnd); } } ////// Position of the caret. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int CaretIndex { get { return SelectionStart; } set { Select(value, 0); } } ////// Number of lines in the TextBox. /// ///number of lines in the TextBox, or -1 if no layout information is available ////// If Wrap == true, changing the width of the TextBox may change this value. /// The value returned is the number of lines in the entire TextBox, regardless of how many are /// currently in view. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int LineCount { get { if (this.RenderScope == null) { return -1; } return GetLineIndexFromCharacterIndex(this.TextContainer.SymbolCount) + 1; } } ////// DependencyProperty for the TextDecorations property. /// public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner( typeof(TextBox), new FrameworkPropertyMetadata( new FreezableDefaultValueFactory(TextDecorationCollection.Empty), FrameworkPropertyMetadataOptions.AffectsRender)); ////// Property used to apply decorations such as underline to content. /// public TextDecorationCollection TextDecorations { get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); } set { SetValue(TextDecorationsProperty, value); } } ////// Access to all text typography properties. /// public Typography Typography { get { return new Typography(this); } } #endregion Public Properties //----------------------------------------------------- // // Protected Methods // //----------------------------------------------------- #region Protected Methods ////// Creates AutomationPeer ( protected override AutomationPeer OnCreateAutomationPeer() { return new TextBoxAutomationPeer(this); } /// ///) /// /// protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { // always call base.OnPropertyChanged, otherwise Property Engine will not work. base.OnPropertyChanged(e); if (this.RenderScope != null) { FrameworkPropertyMetadata fmetadata = e.Property.GetMetadata(typeof(TextBox)) as FrameworkPropertyMetadata; if (fmetadata != null) { if (e.IsAValueChange || e.IsASubPropertyChange) { if (fmetadata.AffectsMeasure || fmetadata.AffectsArrange || fmetadata.AffectsParentMeasure || fmetadata.AffectsParentArrange || e.Property == Control.HorizontalContentAlignmentProperty || e.Property == Control.VerticalContentAlignmentProperty) { ((TextBoxView)this.RenderScope).Remeasure(); } else if (fmetadata.AffectsRender && (e.IsAValueChange || !fmetadata.SubPropertiesDoNotAffectRender)) { ((TextBoxView)this.RenderScope).Rerender(); } if (Speller.IsSpellerAffectingProperty(e.Property) && this.TextEditor.Speller != null) { this.TextEditor.Speller.ResetErrors(); } } } } TextBoxAutomationPeer peer = UIElementAutomationPeer.FromElement(this) as TextBoxAutomationPeer; if (peer != null) { if (e.Property == TextProperty) { peer.RaiseValuePropertyChangedEvent((string)e.OldValue, (string)e.NewValue); } if (e.Property == IsReadOnlyProperty) { peer.RaiseIsReadOnlyPropertyChangedEvent((bool)e.OldValue, (bool)e.NewValue); } } } /// /// Returns enumerator to logical children. /// protected internal override IEnumerator LogicalChildren { get { // return new RangeContentEnumerator((TextPointer)this.TextContainer.Start, (TextPointer)this.TextContainer.End); } } ////// Measurement override. Implement your size-to-content logic here. /// /// /// Sizing constraint. /// protected override Size MeasureOverride(Size constraint) { if (MinLines > 1 && MaxLines < MinLines) { throw new Exception(SR.Get(SRID.TextBoxMinMaxLinesMismatch)); } Size size = base.MeasureOverride(constraint); if (_minmaxChanged) { // If there is a scrollViewer, we'll listen to the ScrollChanged event and // handle min/maxLines there. if (this.ScrollViewer == null) { SetRenderScopeMinMaxHeight(); } else { SetScrollViewerMinMaxHeight(); } _minmaxChanged = false; } return size; } // Called every time after Wrap property gets new value internal void OnTextWrappingChanged() { CoerceValue(HorizontalScrollBarVisibilityProperty); } // Allocates the initial render scope for this control. internal override FrameworkElement CreateRenderScope() { return new TextBoxView(this); } #endregion Protected Methods //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Detaches the editor from old visual tree and attaches it to a new one /// internal override void AttachToVisualTree() { base.AttachToVisualTree(); if (this.RenderScope == null) { return; } // Set TextWrapping property for the new renderScope OnTextWrappingChanged(); // We need to recalculate our min/max story. _minmaxChanged = true; } ////// Gives a string representation of this object. /// internal override string GetPlainText() { return this.Text; } /// // Returns the DependencyObjectType for the registered ThemeStyleKey's default // value. Controls will override this method to return approriate types. internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } ////// Scroll content by one line to the top. /// internal override void DoLineUp() { if (this.ScrollViewer != null) { ScrollViewer.ScrollToVerticalOffset(VerticalOffset - GetLineHeight()); } } ////// Scroll content by one line to the bottom. /// internal override void DoLineDown() { if (this.ScrollViewer != null) { ScrollViewer.ScrollToVerticalOffset(VerticalOffset + GetLineHeight()); } } ////// Handler for TextContainer.Changed event. /// internal override void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e) { // Ignore property changes that originate from OnTextPropertyChange. if (!_isInsideTextContentChange) { _isInsideTextContentChange = true; try { // Use a DeferredTextReference instead of calculating the new // value now for better performance. Most of the time no // one cares what the new is, and loading our content into a // string can be extremely expensive. SetDeferredValue(TextProperty, new DeferredTextReference(this.TextContainer)); } finally { _isInsideTextContentChange = false; } } // Let base raise the public TextBoxBase.TextChanged event. base.OnTextContainerChanged(sender, e); } ////// Handler for ScrollViewer's OnScrollChanged event. /// internal override void OnScrollChanged(object sender, ScrollChangedEventArgs e) { base.OnScrollChanged(sender, e); if (e.ViewportHeightChange != 0) { SetScrollViewerMinMaxHeight(); } } // // This property // 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject // 2. This is a performance optimization // internal override int EffectiveValuesInitialSize { get { return 42; } } #endregion Internal methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties ////// Text Selection (readonly) /// internal TextSelection Selection { get { return (TextSelection)TextSelectionInternal; } } ////// TextPointer where the TextBox's text begins (readonly) /// internal TextPointer StartPosition { get { return (TextPointer)this.TextContainer.Start; } } ////// TextPointer where the TextBox's text ends (readonly) /// internal TextPointer EndPosition { get { return (TextPointer)this.TextContainer.End; } } ////// IsTypographyDefaultValue /// internal bool IsTypographyDefaultValue { get { return !_isTypographySet; } } // ITextContainer holding the Control content. ITextContainer ITextBoxViewHost.TextContainer { get { return this.TextContainer; } } // Set true when typography property values are all default values. bool ITextBoxViewHost.IsTypographyDefaultValue { get { return this.IsTypographyDefaultValue; } } #endregion Internal Properties //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #region Private Methods // Returns false if no layout is available. private bool GetRectangleFromTextPositionInternal(TextPointer position, bool relativeToTextBox, out Rect rect) { if (this.RenderScope == null) { rect = Rect.Empty; return false; } if (position.ValidateLayout()) { rect = TextPointerBase.GetCharacterRect(position, position.LogicalDirection, relativeToTextBox); } else { rect = Rect.Empty; } return rect != Rect.Empty; } // Returns null if no layout is available. private TextPointer GetStartPositionOfLine(int lineIndex) { if (this.RenderScope == null) { return null; } Point point = new Point(); // all lines in TextBox are the same height, so get the line height and multiply... double lineHeight = GetLineHeight(); point.Y = lineHeight * lineIndex + (lineHeight / 2) - VerticalOffset; // use a point in the middle of the line, to be safe point.X = -HorizontalOffset; TextPointer textPointer; if (TextEditor.GetTextView(this.RenderScope).Validate(point)) { textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetTextPositionFromPoint(point, /* snap to text */ true); textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetLineRange(textPointer).Start.CreatePointer(textPointer.LogicalDirection); } else { textPointer = null; } return textPointer; } private TextPointer GetEndPositionOfLine(int lineIndex) { if (this.RenderScope == null) { return null; } // all lines in TextBox are the same height, so get the line height and multiply... Point point = new Point(); double lineHeight = GetLineHeight(); point.Y = lineHeight * lineIndex + (lineHeight / 2) - VerticalOffset; // use a point in the middle of the line, to be safe point.X = 0; TextPointer textPointer; if (TextEditor.GetTextView(this.RenderScope).Validate(point)) { textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetTextPositionFromPoint(point, /* snap to text */ true); textPointer = (TextPointer)TextEditor.GetTextView(this.RenderScope).GetLineRange(textPointer).End.CreatePointer(textPointer.LogicalDirection); // Hit testing ignores line breaks, so the position returned will be between the last visible character // and the line break, if any. We want the position AFTER the line break. if (TextPointerBase.IsNextToPlainLineBreak(textPointer, LogicalDirection.Forward)) { textPointer.MoveToNextInsertionPosition(LogicalDirection.Forward); } } else { textPointer = null; } return textPointer; } private static object CoerceHorizontalScrollBarVisibility(DependencyObject d, object value) { TextBox textBox = d as TextBox; if (textBox != null && (textBox.TextWrapping == TextWrapping.Wrap || textBox.TextWrapping == TextWrapping.WrapWithOverflow)) { return ScrollBarVisibility.Disabled; } return value; } ////// private static bool MaxLengthValidateValue(object value) { return ((int)value) >= 0; } ////// /// private static bool CharacterCasingValidateValue(object value) { return (CharacterCasing.Normal <= (CharacterCasing)value && (CharacterCasing)value <= CharacterCasing.Upper); } ////// /// private static bool MinLinesValidateValue(object value) { return ((int)value > 0); } ////// /// private static bool MaxLinesValidateValue(object value) { return ((int)value > 0); } ////// /// Callback for changes to the MinLines and MaxLines property /// private static void OnMinMaxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox textBox = (TextBox)d; textBox._minmaxChanged = true; } ////// Callback for changes to the Text property /// private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox textBox = (TextBox)d; if (textBox._isInsideTextContentChange) { // Ignore property changes that originate from OnTextContainerChanged. return; } // CoerceText will have already converted null -> String.Empty, // but our default CoerceValueCallback could be overridden by a // derived class. So check again here. string newText = (string)e.NewValue; if (newText == null) { newText = String.Empty; } textBox._isInsideTextContentChange = true; try { using (textBox.TextSelectionInternal.DeclareChangeBlock()) { // Update the text content with new TextProperty value. textBox.TextContainer.DeleteContentInternal((TextPointer)textBox.TextContainer.Start, (TextPointer)textBox.TextContainer.End); textBox.TextContainer.End.InsertTextInRun(newText); // Collapse selection to the beginning of a text box textBox.Select(0, 0); } } finally { // textBox._isInsideTextContentChange = false; } // We need to clear undo stack in case when the value comes from // databinding or some other expression. if (textBox.HasExpression(textBox.LookupEntry(TextBox.TextProperty.GlobalIndex), TextBox.TextProperty)) { UndoManager undoManager = textBox.TextEditor._GetUndoManager(); undoManager.Clear(); } } ////// Callback for changes to the TextWrapping property /// private static void OnTextWrappingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is TextBox) { ((TextBox)d).OnTextWrappingChanged(); } } ////// Update the value of ScrollViewer.MinHeight/MaxHeight /// private void SetScrollViewerMinMaxHeight() { if (this.RenderScope == null) { return; } if (ReadLocalValue(HeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MaxHeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MinHeightProperty) != DependencyProperty.UnsetValue) { // scrub ScrollViewer's min/max height if any height values are set on TextBox this.ScrollViewer.ClearValue(MinHeightProperty); this.ScrollViewer.ClearValue(MaxHeightProperty); return; } double chrome = this.ScrollViewer.ActualHeight - ViewportHeight; double lineHeight = GetLineHeight(); double value = chrome + (lineHeight * MinLines); if (MinLines > 1 && this.ScrollViewer.MinHeight != value) { this.ScrollViewer.MinHeight = value; } value = chrome + (lineHeight * MaxLines); if (MaxLines < Int32.MaxValue && this.ScrollViewer.MaxHeight != value) { this.ScrollViewer.MaxHeight = value; } } ////// Update the value of RenderScope.MinHeight/MaxHeight /// private void SetRenderScopeMinMaxHeight() { if (this.RenderScope == null) { return; } if (ReadLocalValue(HeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MaxHeightProperty) != DependencyProperty.UnsetValue || ReadLocalValue(MinHeightProperty) != DependencyProperty.UnsetValue) { RenderScope.ClearValue(MinHeightProperty); RenderScope.ClearValue(MaxHeightProperty); } else { double lineHeight = GetLineHeight(); double value = lineHeight * MinLines; if (MinLines > 1 && RenderScope.MinHeight != value) { RenderScope.MinHeight = value; } value = lineHeight * MaxLines; if (MaxLines < Int32.MaxValue && RenderScope.MaxHeight != value) { RenderScope.MaxHeight = value; } } } // // Called by MeasureOverride to get the height of one line of text in the current font. // private double GetLineHeight() { // change Text height based on line size FontFamily fontFamily = (FontFamily)this.GetValue(FontFamilyProperty); double fontSize = (double)this.GetValue(TextElement.FontSizeProperty); // If Ps Task 25254 is completed (not likely in V1), LineStackingStrategy // won't be constant and we'll need to call some sort of CalcLineAdvance method. return fontFamily.LineSpacing * fontSize; } // // Only serialize Text when not using the XamlTextHostSerializer // ////// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeText(XamlDesignerSerializationManager manager) { return manager.XmlWriter == null; } // // Callback for command system to verify that the LineUp / LineDown commands should be enabled. // ScrollViewer always returns true, so we follow suit. // private static void OnQueryScrollCommand(object target, CanExecuteRoutedEventArgs args) { args.CanExecute = true; } // Callback from the property system, after a new value is set to the TextProperty. // Note we cannot assume value is a string here -- it may be a DeferredTextReference. private static object CoerceText(DependencyObject d, object value) { if (value == null) { return String.Empty; } return value; } // typography properties changed, no cache for this, just reset the flag private static void OnTypographyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBox textbox = (TextBox)d; textbox._isTypographySet = true; } #endregion Private methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private static DependencyObjectType _dType; // // This flag is set when the MinLines or MaxLines properties are invalidated, and // checked in MeasureOverride. When true, MeasureOverride calls SetMinMaxHeight to // make sure the change in min/max height happens immediately. private bool _minmaxChanged; // Flag used to prevent reentrancy between nested // OnTextPropertyChanged/OnTextContainerChanged callbacks. private bool _isInsideTextContentChange; // Flag used to indicate that Typography properties are not at default values private bool _isTypographySet; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
![Network programming in C#, Network Programming in VB.NET, Network Programming in .NET](/images/book.jpg)
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CLRBindingWorker.cs
- ExcludePathInfo.cs
- DuplicateWaitObjectException.cs
- CodeObjectCreateExpression.cs
- ListenerElementsCollection.cs
- IgnoreFileBuildProvider.cs
- DataSetSchema.cs
- VariableModifiersHelper.cs
- ListBoxItem.cs
- WebServiceHostFactory.cs
- CodeCatchClause.cs
- RegistrationServices.cs
- NullPackagingPolicy.cs
- HwndKeyboardInputProvider.cs
- PrintDocument.cs
- EntityTransaction.cs
- DbReferenceCollection.cs
- DataGridView.cs
- HWStack.cs
- HtmlEmptyTagControlBuilder.cs
- PerfCounters.cs
- ErrorInfoXmlDocument.cs
- TreeNodeCollection.cs
- ContentFileHelper.cs
- VectorAnimationUsingKeyFrames.cs
- _SSPIWrapper.cs
- ParentUndoUnit.cs
- AnnotationResourceCollection.cs
- COM2AboutBoxPropertyDescriptor.cs
- QueryResponse.cs
- DataBoundControlAdapter.cs
- TextBox.cs
- ResXFileRef.cs
- AttributeUsageAttribute.cs
- AuthStoreRoleProvider.cs
- ProfilePropertySettings.cs
- ConfigurationElementProperty.cs
- XsdValidatingReader.cs
- PerformanceCountersElement.cs
- OraclePermissionAttribute.cs
- FtpWebResponse.cs
- AttributeEmitter.cs
- CodeAttributeDeclarationCollection.cs
- ParserOptions.cs
- printdlgexmarshaler.cs
- RTLAwareMessageBox.cs
- ExpressionNormalizer.cs
- DataGridViewColumnHeaderCell.cs
- RelatedView.cs
- WorkflowInstance.cs
- _WebProxyDataBuilder.cs
- ValueProviderWrapper.cs
- XPathNodeHelper.cs
- SchemaAttDef.cs
- DictationGrammar.cs
- Int64Animation.cs
- PeerNameRegistration.cs
- DbConnectionPoolOptions.cs
- DeviceSpecific.cs
- HandlerFactoryCache.cs
- Monitor.cs
- WorkflowMarkupSerializationManager.cs
- ProxyAttribute.cs
- ModelFactory.cs
- SignHashRequest.cs
- DesignerActionVerbList.cs
- ServicePointManager.cs
- ConfigsHelper.cs
- ContentDisposition.cs
- FlowNode.cs
- SHA512.cs
- ConnectionManagementElement.cs
- UIAgentInitializationException.cs
- CompositeScriptReference.cs
- StylusPointPropertyUnit.cs
- DataColumnMappingCollection.cs
- XmlTextAttribute.cs
- RetrieveVirtualItemEventArgs.cs
- WebCategoryAttribute.cs
- HwndKeyboardInputProvider.cs
- controlskin.cs
- ImageCodecInfo.cs
- SqlInternalConnectionTds.cs
- QilLoop.cs
- DefaultEventAttribute.cs
- FontWeight.cs
- DocumentAutomationPeer.cs
- ValidatorCollection.cs
- NullExtension.cs
- NativeActivityContext.cs
- AuthenticationSection.cs
- DataControlField.cs
- CmsInterop.cs
- ToolStripStatusLabel.cs
- CommunicationObjectAbortedException.cs
- GeneralTransform3DTo2D.cs
- Transform.cs
- ManagedFilter.cs
- CheckBoxField.cs
- SQLInt16.cs