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 property.
///
public static readonly DependencyProperty TextWrappingProperty =
TextBlock.TextWrappingProperty.AddOwner(
typeof(TextBox),
new FrameworkPropertyMetadata(
TextWrapping.NoWrap,
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(OnTextWrappingChanged)));
///
/// 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 property.
///
public static readonly DependencyProperty TextAlignmentProperty = Block.TextAlignmentProperty.AddOwner(typeof(TextBox));
///
/// 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 property.
///
public static readonly DependencyProperty TextWrappingProperty =
TextBlock.TextWrappingProperty.AddOwner(
typeof(TextBox),
new FrameworkPropertyMetadata(
TextWrapping.NoWrap,
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(OnTextWrappingChanged)));
///
/// 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 property.
///
public static readonly DependencyProperty TextAlignmentProperty = Block.TextAlignmentProperty.AddOwner(typeof(TextBox));
///
/// 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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InfoCardBaseException.cs
- EnterpriseServicesHelper.cs
- HelpProvider.cs
- SingleStorage.cs
- DeploymentSectionCache.cs
- CfgSemanticTag.cs
- shaper.cs
- FlagsAttribute.cs
- AttributeUsageAttribute.cs
- CollectionDataContract.cs
- TaskHelper.cs
- TableLayoutColumnStyleCollection.cs
- DropShadowBitmapEffect.cs
- ConnectorDragDropGlyph.cs
- ConnectionProviderAttribute.cs
- ContainerControl.cs
- DataSvcMapFileSerializer.cs
- XmlNamespaceDeclarationsAttribute.cs
- AttributeEmitter.cs
- ObjectViewFactory.cs
- BaseAsyncResult.cs
- AsyncPostBackTrigger.cs
- RegisteredHiddenField.cs
- FilePrompt.cs
- MethodToken.cs
- HashMembershipCondition.cs
- COAUTHIDENTITY.cs
- VisualBrush.cs
- TriggerAction.cs
- OrderedDictionaryStateHelper.cs
- TdsEnums.cs
- TemplateBamlTreeBuilder.cs
- WpfKnownMemberInvoker.cs
- EncryptedXml.cs
- SQLGuid.cs
- DayRenderEvent.cs
- ControlSerializer.cs
- CrossAppDomainChannel.cs
- ObjectManager.cs
- Misc.cs
- ThousandthOfEmRealPoints.cs
- CollectionDataContractAttribute.cs
- GridViewDeletedEventArgs.cs
- PackageFilter.cs
- InfiniteTimeSpanConverter.cs
- RequestQueryParser.cs
- TrustLevelCollection.cs
- DbProviderManifest.cs
- RayHitTestParameters.cs
- ColorBlend.cs
- PipeStream.cs
- HwndProxyElementProvider.cs
- Soap12ProtocolReflector.cs
- DesignTimeValidationFeature.cs
- LoadGrammarCompletedEventArgs.cs
- GetPageCompletedEventArgs.cs
- ACL.cs
- CardSpaceSelector.cs
- CodeDOMUtility.cs
- FilePrompt.cs
- Directory.cs
- TdsValueSetter.cs
- SelectedDatesCollection.cs
- DataBindingHandlerAttribute.cs
- XPathNavigatorKeyComparer.cs
- Form.cs
- FileSystemWatcher.cs
- XmlKeywords.cs
- Repeater.cs
- ToolStripDesignerAvailabilityAttribute.cs
- ThicknessConverter.cs
- WebConfigurationManager.cs
- CommonObjectSecurity.cs
- StorageEntityTypeMapping.cs
- DeviceSpecificChoiceCollection.cs
- SafeWaitHandle.cs
- SynthesizerStateChangedEventArgs.cs
- VisualStyleElement.cs
- _HeaderInfo.cs
- Crc32.cs
- FlagsAttribute.cs
- DataGridViewRow.cs
- FontNamesConverter.cs
- SeekableReadStream.cs
- ItemsPanelTemplate.cs
- TimeSpanValidator.cs
- ToolStripMenuItem.cs
- UrlPath.cs
- BeginStoryboard.cs
- EventMappingSettingsCollection.cs
- RegistryDataKey.cs
- AdornerDecorator.cs
- IndexingContentUnit.cs
- CapabilitiesPattern.cs
- QilGenerator.cs
- FactoryId.cs
- EncodingInfo.cs
- PersistenceException.cs
- AggregateNode.cs
- SubqueryRules.cs