Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / WinForms / Managed / System / WinForms / TextBoxBase.cs / 1 / TextBoxBase.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Text; using System.Runtime.Serialization.Formatters; using System.Runtime.InteropServices; using System.Runtime.Remoting; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Collections; using System.Collections.Specialized; using System.Security.Permissions; using System.Windows.Forms.Design; using System.Windows.Forms.Layout; using System.ComponentModel.Design; using System.ComponentModel; using System.Drawing; using System.Windows.Forms.Internal; using System.Drawing.Design; using Microsoft.Win32; using System.Reflection; using System.Globalization; ////// /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), DefaultEvent("TextChanged"), DefaultBindingProperty("Text"), Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + AssemblyRef.SystemDesign) ] public abstract class TextBoxBase : Control { // The boolean properties for this control are contained in the textBoxFlags bit // vector. We can store up to 32 boolean values in this one vector. Here we // create the bitmasks for each bit in the vector. // private static readonly int autoSize = BitVector32.CreateMask(); private static readonly int hideSelection = BitVector32.CreateMask(autoSize); private static readonly int multiline = BitVector32.CreateMask(hideSelection); private static readonly int modified = BitVector32.CreateMask(multiline); private static readonly int readOnly = BitVector32.CreateMask(modified); private static readonly int acceptsTab = BitVector32.CreateMask(readOnly); private static readonly int wordWrap = BitVector32.CreateMask(acceptsTab); private static readonly int creatingHandle = BitVector32.CreateMask(wordWrap); private static readonly int codeUpdateText = BitVector32.CreateMask(creatingHandle); private static readonly int shortcutsEnabled = BitVector32.CreateMask(codeUpdateText); private static readonly int scrollToCaretOnHandleCreated = BitVector32.CreateMask(shortcutsEnabled); private static readonly int setSelectionOnHandleCreated = BitVector32.CreateMask(scrollToCaretOnHandleCreated); private static readonly object EVENT_ACCEPTSTABCHANGED = new object(); private static readonly object EVENT_BORDERSTYLECHANGED = new object(); private static readonly object EVENT_HIDESELECTIONCHANGED = new object(); private static readonly object EVENT_MODIFIEDCHANGED = new object(); private static readonly object EVENT_MULTILINECHANGED = new object(); private static readonly object EVENT_READONLYCHANGED = new object(); ////// Implements the basic functionality required by text /// controls. /// ////// /// The current border for this edit control. /// private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; ////// /// Controls the maximum length of text in the edit control. /// private int maxLength = 32767; // Win9X default, used for consistency ////// /// Used by the autoSizing code to help figure out the desired height of /// the edit box. /// private int requestedHeight; bool integralHeightAdjust = false; //these indices are used to cache the values of the selection, by doing this //if the handle isn't created yet, we don't force a creation. private int selectionStart = 0; private int selectionLength = 0; ////// Controls firing of click event (Left click). /// This is used by TextBox, RichTextBox and MaskedTextBox, code was moved down from TextBox/RichTextBox /// but cannot make it as default behavior to avoid introducing breaking changes. /// private bool doubleClickFired = false; private static int[] shortcutsToDisable; // We store all boolean properties in here. // private BitVector32 textBoxFlags = new BitVector32(); ////// /// Creates a new TextBox control. Uses the parent's current font and color /// set. /// internal TextBoxBase() : base() { // this class overrides GetPreferredSizeCore, let Control automatically cache the result SetState2(STATE2_USEPREFERREDSIZECACHE, true); textBoxFlags[autoSize | hideSelection | wordWrap | shortcutsEnabled] = true; SetStyle(ControlStyles.FixedHeight, textBoxFlags[autoSize]); SetStyle(ControlStyles.StandardClick | ControlStyles.StandardDoubleClick | ControlStyles.UseTextForAccessibility | ControlStyles.UserPaint, false); // cache requestedHeight. Note: Control calls DefaultSize (overridable) in the constructor // to set the control's cached height that is returned when calling Height, so we just // need to get the cached height here. requestedHeight = Height; } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TextBoxAcceptsTabDescr) ] public bool AcceptsTab { get { return textBoxFlags[acceptsTab]; } set { if (textBoxFlags[acceptsTab] != value) { textBoxFlags[acceptsTab] = value; OnAcceptsTabChanged(EventArgs.Empty); } } } ////// Gets or sets /// a value indicating whether pressing the TAB key /// in a multiline text box control types /// a TAB character in the control instead of moving the focus to the next control /// in the tab order. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnAcceptsTabChangedDescr)] public event EventHandler AcceptsTabChanged { add { Events.AddHandler(EVENT_ACCEPTSTABCHANGED, value); } remove { Events.RemoveHandler(EVENT_ACCEPTSTABCHANGED, value); } } ///[To be supplied.] ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TextBoxShortcutsEnabledDescr) ] public virtual bool ShortcutsEnabled { get { return textBoxFlags[shortcutsEnabled]; } set { if (shortcutsToDisable == null) { shortcutsToDisable = new int[] {(int)Shortcut.CtrlZ, (int)Shortcut.CtrlC, (int)Shortcut.CtrlX, (int)Shortcut.CtrlV, (int)Shortcut.CtrlA, (int)Shortcut.CtrlL, (int)Shortcut.CtrlR, (int)Shortcut.CtrlE, (int)Shortcut.CtrlY, (int)Keys.Control + (int)Keys.Back, (int)Shortcut.CtrlDel, (int)Shortcut.ShiftDel, (int)Shortcut.ShiftIns, (int)Shortcut.CtrlJ}; } textBoxFlags[shortcutsEnabled] = value; } } ////// Gets or sets a value indicating whether the following shortcuts should be enabled or not: /// Ctrl-Z, Ctrl-C, Ctrl-X, Ctrl-V, Ctrl-A, Ctrl-L, Ctrl-R, Ctrl-E, Ctrl-I, Ctrl-Y, /// Ctrl-BackSpace, Ctrl-Del, Shift-Del, Shift-Ins. /// ////// /// Implements the [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (this.ShortcutsEnabled == false) { foreach (int shortcutValue in shortcutsToDisable) { if ((int)keyData == shortcutValue || (int)keyData == (shortcutValue | (int)Keys.Shift)) { return true; } } } // // There are a few keys that change the alignment of the text, but that // are not ignored by the native control when the ReadOnly property is set. // We need to workaround that. if (textBoxFlags[readOnly]) { int k = (int)keyData; if (k == (int)Shortcut.CtrlL // align left || k == (int)Shortcut.CtrlR // align right || k == (int)Shortcut.CtrlE // align centre || k == (int)Shortcut.CtrlJ) { // align justified return true; } } return base.ProcessCmdKey(ref msg, keyData); } ///property. /// /// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), Localizable(true), SRDescription(SR.TextBoxAutoSizeDescr), RefreshProperties(RefreshProperties.Repaint), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override bool AutoSize { get { return textBoxFlags[autoSize]; } set { // Note that we intentionally do not call base. TextBoxes size themselves by // overriding SetBoundsCore (old RTM code). We let CommonProperties.GetAutoSize // continue to return false to keep our LayoutEngines from messing with TextBoxes. // This is done for backwards compatibility since the new AutoSize behavior differs. if (textBoxFlags[autoSize] != value) { textBoxFlags[autoSize] = value; // AutoSize's effects are ignored for a multi-line textbox // if (!Multiline) { SetStyle(ControlStyles.FixedHeight, value); AdjustHeight(false); } OnAutoSizeChanged(EventArgs.Empty); } } } ////// Gets or sets a value indicating whether the size /// of the control automatically adjusts when the font assigned to the control /// is changed. /// /// Note: this works differently than other Controls' AutoSize, so we're hiding /// it to avoid confusion. /// ////// /// [ SRCategory(SR.CatAppearance), DispId(NativeMethods.ActiveX.DISPID_BACKCOLOR), SRDescription(SR.ControlBackColorDescr) ] public override Color BackColor { get { if (ShouldSerializeBackColor()) { return base.BackColor; } else if (this.ReadOnly) { return SystemColors.Control; } else { return SystemColors.Window; } } set { base.BackColor = value; } } ////// Gets or sets /// the background color of the control. /// ////// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler AutoSizeChanged { add { base.AutoSizeChanged += value; } remove { base.AutoSizeChanged -= value; } } /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// [ SRCategory(SR.CatAppearance), DefaultValue(BorderStyle.Fixed3D), DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), SRDescription(SR.TextBoxBorderDescr) ] public BorderStyle BorderStyle { get { return borderStyle; } set { if (borderStyle != value) { //verify that 'value' is a valid enum type... //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); } borderStyle = value; UpdateStyles(); RecreateHandle(); // PreferredSize depends on BorderStyle : thru CreateParams.ExStyle in User32!AdjustRectEx. // So when the BorderStyle changes let the parent of this control know about it. using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.BorderStyle)) { OnBorderStyleChanged(EventArgs.Empty); } } } } ////// Gets or sets the border type /// of the text box control. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnBorderStyleChangedDescr)] public event EventHandler BorderStyleChanged { add { Events.AddHandler(EVENT_BORDERSTYLECHANGED, value); } remove { Events.RemoveHandler(EVENT_BORDERSTYLECHANGED, value); } } internal virtual bool CanRaiseTextChangedEvent { get { return true; } } ///[To be supplied.] ////// Specifies whether the ImeMode can be enabled - See also ImeModeBase. /// protected override bool CanEnableIme { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CanEnableIme(), this = " + this ); Debug.Indent(); bool canEnable = !( this.ReadOnly || this.PasswordProtect ) && base.CanEnableIme; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + canEnable ); Debug.Unindent(); return canEnable; } } ////// /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxCanUndoDescr) ] public bool CanUndo { get { if (IsHandleCreated) { bool b; b = (int)SendMessage(NativeMethods.EM_CANUNDO, 0, 0) != 0; return b; } return false; } } ////// Gets a value /// indicating whether the user can undo the previous operation in a text box control. /// ////// /// /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ClassName = "EDIT"; cp.Style |= NativeMethods.ES_AUTOHSCROLL | NativeMethods.ES_AUTOVSCROLL; if (!textBoxFlags[hideSelection]) cp.Style |= NativeMethods.ES_NOHIDESEL; if (textBoxFlags[readOnly]) cp.Style |= NativeMethods.ES_READONLY; cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); cp.Style &= (~NativeMethods.WS_BORDER); switch (borderStyle) { case BorderStyle.Fixed3D: cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; break; case BorderStyle.FixedSingle: cp.Style |= NativeMethods.WS_BORDER; break; } if (textBoxFlags[multiline]) { cp.Style |= NativeMethods.ES_MULTILINE; if (textBoxFlags[wordWrap]) cp.Style &= ~NativeMethods.ES_AUTOHSCROLL; } return cp; } } ////// Returns the parameters needed to create the handle. Inheriting classes /// can override this to provide extra functionality. They should not, /// however, forget to call base.getCreateParams() first to get the struct /// filled up with the basic info. /// ////// /// This property is overridden and hidden from statement completion /// on controls that are based on Win32 Native Controls. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override bool DoubleBuffered { get { return base.DoubleBuffered; } set { base.DoubleBuffered = value; } } ////// /// [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event EventHandler Click { add { base.Click += value; } remove { base.Click -= value; } } ///[To be supplied.] ////// /// [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event MouseEventHandler MouseClick { add { base.MouseClick += value; } remove { base.MouseClick -= value; } } ///[To be supplied.] ///protected override Cursor DefaultCursor { get { return Cursors.IBeam; } } /// /// /// Deriving classes can override this to configure a default size for their control. /// This is more efficient than setting the size in the control's constructor. /// protected override Size DefaultSize { get { return new Size(100, PreferredHeight); } } ////// /// [ SRCategory(SR.CatAppearance), DispId(NativeMethods.ActiveX.DISPID_FORECOLOR), SRDescription(SR.ControlForeColorDescr) ] public override Color ForeColor { get { if (ShouldSerializeForeColor()) { return base.ForeColor; } else { return SystemColors.WindowText; } } set { base.ForeColor = value; } } ////// Gets or sets the foreground color of the control. /// ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TextBoxHideSelectionDescr) ] public bool HideSelection { get { return textBoxFlags[hideSelection]; } set { if (textBoxFlags[hideSelection] != value) { textBoxFlags[hideSelection] = value; RecreateHandle(); OnHideSelectionChanged(EventArgs.Empty); } } } ////// Gets or sets a value indicating whether the selected /// text in the text box control remains highlighted when the control loses focus. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnHideSelectionChangedDescr)] public event EventHandler HideSelectionChanged { add { Events.AddHandler(EVENT_HIDESELECTIONCHANGED, value); } remove { Events.RemoveHandler(EVENT_HIDESELECTIONCHANGED, value); } } ///[To be supplied.] ////// Internal version of ImeMode property. The ImeMode of TextBoxBase controls depend on its IME restricted /// mode which is determined by the CanEnableIme property which checks whether the control is in Password or /// ReadOnly mode. /// protected override ImeMode ImeModeBase { get { if( DesignMode ) { return base.ImeModeBase; } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_ImeModeInternal(), this = " + this ); Debug.Indent(); ImeMode imeMode = CanEnableIme ? base.ImeModeBase : ImeMode.Disable; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + imeMode ); Debug.Unindent(); return imeMode; } set { base.ImeModeBase = value; } } ////// /// [ SRCategory(SR.CatAppearance), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MergableProperty(false), Localizable(true), SRDescription(SR.TextBoxLinesDescr), Editor("System.Windows.Forms.Design.StringArrayEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) ] public string[] Lines { get { string text = Text; ArrayList list = new ArrayList(); int lineStart = 0; while (lineStart < text.Length) { int lineEnd = lineStart; for (; lineEnd < text.Length; lineEnd++) { char c = text[lineEnd]; if (c == '\r' || c == '\n') break; } string line = text.Substring(lineStart, lineEnd - lineStart); list.Add(line); // Treat "\r", "\r\n", and "\n" as new lines if (lineEnd < text.Length && text[lineEnd] == '\r') lineEnd++; if (lineEnd < text.Length && text[lineEnd] == '\n') lineEnd++; lineStart = lineEnd; } // Corner case -- last character in Text is a new line; need to add blank line to list if (text.Length > 0 && (text[text.Length - 1] == '\r' || text[text.Length - 1] == '\n')) list.Add(""); return(string[]) list.ToArray(typeof(string)); } [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We append a new line to the Text property. // Don't localize the new line character. ] set { //unparse this string list... if (value != null && value.Length > 0) { // Work Item #40689: // Using a StringBuilder instead of a String // speeds things up approx 150 times StringBuilder text = new StringBuilder(value[0]); for (int i=1; i < value.Length; ++i) { text.Append("\r\n"); text.Append(value[i]); } Text = text.ToString(); } else { Text = ""; } } } ////// Gets or /// sets the lines of text in an text box control. /// ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(32767), Localizable(true), SRDescription(SR.TextBoxMaxLengthDescr) ] public virtual int MaxLength { get { return maxLength; } set { if (value < 0) { throw new ArgumentOutOfRangeException("MaxLength", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxLength", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (maxLength != value) { maxLength = value; UpdateMaxLength(); } } } ////// Gets or sets the maximum number of /// characters the user can type into the text box control. /// ////// /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxModifiedDescr) ] public bool Modified { get { if (IsHandleCreated) { bool curState = (0 != (int) SendMessage(NativeMethods.EM_GETMODIFY, 0, 0)); if (textBoxFlags[modified] != curState) { // VSWhidbey 94719 - raise ModifiedChanged event. See WmReflectCommand for more info. textBoxFlags[modified] = curState; OnModifiedChanged(EventArgs.Empty); } return curState; } else{ return textBoxFlags[modified]; } } set { if (Modified != value) { if (IsHandleCreated) { SendMessage(NativeMethods.EM_SETMODIFY, value ? 1 : 0, 0); // VSWhidbey 94719 - must maintain this state always in order for the // test in the Get method to work properly. } textBoxFlags[modified] = value; OnModifiedChanged(EventArgs.Empty); } } } ////// Gets or sets a value that indicates that the text box control has been modified by the user since /// the control was created or its contents were last set. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnModifiedChangedDescr)] public event EventHandler ModifiedChanged { add { Events.AddHandler(EVENT_MODIFIEDCHANGED, value); } remove { Events.RemoveHandler(EVENT_MODIFIEDCHANGED, value); } } ///[To be supplied.] ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Localizable(true), SRDescription(SR.TextBoxMultilineDescr), RefreshProperties(RefreshProperties.All) ] public virtual bool Multiline { get { return textBoxFlags[multiline]; } set { if (textBoxFlags[multiline] != value) { using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Multiline)) { textBoxFlags[multiline] = value; if (value) { // Multi-line textboxes do not have fixed height // SetStyle(ControlStyles.FixedHeight, false); } else { // Single-line textboxes may have fixed height, depending on AutoSize SetStyle(ControlStyles.FixedHeight, AutoSize); } RecreateHandle(); AdjustHeight(false); OnMultilineChanged(EventArgs.Empty); } } } } ////// Gets or sets a value indicating whether this /// is a multiline text box control. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnMultilineChangedDescr)] public event EventHandler MultilineChanged { add { Events.AddHandler(EVENT_MULTILINECHANGED, value); } remove { Events.RemoveHandler(EVENT_MULTILINECHANGED, value); } } ///[To be supplied.] ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new Padding Padding { get { return base.Padding; } set { base.Padding = value;} } ////// ///[To be supplied.] ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRCategory(SR.CatLayout), SRDescription(SR.ControlOnPaddingChangedDescr) ] public new event EventHandler PaddingChanged { add { base.PaddingChanged += value; } remove { base.PaddingChanged -= value; } } ///[To be supplied.] ////// Determines if the control is in password protect mode. This is overridden in TextBox and /// MaskedTextBox and is false by default so RichTextBox that doesn't support Password doesn't /// have to care about this. /// virtual internal bool PasswordProtect { get { return false; } } ////// /// [ SRCategory(SR.CatLayout), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxPreferredHeightDescr) ] public int PreferredHeight { get { // VSWhidbey 523205: COMPAT we must return the same busted height we did in Everett, even // if it doesnt take multiline and word wrap into account. For better accuracy and/or wrapping use // GetPreferredSize instead. int height = FontHeight; if (borderStyle != BorderStyle.None) { height += SystemInformation.BorderSize.Height * 4 + 3; } return height; } } // GetPreferredSizeCore // VSWhidbey 523205: This method can return a different value than PreferredHeight! It properly handles // border style + multiline and wordwrap. internal override Size GetPreferredSizeCore(Size proposedConstraints) { // 3px vertical space is required between the text and the border to keep the last // line from being clipped. // This 3 pixel size was added in everett and we do this to maintain compat. // old everett behavior was FontHeight + [SystemInformation.BorderSize.Height * 4 + 3] // however the [ ] was only added if borderstyle was not none. Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size; if (BorderStyle != BorderStyle.None) { bordersAndPadding += new Size(0, 3); } if (BorderStyle == BorderStyle.FixedSingle) { // VSWhidbey 321520: bump these by 2px to match BorderStyle.Fixed3D - they'll be omitted from the SizeFromClientSize call. bordersAndPadding.Width +=2; bordersAndPadding.Height +=2; } // Reduce constraints by border/padding size proposedConstraints -= bordersAndPadding; // Fit the text to the remaining space TextFormatFlags format = TextFormatFlags.Default; if(!Multiline) { format = TextFormatFlags.SingleLine; } else if(WordWrap) { format = TextFormatFlags.WordBreak; } Size textSize = TextRenderer.MeasureText(this.Text, this.Font, proposedConstraints, format); // We use this old computation as a lower bound to ensure backwards compatibility. textSize.Height = Math.Max(textSize.Height, FontHeight); Size preferredSize = textSize + bordersAndPadding; return preferredSize; } ////// Returns the preferred /// height for a single-line text box. /// ////// Get the currently selected text start position and length. Use this method internally /// to avoid calling SelectionStart + SelectionLength each of which does essentially the /// same (save one message round trip). /// internal void GetSelectionStartAndLength( out int start, out int length ){ int end = 0; if( !IsHandleCreated ) { // It is possible that the cached values are no longer valid if the Text has been changed // while the control does not have a handle. We need to return valid values. We also need // to keep the old cached values in case the Text is changed again making the cached values // valid again. See VSW#495181. AdjustSelectionStartAndEnd( this.selectionStart, this.selectionLength, out start, out end, -1 ); length = end - start; } else { start = 0; UnsafeNativeMethods.SendMessage( new HandleRef( this, Handle ), NativeMethods.EM_GETSEL, ref start, ref end ); //Here, we return the max of either 0 or the # returned by //the windows call. This eliminates a problem on nt4 where // a huge negative # is being returned. // start = Math.Max( 0, start ); // ditto for end end = Math.Max( 0, end ); if( this.SelectionUsesDbcsOffsetsInWin9x && Marshal.SystemDefaultCharSize == 1 ) { // When processing EM_GETSEL, EDIT control returns byte offsets instead of character offsets, this // makes a difference in unicode code pages like Japanese. We need to adjust the offsets. ToUnicodeOffsets( WindowText, ref start, ref end ); } length = end - start; } #if DEBUG { string t = WindowText; int len; end = start + length - 1; if (t == null) { len = 0; } else { len = t.Length; } Debug.Assert(end <= len, "SelectionEnd is outside the set of valid caret positions for the current WindowText (end =" + end + ", WindowText.Length =" + len +")"); } #endif } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), RefreshProperties(RefreshProperties.Repaint), SRDescription(SR.TextBoxReadOnlyDescr) ] public bool ReadOnly { get { return textBoxFlags[readOnly]; } set { if (textBoxFlags[readOnly] != value) { textBoxFlags[readOnly] = value; if (IsHandleCreated) { SendMessage(NativeMethods.EM_SETREADONLY, value? -1: 0, 0); } OnReadOnlyChanged(EventArgs.Empty); VerifyImeRestrictedModeChanged(); } } } ////// Gets or sets a value indicating whether text in the text box is read-only. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnReadOnlyChangedDescr)] public event EventHandler ReadOnlyChanged { add { Events.AddHandler(EVENT_READONLYCHANGED, value); } remove { Events.RemoveHandler(EVENT_READONLYCHANGED, value); } } ///[To be supplied.] ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectedTextDescr) ] public virtual string SelectedText { get { int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); return Text.Substring(selStart, selLength); } set { SetSelectedTextInternal(value, true); } } ////// The currently selected text in the control. /// ////// Replaces the selected text with the one passed in. /// internal virtual void SetSelectedTextInternal(string text, bool clearUndo){ if (!IsHandleCreated) { CreateHandle(); } if( text == null ){ text = ""; } // The EM_LIMITTEXT message limits only the text the user can enter. It does not affect any text // already in the edit control when the message is sent, nor does it affect the length of the text // copied to the edit control by the WM_SETTEXT message. SendMessage(NativeMethods.EM_LIMITTEXT, 0, 0); if( clearUndo ){ SendMessage(NativeMethods.EM_REPLACESEL, 0, text); // For consistency with Text, we clear the modified flag SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); ClearUndo(); } else{ SendMessage(NativeMethods.EM_REPLACESEL, /*undoable*/ -1, text); } // Re-enable user input. SendMessage(NativeMethods.EM_LIMITTEXT, maxLength, 0); } ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectionLengthDescr) ] public virtual int SelectionLength { get { int start, length; GetSelectionStartAndLength( out start, out length ); return length; } set { if (value < 0){ throw new ArgumentOutOfRangeException("SelectionLength", SR.GetString(SR.InvalidArgument, "SelectionLength", value.ToString(CultureInfo.CurrentCulture))); } int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); if (value != selLength) { Select(selStart, value); } } } ////// Gets or sets the number of characters selected in the text /// box. /// ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectionStartDescr) ] public int SelectionStart { get { int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); return selStart; } set { if (value < 0){ throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidArgument, "SelectionStart", value.ToString(CultureInfo.CurrentCulture))); } Select(value, SelectionLength); } } // Call SetSelectionOnHandle inside CreateHandle() internal virtual bool SetSelectionInCreateHandle { get { return true; } } ////// Gets or sets the starting /// point of text selected in the text /// box. /// ////// /// [ Localizable(true), Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) ] public override string Text { get { return base.Text; } set { if (value != base.Text) { base.Text = value; if (IsHandleCreated) { // clear the modified flag SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); } } } } ////// Gets or sets /// the current text in the text box. /// ///[Browsable(false)] public virtual int TextLength { get { // Note: Currently Winforms does not fully support surrogates - VSW#327396. If // the text contains surrogate characters this property may return incorrect values. if (IsHandleCreated && Marshal.SystemDefaultCharSize == 2) { return SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); } else { return Text.Length; } } } /// /// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL) /// in Win9x. /// internal virtual bool SelectionUsesDbcsOffsetsInWin9x { get { return true; } } // Since setting the WindowText while the handle is created // generates a WM_COMMAND message, we must trap that case // and prevent the event from getting fired, or we get // double "TextChanged" events. // ////// /// /// internal override string WindowText { get { return base.WindowText; } set { if (value == null) value = ""; if (!WindowText.Equals(value)) { textBoxFlags[codeUpdateText] = true; try { base.WindowText = value; } finally { textBoxFlags[codeUpdateText] = false; } } } } ////// For VSWhidbey 325345. In certain circumstances we might have to force /// text into the window whether or not the text is the same. /// Make this a method on TextBoxBase rather than RichTextBox (which is the only /// control that needs this at this point), since we need to set codeUpdateText. /// internal void ForceWindowText(string value) { if (value == null) { value = ""; } textBoxFlags[codeUpdateText] = true; try { if (IsHandleCreated) { UnsafeNativeMethods.SetWindowText(new HandleRef(this, Handle), value); } else { if (value.Length == 0) { Text = null; } else { Text = value; } } } finally { textBoxFlags[codeUpdateText] = false; } } ////// /// [ SRCategory(SR.CatBehavior), Localizable(true), DefaultValue(true), SRDescription(SR.TextBoxWordWrapDescr) ] public bool WordWrap { get { return textBoxFlags[wordWrap]; } set { using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.WordWrap)) { if (textBoxFlags[wordWrap] != value) { textBoxFlags[wordWrap] = value; RecreateHandle(); } } } } ////// Gets or sets a value indicating whether a /// multiline text box control automatically wraps words to the beginning of the next /// line when necessary. /// ////// /// Adjusts the height of a single-line edit control to match the height of /// the control's font. /// ///private void AdjustHeight(bool returnIfAnchored) { // If we're anchored to two opposite sides of the form, don't adjust the size because // we'll lose our anchored size by resetting to the requested width. // if (returnIfAnchored && (this.Anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom)) { return; } int saveHeight = requestedHeight; try { if (textBoxFlags[autoSize] && !textBoxFlags[multiline]) { Height = PreferredHeight; } else { int curHeight = Height; // Changing the font of a multi-line textbox can sometimes cause a painting problem // The only workaround I can find is to size the textbox big enough for the font, and // then restore its correct size. // if (textBoxFlags[multiline]) { Height = Math.Max(saveHeight, PreferredHeight + 2); // 2 = fudge factor } integralHeightAdjust = true; try { Height = saveHeight; } finally { integralHeightAdjust = false; } } } finally { requestedHeight = saveHeight; } } /// /// /// public void AppendText( string text ) { if (text.Length > 0) { int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); try { // This enables you to use SelectionColor to AppendText in color. int endOfText = GetEndPosition(); SelectInternal(endOfText, endOfText, endOfText); SelectedText = text; } finally { // If AppendText is called when the control is docked and the form is minimized, // all the text will scroll to the top and the control will look empty when the // form is restored. We work around this by selecting back whatever was originally // selected when AppendText was called. if (this.Width == 0 || this.Height == 0) { this.Select(selStart, selLength); } } } } ////// Append text to the current text of text box. /// ////// /// public void Clear() { Text = null; } ////// Clears all text from the text box control. /// ////// /// public void ClearUndo() { if (IsHandleCreated) { SendMessage(NativeMethods.EM_EMPTYUNDOBUFFER, 0, 0); } } ////// Clears information about the most recent operation /// from the undo buffer of the text box. /// ////// /// [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] public void Copy() { SendMessage(NativeMethods.WM_COPY, 0, 0); } ////// Copies the current selection in the text box to the Clipboard. /// ////// /// ///protected override void CreateHandle() { // This "creatingHandle" stuff is to avoid property change events // when we set the Text property. textBoxFlags[creatingHandle] = true; try { base.CreateHandle(); if (SetSelectionInCreateHandle) { // send EM_SETSEL message SetSelectionOnHandle(); } } finally { textBoxFlags[creatingHandle] = false; } } /// /// /// public void Cut() { SendMessage(NativeMethods.WM_CUT, 0, 0); } ////// Moves the current selection in the text box to the Clipboard. /// ////// Returns the text end position (one past the last input character). This property is virtual to allow MaskedTextBox /// to set the last input char position as opposed to the last char position which may be a mask character. /// internal virtual int GetEndPosition(){ // +1 because RichTextBox has this funny EOF pseudo-character after all the text. return IsHandleCreated ? TextLength + 1 : TextLength; } ////// /// /// Overridden to handle TAB key. /// protected override bool IsInputKey(Keys keyData) { if ((keyData & Keys.Alt) != Keys.Alt) { switch (keyData & Keys.KeyCode) { case Keys.Tab: // Single-line RichEd's want tab characters (see WM_GETDLGCODE), // so we don't ask it return Multiline && textBoxFlags[acceptsTab] && ((keyData & Keys.Control) == 0); case Keys.Escape: if (Multiline) return false; break; case Keys.PageUp: case Keys.PageDown: case Keys.Home: case Keys.End: return true; // else fall through to base } } return base.IsInputKey(keyData); } ////// /// /// Overridden to update the newly created handle with the settings of the /// MaxLength and PasswordChar properties. /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); // it's likely here that the create params could have changed // the border size/etc. CommonProperties.xClearPreferredSizeCache(this); AdjustHeight(true); UpdateMaxLength(); if (textBoxFlags[modified]){ SendMessage(NativeMethods.EM_SETMODIFY, 1, 0); } if (textBoxFlags[scrollToCaretOnHandleCreated]) { ScrollToCaret(); textBoxFlags[scrollToCaretOnHandleCreated] = false; } } ////// /// ///protected override void OnHandleDestroyed(EventArgs e) { textBoxFlags[modified] = Modified; textBoxFlags[setSelectionOnHandleCreated] = true; // Update text selection cached values to be restored when recreating the handle. GetSelectionStartAndLength( out this.selectionStart, out this.selectionLength ); base.OnHandleDestroyed(e); } /// /// /// [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] public void Paste() { Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); IntSecurity.ClipboardRead.Demand(); SendMessage(NativeMethods.WM_PASTE, 0, 0); } ////// Replaces the current selection in the text box with the contents of the Clipboard. /// ////// /// [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] protected override bool ProcessDialogKey(Keys keyData) { Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "TextBoxBase.ProcessDialogKey [" + keyData.ToString() + "]"); Keys keyCode = (Keys)keyData & Keys.KeyCode; if (keyCode == Keys.Tab && this.AcceptsTab && (keyData & Keys.Control) != 0) { // When this control accepts Tabs, Ctrl-Tab is treated exactly like Tab. keyData &= ~Keys.Control; } return base.ProcessDialogKey(keyData); } ///[To be supplied.] ////// /// TextBox / RichTextBox Onpaint. /// ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event PaintEventHandler Paint { add { base.Paint += value; } remove { base.Paint -= value; } } /// /// /// protected virtual void OnAcceptsTabChanged(EventArgs e) { EventHandler eh = Events[EVENT_ACCEPTSTABCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// /// protected virtual void OnBorderStyleChanged(EventArgs e) { EventHandler eh = Events[EVENT_BORDERSTYLECHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// /// protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); AdjustHeight(false); } ///[To be supplied.] ////// /// protected virtual void OnHideSelectionChanged(EventArgs e) { EventHandler eh = Events[EVENT_HIDESELECTIONCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// /// protected virtual void OnModifiedChanged(EventArgs e) { EventHandler eh = Events[EVENT_MODIFIEDCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// Raises the MouseUp event. /// protected override void OnMouseUp(MouseEventArgs mevent) { Point pt = PointToScreen(mevent.Location); if (mevent.Button == MouseButtons.Left) { if (!ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { if (!this.doubleClickFired) { OnClick(mevent); OnMouseClick(mevent); } else { this.doubleClickFired = false; OnDoubleClick(mevent); OnMouseDoubleClick(mevent); } } this.doubleClickFired = false; } base.OnMouseUp(mevent); } ////// /// protected virtual void OnMultilineChanged(EventArgs e) { EventHandler eh = Events[EVENT_MULTILINECHANGED] as EventHandler; if (eh != null) { eh(this, e); } } protected override void OnPaddingChanged(EventArgs e) { base.OnPaddingChanged(e); AdjustHeight(false); } ///[To be supplied.] ////// /// protected virtual void OnReadOnlyChanged(EventArgs e) { EventHandler eh = Events[EVENT_READONLYCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } protected override void OnTextChanged(EventArgs e) { // since AutoSize existed in Everett, (and is the default) we can't // relayout the parent when the "preferredsize" of the control changes. // this means a multiline = true textbox wont natrually grow in height when // the text changes. CommonProperties.xClearPreferredSizeCache(this); base.OnTextChanged(e); } ///[To be supplied.] ////// /// Returns the character nearest to the given point. /// public virtual char GetCharFromPosition(Point pt) { string t = this.Text; int index = GetCharIndexFromPosition(pt); return (index < 0 || index >= t.Length) ? (char)0 : t[index]; } ////// /// Returns the index of the character nearest to the given point. /// public virtual int GetCharIndexFromPosition(Point pt) { int longPoint = NativeMethods.Util.MAKELONG(pt.X, pt.Y); int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, longPoint); index = NativeMethods.Util.LOWORD(index); if (index < 0) { index = 0; } else { string t = this.Text; // EM_CHARFROMPOS will return an invalid number if the last character in the RichEdit // is a newline. // if (index >= t.Length) { index = Math.Max(t.Length - 1, 0); } } return index; } ////// /// Returns the number of the line containing a specified character position /// in a textbox. Note that this returns the physical line number /// and not the conceptual line number. For example, if the first conceptual /// line (line number 0) word-wraps and extends to the second line, and if /// you pass the index of a overflowed character, GetLineFromCharIndex would /// return 1 and not 0. /// public virtual int GetLineFromCharIndex(int index) { return (int)SendMessage(NativeMethods.EM_LINEFROMCHAR, index, 0); } ////// /// Returns the location of the character at the given index. /// public virtual Point GetPositionFromCharIndex(int index) { if (index < 0 || index >= Text.Length) return Point.Empty; int i = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_POSFROMCHAR, index, 0); return new Point(NativeMethods.Util.LOWORD(i), NativeMethods.Util.HIWORD(i)); } ////// /// Returns the index of the first character of a given line. Returns -1 of lineNumber is invalid. /// public int GetFirstCharIndexFromLine(int lineNumber) { if (lineNumber < 0) { throw new ArgumentOutOfRangeException("lineNumber", SR.GetString(SR.InvalidArgument, "lineNumber", lineNumber.ToString(CultureInfo.CurrentCulture))); } return (int)SendMessage(NativeMethods.EM_LINEINDEX, lineNumber, 0); } ////// /// Returns the index of the first character of the line where the caret is. /// public int GetFirstCharIndexOfCurrentLine() { return (int)SendMessage(NativeMethods.EM_LINEINDEX, -1, 0); } ////// /// Ensures that the caret is visible in the TextBox window, by scrolling the /// TextBox control surface if necessary. /// public void ScrollToCaret() { if (IsHandleCreated) { if (String.IsNullOrEmpty(this.WindowText)) { // If there is no text, then there is no place to go. return; } bool scrolled = false; object editOle = null; IntPtr editOlePtr = IntPtr.Zero; try { if (UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETOLEINTERFACE, 0, out editOle) != 0) { editOlePtr = Marshal.GetIUnknownForObject(editOle); if (editOlePtr != IntPtr.Zero) { IntPtr iTextDocument = IntPtr.Zero; Guid iiTextDocumentGuid = typeof(UnsafeNativeMethods.ITextDocument).GUID; try { Marshal.QueryInterface(editOlePtr, ref iiTextDocumentGuid, out iTextDocument); UnsafeNativeMethods.ITextDocument textDocument = Marshal.GetObjectForIUnknown(iTextDocument) as UnsafeNativeMethods.ITextDocument; if (textDocument != null) { int selStart, selLength; // vsWhidbey 372764: when the user calls RichTextBox::ScrollToCaret we want the RichTextBox to show as // much text as possible. // Here is how we do that: // 1. We scroll the RichTextBox all the way to the bottom so the last line of text is the last visible line. // 2. We get the first visible line. // 3. If the first visible line is smaller than the start of the selection, then we are done: // The selection fits inside the RichTextBox display rectangle. // 4. Otherwise, scroll the selection to the top of the RichTextBox. GetSelectionStartAndLength( out selStart, out selLength ); int selStartLine = GetLineFromCharIndex(selStart); // 1. Scroll the RichTextBox all the way to the bottom UnsafeNativeMethods.ITextRange textRange = textDocument.Range(this.WindowText.Length - 1, this.WindowText.Length - 1); textRange.ScrollIntoView(0); // 0 ==> tomEnd // 2. Get the first visible line. int firstVisibleLine = (int) SendMessage(NativeMethods.EM_GETFIRSTVISIBLELINE, 0, 0); // 3. If the first visible line is smaller than the start of the selection, we are done; if (firstVisibleLine <= selStartLine) { // we are done } else { // 4. Scroll the selection to the top of the RichTextBox textRange = textDocument.Range(selStart, selStart + selLength); textRange.ScrollIntoView(32); // 32 ==> tomStart } scrolled = true; } } finally { if (iTextDocument != IntPtr.Zero) { Marshal.Release(iTextDocument); } } } } } finally { if (editOlePtr != IntPtr.Zero) { Marshal.Release(editOlePtr); } } if (!scrolled) { SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0); } } else { textBoxFlags[scrollToCaretOnHandleCreated] = true; } } ////// /// public void DeselectAll() { this.SelectionLength = 0; } ////// Sets the SelectionLength to 0. /// ////// /// public void Select(int start, int length) { if (start < 0){ throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidArgument, "start", start.ToString(CultureInfo.CurrentCulture))); } int textLen = TextLength; if (start > textLen) { //We shouldn't allow positive length if you're starting at the end, but //should allow negative length. long longLength = Math.Min(0, (long)length + start - textLen); if (longLength < int.MinValue) { length = int.MinValue; } else { length = (int)longLength; } start = textLen; } SelectInternal(start, length, textLen); } ////// Selects a range of text in the text box. /// ////// Performs the actual select without doing arg checking. /// /// Send in -1 for the textLen parameter if you don't have the text /// length cached when calling this method. It will be computed. /// But if you do have it cached, please pass it in. This will avoid /// the expensive call to the TextLength property. /// internal virtual void SelectInternal(int start, int length, int textLen) { //if our handle is created - send message... if (IsHandleCreated) { int s, e; AdjustSelectionStartAndEnd(start, length, out s, out e, textLen); SendMessage(NativeMethods.EM_SETSEL, s, e); // } else { //otherwise, wait until handle is created to send this message. //Store the indices until then... this.selectionStart = start; this.selectionLength = length; textBoxFlags[setSelectionOnHandleCreated] = true; } } ////// /// public void SelectAll() { int textLen = TextLength; SelectInternal(0, textLen, textLen); } ////// Selects all text in the text box. /// ////// /// Overrides Control.setBoundsCore to enforce autoSize. /// ///protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { if (!integralHeightAdjust && height != Height) requestedHeight = height; if (textBoxFlags[autoSize] && !textBoxFlags[multiline]) height = PreferredHeight; base.SetBoundsCore(x, y, width, height, specified); } private static void Swap(ref int n1, ref int n2) { int temp = n2; n2 = n1; n1 = temp; } // // Send in -1 if you don't have the text length cached // when calling this method. It will be computed. If not, // please pass in the text length as the last parameter. // This will avoid the expensive call to the TextLength // property. internal void AdjustSelectionStartAndEnd(int selStart, int selLength, out int start, out int end, int textLen) { start = selStart; end = 0; if (start <= -1) { start = -1; } else { int textLength; if (textLen >= 0) { textLength = textLen; } else { textLength = this.TextLength; } if (start > textLength) { start = textLength; } checked { try { end = start + selLength; } catch (OverflowException) { //Since we overflowed, cap at the max/min value: we'll correct the value below end = start > 0 ? int.MaxValue : int.MinValue; } } // Make sure end is in range if (end < 0) { end = 0; } else if (end > textLength) { end = textLength; } if (this.SelectionUsesDbcsOffsetsInWin9x && Marshal.SystemDefaultCharSize == 1) { // EDIT control expects selection values to be byte offsets instead of character offsets, // this makes a difference in unicode code pages like Japanese. // We need to adjust the offsets. ToDbcsOffsets(WindowText, ref start, ref end); } } } // Called by CreateHandle or OnHandleCreated internal void SetSelectionOnHandle() { Debug.Assert(IsHandleCreated, "Don't call this method until the handle is created."); if (textBoxFlags[setSelectionOnHandleCreated]) { textBoxFlags[setSelectionOnHandleCreated] = false; int start, end; AdjustSelectionStartAndEnd(this.selectionStart, this.selectionLength, out start, out end, -1); SendMessage(NativeMethods.EM_SETSEL, start, end); } } /// /// Converts byte offsset to unicode offsets. /// When procssing WM_GETSEL/WM_SETSEL, EDIT control works with byte offsets instead of character positions /// as opposed to RICHEDIT which does it always as character positions. /// This method is used when handling the WM_GETSEL message. /// static void ToUnicodeOffsets(string str, ref int start, ref int end) { Encoding e = Encoding.Default; // Acutally, we may get away with this call if we can get the bytes from Win9x. Dont know if it is possible. // This can be expensive since start/end could be small, but str.Length can be quite big. byte[] bytes = e.GetBytes(str); bool swap = start > end; if (swap) { Swap(ref start, ref end); } // Make sure start and end are within the string // if (start < 0){ start = 0; } if (start > bytes.Length){ start = bytes.Length; } if (end > bytes.Length){ end = bytes.Length; } // IMPORTANT: Avoid off-by-1 errors! // The end value passed in is the character immediately after the last character selected. int newStart = start == 0 ? 0 : e.GetCharCount(bytes, 0, start); end = newStart + e.GetCharCount(bytes, start, end - start); start = newStart; if (swap){ Swap(ref start, ref end); } } ////// Converts unicode offsset to byte offsets. /// When procssing WM_GETSEL/WM_SETSEL, EDIT control works with byte offsets instead of character positions /// as opposed to RICHEDIT which does it always as character positions. /// This method is used when handling the WM_SETSEL message. /// static internal void ToDbcsOffsets(string str, ref int start, ref int end) { Encoding e = Encoding.Default; bool swap = start > end; if (swap) { Swap(ref start, ref end); } // Make sure start and end are within the string // if (start < 0) { start = 0; } if (start > str.Length) { start = str.Length; } if (end < start) { end = start; } if (end > str.Length) { end = str.Length; } // IMPORTANT: Avoid off-by-1 errors! // The end value passed in is the character immediately after the last character selected. int newStart = start == 0 ? 0 : e.GetByteCount(str.Substring(0, start)); end = newStart + e.GetByteCount(str.Substring(start, end - start)); start = newStart; if (swap) { Swap(ref start, ref end); } } ////// /// Provides some interesting information for the TextBox control in /// String form. /// ///public override string ToString() { string s = base.ToString(); string txt = Text; if (txt.Length > 40) txt = txt.Substring(0, 40) + "..."; return s + ", Text: " + txt.ToString(); } /// /// /// public void Undo() { SendMessage(NativeMethods.EM_UNDO, 0, 0); } internal virtual void UpdateMaxLength() { if (IsHandleCreated) { SendMessage(NativeMethods.EM_LIMITTEXT, maxLength, 0); } } internal override IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) { if ((msg == NativeMethods.WM_CTLCOLORSTATIC) && !ShouldSerializeBackColor()) { // Let the Win32 Edit control handle background colors itself. // This is necessary because a disabled edit control will display a different // BackColor than when enabled. return IntPtr.Zero; } else { return base.InitializeDCForWmCtlColor(dc, msg); } } ////// Undoes the last edit operation in the text box. /// ////// /// ///private void WmReflectCommand(ref Message m) { if (!textBoxFlags[codeUpdateText] && !textBoxFlags[creatingHandle]) { if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.EN_CHANGE && CanRaiseTextChangedEvent) { OnTextChanged(EventArgs.Empty); } else if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.EN_UPDATE) { // VSWhidbey 94719: force update to the Modified property, which will trigger // ModifiedChanged event handlers bool force = this.Modified; } } } /// /// /// ///void WmSetFont(ref Message m) { base.WndProc(ref m); if (!textBoxFlags[multiline]) { SendMessage(NativeMethods.EM_SETMARGINS, NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); } } void WmGetDlgCode(ref Message m) { base.WndProc(ref m); if (AcceptsTab) { Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "TextBox wants tabs"); m.Result = (IntPtr)((int)m.Result | NativeMethods.DLGC_WANTTAB); } else { Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "TextBox doesn't want tabs"); m.Result = (IntPtr)((int)m.Result & ~(NativeMethods.DLGC_WANTTAB | NativeMethods.DLGC_WANTALLKEYS)); } } /// /// Handles the WM_CONTEXTMENU message based on this /// table: /// ShortcutsEnabled #1 #2 #3 /// Yes strip context system /// No strip context N/A /// ///private void WmTextBoxContextMenu(ref Message m) { if (ContextMenu != null || ContextMenuStrip != null) { int x = NativeMethods.Util.SignedLOWORD(m.LParam); int y = NativeMethods.Util.SignedHIWORD(m.LParam); Point client; bool keyboardActivated = false; // lparam will be exactly -1 when the user invokes the context menu // with the keyboard. // if (unchecked((int)(long)m.LParam) == -1) { keyboardActivated = true; client = new Point(Width/2, Height/2); } else { client = PointToClientInternal(new Point(x, y)); } // // VisualStudio7 # 156, only show the context menu when clicked in the client area if (ClientRectangle.Contains( client )) { if (ContextMenu != null) { ContextMenu.Show(this, client); } else if (ContextMenuStrip != null) { ContextMenuStrip.ShowInternal(this, client, keyboardActivated); } else { Debug.Fail("contextmenu and contextmenustrip are both null... hmm how did we get here?"); DefWndProc( ref m ); } } } } /// /// /// /// The control's window procedure. Inheriting classes can override this /// to add extra functionality, but should not forget to call /// base.wndProc(m); to ensure the control continues to function properly. /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_LBUTTONDBLCLK: this.doubleClickFired = true; base.WndProc(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: WmReflectCommand(ref m); break; case NativeMethods.WM_GETDLGCODE: WmGetDlgCode(ref m); break; case NativeMethods.WM_SETFONT: WmSetFont(ref m); break; case NativeMethods.WM_CONTEXTMENU: if (ShortcutsEnabled) { //calling base will find ContextMenus in this order: // 1) ContextMenu 2) ContextMenuStrip 3) SystemMenu base.WndProc(ref m); } else { // we'll handle this message so we can hide the // SystemMenu if Context and Strip menus are null WmTextBoxContextMenu(ref m); } break; default: base.WndProc(ref m); break; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Text; using System.Runtime.Serialization.Formatters; using System.Runtime.InteropServices; using System.Runtime.Remoting; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Collections; using System.Collections.Specialized; using System.Security.Permissions; using System.Windows.Forms.Design; using System.Windows.Forms.Layout; using System.ComponentModel.Design; using System.ComponentModel; using System.Drawing; using System.Windows.Forms.Internal; using System.Drawing.Design; using Microsoft.Win32; using System.Reflection; using System.Globalization; ////// /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), DefaultEvent("TextChanged"), DefaultBindingProperty("Text"), Designer("System.Windows.Forms.Design.TextBoxBaseDesigner, " + AssemblyRef.SystemDesign) ] public abstract class TextBoxBase : Control { // The boolean properties for this control are contained in the textBoxFlags bit // vector. We can store up to 32 boolean values in this one vector. Here we // create the bitmasks for each bit in the vector. // private static readonly int autoSize = BitVector32.CreateMask(); private static readonly int hideSelection = BitVector32.CreateMask(autoSize); private static readonly int multiline = BitVector32.CreateMask(hideSelection); private static readonly int modified = BitVector32.CreateMask(multiline); private static readonly int readOnly = BitVector32.CreateMask(modified); private static readonly int acceptsTab = BitVector32.CreateMask(readOnly); private static readonly int wordWrap = BitVector32.CreateMask(acceptsTab); private static readonly int creatingHandle = BitVector32.CreateMask(wordWrap); private static readonly int codeUpdateText = BitVector32.CreateMask(creatingHandle); private static readonly int shortcutsEnabled = BitVector32.CreateMask(codeUpdateText); private static readonly int scrollToCaretOnHandleCreated = BitVector32.CreateMask(shortcutsEnabled); private static readonly int setSelectionOnHandleCreated = BitVector32.CreateMask(scrollToCaretOnHandleCreated); private static readonly object EVENT_ACCEPTSTABCHANGED = new object(); private static readonly object EVENT_BORDERSTYLECHANGED = new object(); private static readonly object EVENT_HIDESELECTIONCHANGED = new object(); private static readonly object EVENT_MODIFIEDCHANGED = new object(); private static readonly object EVENT_MULTILINECHANGED = new object(); private static readonly object EVENT_READONLYCHANGED = new object(); ////// Implements the basic functionality required by text /// controls. /// ////// /// The current border for this edit control. /// private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; ////// /// Controls the maximum length of text in the edit control. /// private int maxLength = 32767; // Win9X default, used for consistency ////// /// Used by the autoSizing code to help figure out the desired height of /// the edit box. /// private int requestedHeight; bool integralHeightAdjust = false; //these indices are used to cache the values of the selection, by doing this //if the handle isn't created yet, we don't force a creation. private int selectionStart = 0; private int selectionLength = 0; ////// Controls firing of click event (Left click). /// This is used by TextBox, RichTextBox and MaskedTextBox, code was moved down from TextBox/RichTextBox /// but cannot make it as default behavior to avoid introducing breaking changes. /// private bool doubleClickFired = false; private static int[] shortcutsToDisable; // We store all boolean properties in here. // private BitVector32 textBoxFlags = new BitVector32(); ////// /// Creates a new TextBox control. Uses the parent's current font and color /// set. /// internal TextBoxBase() : base() { // this class overrides GetPreferredSizeCore, let Control automatically cache the result SetState2(STATE2_USEPREFERREDSIZECACHE, true); textBoxFlags[autoSize | hideSelection | wordWrap | shortcutsEnabled] = true; SetStyle(ControlStyles.FixedHeight, textBoxFlags[autoSize]); SetStyle(ControlStyles.StandardClick | ControlStyles.StandardDoubleClick | ControlStyles.UseTextForAccessibility | ControlStyles.UserPaint, false); // cache requestedHeight. Note: Control calls DefaultSize (overridable) in the constructor // to set the control's cached height that is returned when calling Height, so we just // need to get the cached height here. requestedHeight = Height; } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.TextBoxAcceptsTabDescr) ] public bool AcceptsTab { get { return textBoxFlags[acceptsTab]; } set { if (textBoxFlags[acceptsTab] != value) { textBoxFlags[acceptsTab] = value; OnAcceptsTabChanged(EventArgs.Empty); } } } ////// Gets or sets /// a value indicating whether pressing the TAB key /// in a multiline text box control types /// a TAB character in the control instead of moving the focus to the next control /// in the tab order. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnAcceptsTabChangedDescr)] public event EventHandler AcceptsTabChanged { add { Events.AddHandler(EVENT_ACCEPTSTABCHANGED, value); } remove { Events.RemoveHandler(EVENT_ACCEPTSTABCHANGED, value); } } ///[To be supplied.] ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TextBoxShortcutsEnabledDescr) ] public virtual bool ShortcutsEnabled { get { return textBoxFlags[shortcutsEnabled]; } set { if (shortcutsToDisable == null) { shortcutsToDisable = new int[] {(int)Shortcut.CtrlZ, (int)Shortcut.CtrlC, (int)Shortcut.CtrlX, (int)Shortcut.CtrlV, (int)Shortcut.CtrlA, (int)Shortcut.CtrlL, (int)Shortcut.CtrlR, (int)Shortcut.CtrlE, (int)Shortcut.CtrlY, (int)Keys.Control + (int)Keys.Back, (int)Shortcut.CtrlDel, (int)Shortcut.ShiftDel, (int)Shortcut.ShiftIns, (int)Shortcut.CtrlJ}; } textBoxFlags[shortcutsEnabled] = value; } } ////// Gets or sets a value indicating whether the following shortcuts should be enabled or not: /// Ctrl-Z, Ctrl-C, Ctrl-X, Ctrl-V, Ctrl-A, Ctrl-L, Ctrl-R, Ctrl-E, Ctrl-I, Ctrl-Y, /// Ctrl-BackSpace, Ctrl-Del, Shift-Del, Shift-Ins. /// ////// /// Implements the [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (this.ShortcutsEnabled == false) { foreach (int shortcutValue in shortcutsToDisable) { if ((int)keyData == shortcutValue || (int)keyData == (shortcutValue | (int)Keys.Shift)) { return true; } } } // // There are a few keys that change the alignment of the text, but that // are not ignored by the native control when the ReadOnly property is set. // We need to workaround that. if (textBoxFlags[readOnly]) { int k = (int)keyData; if (k == (int)Shortcut.CtrlL // align left || k == (int)Shortcut.CtrlR // align right || k == (int)Shortcut.CtrlE // align centre || k == (int)Shortcut.CtrlJ) { // align justified return true; } } return base.ProcessCmdKey(ref msg, keyData); } ///property. /// /// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), Localizable(true), SRDescription(SR.TextBoxAutoSizeDescr), RefreshProperties(RefreshProperties.Repaint), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override bool AutoSize { get { return textBoxFlags[autoSize]; } set { // Note that we intentionally do not call base. TextBoxes size themselves by // overriding SetBoundsCore (old RTM code). We let CommonProperties.GetAutoSize // continue to return false to keep our LayoutEngines from messing with TextBoxes. // This is done for backwards compatibility since the new AutoSize behavior differs. if (textBoxFlags[autoSize] != value) { textBoxFlags[autoSize] = value; // AutoSize's effects are ignored for a multi-line textbox // if (!Multiline) { SetStyle(ControlStyles.FixedHeight, value); AdjustHeight(false); } OnAutoSizeChanged(EventArgs.Empty); } } } ////// Gets or sets a value indicating whether the size /// of the control automatically adjusts when the font assigned to the control /// is changed. /// /// Note: this works differently than other Controls' AutoSize, so we're hiding /// it to avoid confusion. /// ////// /// [ SRCategory(SR.CatAppearance), DispId(NativeMethods.ActiveX.DISPID_BACKCOLOR), SRDescription(SR.ControlBackColorDescr) ] public override Color BackColor { get { if (ShouldSerializeBackColor()) { return base.BackColor; } else if (this.ReadOnly) { return SystemColors.Control; } else { return SystemColors.Window; } } set { base.BackColor = value; } } ////// Gets or sets /// the background color of the control. /// ////// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler AutoSizeChanged { add { base.AutoSizeChanged += value; } remove { base.AutoSizeChanged -= value; } } /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// [ SRCategory(SR.CatAppearance), DefaultValue(BorderStyle.Fixed3D), DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), SRDescription(SR.TextBoxBorderDescr) ] public BorderStyle BorderStyle { get { return borderStyle; } set { if (borderStyle != value) { //verify that 'value' is a valid enum type... //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); } borderStyle = value; UpdateStyles(); RecreateHandle(); // PreferredSize depends on BorderStyle : thru CreateParams.ExStyle in User32!AdjustRectEx. // So when the BorderStyle changes let the parent of this control know about it. using(LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.BorderStyle)) { OnBorderStyleChanged(EventArgs.Empty); } } } } ////// Gets or sets the border type /// of the text box control. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnBorderStyleChangedDescr)] public event EventHandler BorderStyleChanged { add { Events.AddHandler(EVENT_BORDERSTYLECHANGED, value); } remove { Events.RemoveHandler(EVENT_BORDERSTYLECHANGED, value); } } internal virtual bool CanRaiseTextChangedEvent { get { return true; } } ///[To be supplied.] ////// Specifies whether the ImeMode can be enabled - See also ImeModeBase. /// protected override bool CanEnableIme { get { Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_CanEnableIme(), this = " + this ); Debug.Indent(); bool canEnable = !( this.ReadOnly || this.PasswordProtect ) && base.CanEnableIme; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + canEnable ); Debug.Unindent(); return canEnable; } } ////// /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxCanUndoDescr) ] public bool CanUndo { get { if (IsHandleCreated) { bool b; b = (int)SendMessage(NativeMethods.EM_CANUNDO, 0, 0) != 0; return b; } return false; } } ////// Gets a value /// indicating whether the user can undo the previous operation in a text box control. /// ////// /// /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ClassName = "EDIT"; cp.Style |= NativeMethods.ES_AUTOHSCROLL | NativeMethods.ES_AUTOVSCROLL; if (!textBoxFlags[hideSelection]) cp.Style |= NativeMethods.ES_NOHIDESEL; if (textBoxFlags[readOnly]) cp.Style |= NativeMethods.ES_READONLY; cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE); cp.Style &= (~NativeMethods.WS_BORDER); switch (borderStyle) { case BorderStyle.Fixed3D: cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; break; case BorderStyle.FixedSingle: cp.Style |= NativeMethods.WS_BORDER; break; } if (textBoxFlags[multiline]) { cp.Style |= NativeMethods.ES_MULTILINE; if (textBoxFlags[wordWrap]) cp.Style &= ~NativeMethods.ES_AUTOHSCROLL; } return cp; } } ////// Returns the parameters needed to create the handle. Inheriting classes /// can override this to provide extra functionality. They should not, /// however, forget to call base.getCreateParams() first to get the struct /// filled up with the basic info. /// ////// /// This property is overridden and hidden from statement completion /// on controls that are based on Win32 Native Controls. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override bool DoubleBuffered { get { return base.DoubleBuffered; } set { base.DoubleBuffered = value; } } ////// /// [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event EventHandler Click { add { base.Click += value; } remove { base.Click -= value; } } ///[To be supplied.] ////// /// [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event MouseEventHandler MouseClick { add { base.MouseClick += value; } remove { base.MouseClick -= value; } } ///[To be supplied.] ///protected override Cursor DefaultCursor { get { return Cursors.IBeam; } } /// /// /// Deriving classes can override this to configure a default size for their control. /// This is more efficient than setting the size in the control's constructor. /// protected override Size DefaultSize { get { return new Size(100, PreferredHeight); } } ////// /// [ SRCategory(SR.CatAppearance), DispId(NativeMethods.ActiveX.DISPID_FORECOLOR), SRDescription(SR.ControlForeColorDescr) ] public override Color ForeColor { get { if (ShouldSerializeForeColor()) { return base.ForeColor; } else { return SystemColors.WindowText; } } set { base.ForeColor = value; } } ////// Gets or sets the foreground color of the control. /// ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.TextBoxHideSelectionDescr) ] public bool HideSelection { get { return textBoxFlags[hideSelection]; } set { if (textBoxFlags[hideSelection] != value) { textBoxFlags[hideSelection] = value; RecreateHandle(); OnHideSelectionChanged(EventArgs.Empty); } } } ////// Gets or sets a value indicating whether the selected /// text in the text box control remains highlighted when the control loses focus. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnHideSelectionChangedDescr)] public event EventHandler HideSelectionChanged { add { Events.AddHandler(EVENT_HIDESELECTIONCHANGED, value); } remove { Events.RemoveHandler(EVENT_HIDESELECTIONCHANGED, value); } } ///[To be supplied.] ////// Internal version of ImeMode property. The ImeMode of TextBoxBase controls depend on its IME restricted /// mode which is determined by the CanEnableIme property which checks whether the control is in Password or /// ReadOnly mode. /// protected override ImeMode ImeModeBase { get { if( DesignMode ) { return base.ImeModeBase; } Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Inside get_ImeModeInternal(), this = " + this ); Debug.Indent(); ImeMode imeMode = CanEnableIme ? base.ImeModeBase : ImeMode.Disable; Debug.WriteLineIf( CompModSwitches.ImeMode.Level >= TraceLevel.Info, "Value = " + imeMode ); Debug.Unindent(); return imeMode; } set { base.ImeModeBase = value; } } ////// /// [ SRCategory(SR.CatAppearance), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MergableProperty(false), Localizable(true), SRDescription(SR.TextBoxLinesDescr), Editor("System.Windows.Forms.Design.StringArrayEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) ] public string[] Lines { get { string text = Text; ArrayList list = new ArrayList(); int lineStart = 0; while (lineStart < text.Length) { int lineEnd = lineStart; for (; lineEnd < text.Length; lineEnd++) { char c = text[lineEnd]; if (c == '\r' || c == '\n') break; } string line = text.Substring(lineStart, lineEnd - lineStart); list.Add(line); // Treat "\r", "\r\n", and "\n" as new lines if (lineEnd < text.Length && text[lineEnd] == '\r') lineEnd++; if (lineEnd < text.Length && text[lineEnd] == '\n') lineEnd++; lineStart = lineEnd; } // Corner case -- last character in Text is a new line; need to add blank line to list if (text.Length > 0 && (text[text.Length - 1] == '\r' || text[text.Length - 1] == '\n')) list.Add(""); return(string[]) list.ToArray(typeof(string)); } [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // We append a new line to the Text property. // Don't localize the new line character. ] set { //unparse this string list... if (value != null && value.Length > 0) { // Work Item #40689: // Using a StringBuilder instead of a String // speeds things up approx 150 times StringBuilder text = new StringBuilder(value[0]); for (int i=1; i < value.Length; ++i) { text.Append("\r\n"); text.Append(value[i]); } Text = text.ToString(); } else { Text = ""; } } } ////// Gets or /// sets the lines of text in an text box control. /// ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(32767), Localizable(true), SRDescription(SR.TextBoxMaxLengthDescr) ] public virtual int MaxLength { get { return maxLength; } set { if (value < 0) { throw new ArgumentOutOfRangeException("MaxLength", SR.GetString(SR.InvalidLowBoundArgumentEx, "MaxLength", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (maxLength != value) { maxLength = value; UpdateMaxLength(); } } } ////// Gets or sets the maximum number of /// characters the user can type into the text box control. /// ////// /// [ SRCategory(SR.CatBehavior), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxModifiedDescr) ] public bool Modified { get { if (IsHandleCreated) { bool curState = (0 != (int) SendMessage(NativeMethods.EM_GETMODIFY, 0, 0)); if (textBoxFlags[modified] != curState) { // VSWhidbey 94719 - raise ModifiedChanged event. See WmReflectCommand for more info. textBoxFlags[modified] = curState; OnModifiedChanged(EventArgs.Empty); } return curState; } else{ return textBoxFlags[modified]; } } set { if (Modified != value) { if (IsHandleCreated) { SendMessage(NativeMethods.EM_SETMODIFY, value ? 1 : 0, 0); // VSWhidbey 94719 - must maintain this state always in order for the // test in the Get method to work properly. } textBoxFlags[modified] = value; OnModifiedChanged(EventArgs.Empty); } } } ////// Gets or sets a value that indicates that the text box control has been modified by the user since /// the control was created or its contents were last set. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnModifiedChangedDescr)] public event EventHandler ModifiedChanged { add { Events.AddHandler(EVENT_MODIFIEDCHANGED, value); } remove { Events.RemoveHandler(EVENT_MODIFIEDCHANGED, value); } } ///[To be supplied.] ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Localizable(true), SRDescription(SR.TextBoxMultilineDescr), RefreshProperties(RefreshProperties.All) ] public virtual bool Multiline { get { return textBoxFlags[multiline]; } set { if (textBoxFlags[multiline] != value) { using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.Multiline)) { textBoxFlags[multiline] = value; if (value) { // Multi-line textboxes do not have fixed height // SetStyle(ControlStyles.FixedHeight, false); } else { // Single-line textboxes may have fixed height, depending on AutoSize SetStyle(ControlStyles.FixedHeight, AutoSize); } RecreateHandle(); AdjustHeight(false); OnMultilineChanged(EventArgs.Empty); } } } } ////// Gets or sets a value indicating whether this /// is a multiline text box control. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnMultilineChangedDescr)] public event EventHandler MultilineChanged { add { Events.AddHandler(EVENT_MULTILINECHANGED, value); } remove { Events.RemoveHandler(EVENT_MULTILINECHANGED, value); } } ///[To be supplied.] ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new Padding Padding { get { return base.Padding; } set { base.Padding = value;} } ////// ///[To be supplied.] ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRCategory(SR.CatLayout), SRDescription(SR.ControlOnPaddingChangedDescr) ] public new event EventHandler PaddingChanged { add { base.PaddingChanged += value; } remove { base.PaddingChanged -= value; } } ///[To be supplied.] ////// Determines if the control is in password protect mode. This is overridden in TextBox and /// MaskedTextBox and is false by default so RichTextBox that doesn't support Password doesn't /// have to care about this. /// virtual internal bool PasswordProtect { get { return false; } } ////// /// [ SRCategory(SR.CatLayout), Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxPreferredHeightDescr) ] public int PreferredHeight { get { // VSWhidbey 523205: COMPAT we must return the same busted height we did in Everett, even // if it doesnt take multiline and word wrap into account. For better accuracy and/or wrapping use // GetPreferredSize instead. int height = FontHeight; if (borderStyle != BorderStyle.None) { height += SystemInformation.BorderSize.Height * 4 + 3; } return height; } } // GetPreferredSizeCore // VSWhidbey 523205: This method can return a different value than PreferredHeight! It properly handles // border style + multiline and wordwrap. internal override Size GetPreferredSizeCore(Size proposedConstraints) { // 3px vertical space is required between the text and the border to keep the last // line from being clipped. // This 3 pixel size was added in everett and we do this to maintain compat. // old everett behavior was FontHeight + [SystemInformation.BorderSize.Height * 4 + 3] // however the [ ] was only added if borderstyle was not none. Size bordersAndPadding = SizeFromClientSize(Size.Empty) + Padding.Size; if (BorderStyle != BorderStyle.None) { bordersAndPadding += new Size(0, 3); } if (BorderStyle == BorderStyle.FixedSingle) { // VSWhidbey 321520: bump these by 2px to match BorderStyle.Fixed3D - they'll be omitted from the SizeFromClientSize call. bordersAndPadding.Width +=2; bordersAndPadding.Height +=2; } // Reduce constraints by border/padding size proposedConstraints -= bordersAndPadding; // Fit the text to the remaining space TextFormatFlags format = TextFormatFlags.Default; if(!Multiline) { format = TextFormatFlags.SingleLine; } else if(WordWrap) { format = TextFormatFlags.WordBreak; } Size textSize = TextRenderer.MeasureText(this.Text, this.Font, proposedConstraints, format); // We use this old computation as a lower bound to ensure backwards compatibility. textSize.Height = Math.Max(textSize.Height, FontHeight); Size preferredSize = textSize + bordersAndPadding; return preferredSize; } ////// Returns the preferred /// height for a single-line text box. /// ////// Get the currently selected text start position and length. Use this method internally /// to avoid calling SelectionStart + SelectionLength each of which does essentially the /// same (save one message round trip). /// internal void GetSelectionStartAndLength( out int start, out int length ){ int end = 0; if( !IsHandleCreated ) { // It is possible that the cached values are no longer valid if the Text has been changed // while the control does not have a handle. We need to return valid values. We also need // to keep the old cached values in case the Text is changed again making the cached values // valid again. See VSW#495181. AdjustSelectionStartAndEnd( this.selectionStart, this.selectionLength, out start, out end, -1 ); length = end - start; } else { start = 0; UnsafeNativeMethods.SendMessage( new HandleRef( this, Handle ), NativeMethods.EM_GETSEL, ref start, ref end ); //Here, we return the max of either 0 or the # returned by //the windows call. This eliminates a problem on nt4 where // a huge negative # is being returned. // start = Math.Max( 0, start ); // ditto for end end = Math.Max( 0, end ); if( this.SelectionUsesDbcsOffsetsInWin9x && Marshal.SystemDefaultCharSize == 1 ) { // When processing EM_GETSEL, EDIT control returns byte offsets instead of character offsets, this // makes a difference in unicode code pages like Japanese. We need to adjust the offsets. ToUnicodeOffsets( WindowText, ref start, ref end ); } length = end - start; } #if DEBUG { string t = WindowText; int len; end = start + length - 1; if (t == null) { len = 0; } else { len = t.Length; } Debug.Assert(end <= len, "SelectionEnd is outside the set of valid caret positions for the current WindowText (end =" + end + ", WindowText.Length =" + len +")"); } #endif } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), RefreshProperties(RefreshProperties.Repaint), SRDescription(SR.TextBoxReadOnlyDescr) ] public bool ReadOnly { get { return textBoxFlags[readOnly]; } set { if (textBoxFlags[readOnly] != value) { textBoxFlags[readOnly] = value; if (IsHandleCreated) { SendMessage(NativeMethods.EM_SETREADONLY, value? -1: 0, 0); } OnReadOnlyChanged(EventArgs.Empty); VerifyImeRestrictedModeChanged(); } } } ////// Gets or sets a value indicating whether text in the text box is read-only. /// ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.TextBoxBaseOnReadOnlyChangedDescr)] public event EventHandler ReadOnlyChanged { add { Events.AddHandler(EVENT_READONLYCHANGED, value); } remove { Events.RemoveHandler(EVENT_READONLYCHANGED, value); } } ///[To be supplied.] ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectedTextDescr) ] public virtual string SelectedText { get { int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); return Text.Substring(selStart, selLength); } set { SetSelectedTextInternal(value, true); } } ////// The currently selected text in the control. /// ////// Replaces the selected text with the one passed in. /// internal virtual void SetSelectedTextInternal(string text, bool clearUndo){ if (!IsHandleCreated) { CreateHandle(); } if( text == null ){ text = ""; } // The EM_LIMITTEXT message limits only the text the user can enter. It does not affect any text // already in the edit control when the message is sent, nor does it affect the length of the text // copied to the edit control by the WM_SETTEXT message. SendMessage(NativeMethods.EM_LIMITTEXT, 0, 0); if( clearUndo ){ SendMessage(NativeMethods.EM_REPLACESEL, 0, text); // For consistency with Text, we clear the modified flag SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); ClearUndo(); } else{ SendMessage(NativeMethods.EM_REPLACESEL, /*undoable*/ -1, text); } // Re-enable user input. SendMessage(NativeMethods.EM_LIMITTEXT, maxLength, 0); } ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectionLengthDescr) ] public virtual int SelectionLength { get { int start, length; GetSelectionStartAndLength( out start, out length ); return length; } set { if (value < 0){ throw new ArgumentOutOfRangeException("SelectionLength", SR.GetString(SR.InvalidArgument, "SelectionLength", value.ToString(CultureInfo.CurrentCulture))); } int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); if (value != selLength) { Select(selStart, value); } } } ////// Gets or sets the number of characters selected in the text /// box. /// ////// /// [ SRCategory(SR.CatAppearance), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.TextBoxSelectionStartDescr) ] public int SelectionStart { get { int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); return selStart; } set { if (value < 0){ throw new ArgumentOutOfRangeException("SelectionStart", SR.GetString(SR.InvalidArgument, "SelectionStart", value.ToString(CultureInfo.CurrentCulture))); } Select(value, SelectionLength); } } // Call SetSelectionOnHandle inside CreateHandle() internal virtual bool SetSelectionInCreateHandle { get { return true; } } ////// Gets or sets the starting /// point of text selected in the text /// box. /// ////// /// [ Localizable(true), Editor("System.ComponentModel.Design.MultilineStringEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) ] public override string Text { get { return base.Text; } set { if (value != base.Text) { base.Text = value; if (IsHandleCreated) { // clear the modified flag SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); } } } } ////// Gets or sets /// the current text in the text box. /// ///[Browsable(false)] public virtual int TextLength { get { // Note: Currently Winforms does not fully support surrogates - VSW#327396. If // the text contains surrogate characters this property may return incorrect values. if (IsHandleCreated && Marshal.SystemDefaultCharSize == 2) { return SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle)); } else { return Text.Length; } } } /// /// Specifies whether the control uses unicode to set/get text selection information (WM_GESEL/WM_SETSEL) /// in Win9x. /// internal virtual bool SelectionUsesDbcsOffsetsInWin9x { get { return true; } } // Since setting the WindowText while the handle is created // generates a WM_COMMAND message, we must trap that case // and prevent the event from getting fired, or we get // double "TextChanged" events. // ////// /// /// internal override string WindowText { get { return base.WindowText; } set { if (value == null) value = ""; if (!WindowText.Equals(value)) { textBoxFlags[codeUpdateText] = true; try { base.WindowText = value; } finally { textBoxFlags[codeUpdateText] = false; } } } } ////// For VSWhidbey 325345. In certain circumstances we might have to force /// text into the window whether or not the text is the same. /// Make this a method on TextBoxBase rather than RichTextBox (which is the only /// control that needs this at this point), since we need to set codeUpdateText. /// internal void ForceWindowText(string value) { if (value == null) { value = ""; } textBoxFlags[codeUpdateText] = true; try { if (IsHandleCreated) { UnsafeNativeMethods.SetWindowText(new HandleRef(this, Handle), value); } else { if (value.Length == 0) { Text = null; } else { Text = value; } } } finally { textBoxFlags[codeUpdateText] = false; } } ////// /// [ SRCategory(SR.CatBehavior), Localizable(true), DefaultValue(true), SRDescription(SR.TextBoxWordWrapDescr) ] public bool WordWrap { get { return textBoxFlags[wordWrap]; } set { using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.WordWrap)) { if (textBoxFlags[wordWrap] != value) { textBoxFlags[wordWrap] = value; RecreateHandle(); } } } } ////// Gets or sets a value indicating whether a /// multiline text box control automatically wraps words to the beginning of the next /// line when necessary. /// ////// /// Adjusts the height of a single-line edit control to match the height of /// the control's font. /// ///private void AdjustHeight(bool returnIfAnchored) { // If we're anchored to two opposite sides of the form, don't adjust the size because // we'll lose our anchored size by resetting to the requested width. // if (returnIfAnchored && (this.Anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom)) { return; } int saveHeight = requestedHeight; try { if (textBoxFlags[autoSize] && !textBoxFlags[multiline]) { Height = PreferredHeight; } else { int curHeight = Height; // Changing the font of a multi-line textbox can sometimes cause a painting problem // The only workaround I can find is to size the textbox big enough for the font, and // then restore its correct size. // if (textBoxFlags[multiline]) { Height = Math.Max(saveHeight, PreferredHeight + 2); // 2 = fudge factor } integralHeightAdjust = true; try { Height = saveHeight; } finally { integralHeightAdjust = false; } } } finally { requestedHeight = saveHeight; } } /// /// /// public void AppendText( string text ) { if (text.Length > 0) { int selStart, selLength; GetSelectionStartAndLength( out selStart, out selLength ); try { // This enables you to use SelectionColor to AppendText in color. int endOfText = GetEndPosition(); SelectInternal(endOfText, endOfText, endOfText); SelectedText = text; } finally { // If AppendText is called when the control is docked and the form is minimized, // all the text will scroll to the top and the control will look empty when the // form is restored. We work around this by selecting back whatever was originally // selected when AppendText was called. if (this.Width == 0 || this.Height == 0) { this.Select(selStart, selLength); } } } } ////// Append text to the current text of text box. /// ////// /// public void Clear() { Text = null; } ////// Clears all text from the text box control. /// ////// /// public void ClearUndo() { if (IsHandleCreated) { SendMessage(NativeMethods.EM_EMPTYUNDOBUFFER, 0, 0); } } ////// Clears information about the most recent operation /// from the undo buffer of the text box. /// ////// /// [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] public void Copy() { SendMessage(NativeMethods.WM_COPY, 0, 0); } ////// Copies the current selection in the text box to the Clipboard. /// ////// /// ///protected override void CreateHandle() { // This "creatingHandle" stuff is to avoid property change events // when we set the Text property. textBoxFlags[creatingHandle] = true; try { base.CreateHandle(); if (SetSelectionInCreateHandle) { // send EM_SETSEL message SetSelectionOnHandle(); } } finally { textBoxFlags[creatingHandle] = false; } } /// /// /// public void Cut() { SendMessage(NativeMethods.WM_CUT, 0, 0); } ////// Moves the current selection in the text box to the Clipboard. /// ////// Returns the text end position (one past the last input character). This property is virtual to allow MaskedTextBox /// to set the last input char position as opposed to the last char position which may be a mask character. /// internal virtual int GetEndPosition(){ // +1 because RichTextBox has this funny EOF pseudo-character after all the text. return IsHandleCreated ? TextLength + 1 : TextLength; } ////// /// /// Overridden to handle TAB key. /// protected override bool IsInputKey(Keys keyData) { if ((keyData & Keys.Alt) != Keys.Alt) { switch (keyData & Keys.KeyCode) { case Keys.Tab: // Single-line RichEd's want tab characters (see WM_GETDLGCODE), // so we don't ask it return Multiline && textBoxFlags[acceptsTab] && ((keyData & Keys.Control) == 0); case Keys.Escape: if (Multiline) return false; break; case Keys.PageUp: case Keys.PageDown: case Keys.Home: case Keys.End: return true; // else fall through to base } } return base.IsInputKey(keyData); } ////// /// /// Overridden to update the newly created handle with the settings of the /// MaxLength and PasswordChar properties. /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); // it's likely here that the create params could have changed // the border size/etc. CommonProperties.xClearPreferredSizeCache(this); AdjustHeight(true); UpdateMaxLength(); if (textBoxFlags[modified]){ SendMessage(NativeMethods.EM_SETMODIFY, 1, 0); } if (textBoxFlags[scrollToCaretOnHandleCreated]) { ScrollToCaret(); textBoxFlags[scrollToCaretOnHandleCreated] = false; } } ////// /// ///protected override void OnHandleDestroyed(EventArgs e) { textBoxFlags[modified] = Modified; textBoxFlags[setSelectionOnHandleCreated] = true; // Update text selection cached values to be restored when recreating the handle. GetSelectionStartAndLength( out this.selectionStart, out this.selectionLength ); base.OnHandleDestroyed(e); } /// /// /// [UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)] public void Paste() { Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ClipboardRead Demanded"); IntSecurity.ClipboardRead.Demand(); SendMessage(NativeMethods.WM_PASTE, 0, 0); } ////// Replaces the current selection in the text box with the contents of the Clipboard. /// ////// /// [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] protected override bool ProcessDialogKey(Keys keyData) { Debug.WriteLineIf(ControlKeyboardRouting.TraceVerbose, "TextBoxBase.ProcessDialogKey [" + keyData.ToString() + "]"); Keys keyCode = (Keys)keyData & Keys.KeyCode; if (keyCode == Keys.Tab && this.AcceptsTab && (keyData & Keys.Control) != 0) { // When this control accepts Tabs, Ctrl-Tab is treated exactly like Tab. keyData &= ~Keys.Control; } return base.ProcessDialogKey(keyData); } ///[To be supplied.] ////// /// TextBox / RichTextBox Onpaint. /// ///[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event PaintEventHandler Paint { add { base.Paint += value; } remove { base.Paint -= value; } } /// /// /// protected virtual void OnAcceptsTabChanged(EventArgs e) { EventHandler eh = Events[EVENT_ACCEPTSTABCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// /// protected virtual void OnBorderStyleChanged(EventArgs e) { EventHandler eh = Events[EVENT_BORDERSTYLECHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// /// protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); AdjustHeight(false); } ///[To be supplied.] ////// /// protected virtual void OnHideSelectionChanged(EventArgs e) { EventHandler eh = Events[EVENT_HIDESELECTIONCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// /// protected virtual void OnModifiedChanged(EventArgs e) { EventHandler eh = Events[EVENT_MODIFIEDCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ///[To be supplied.] ////// Raises the MouseUp event. /// protected override void OnMouseUp(MouseEventArgs mevent) { Point pt = PointToScreen(mevent.Location); if (mevent.Button == MouseButtons.Left) { if (!ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { if (!this.doubleClickFired) { OnClick(mevent); OnMouseClick(mevent); } else { this.doubleClickFired = false; OnDoubleClick(mevent); OnMouseDoubleClick(mevent); } } this.doubleClickFired = false; } base.OnMouseUp(mevent); } ////// /// protected virtual void OnMultilineChanged(EventArgs e) { EventHandler eh = Events[EVENT_MULTILINECHANGED] as EventHandler; if (eh != null) { eh(this, e); } } protected override void OnPaddingChanged(EventArgs e) { base.OnPaddingChanged(e); AdjustHeight(false); } ///[To be supplied.] ////// /// protected virtual void OnReadOnlyChanged(EventArgs e) { EventHandler eh = Events[EVENT_READONLYCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } protected override void OnTextChanged(EventArgs e) { // since AutoSize existed in Everett, (and is the default) we can't // relayout the parent when the "preferredsize" of the control changes. // this means a multiline = true textbox wont natrually grow in height when // the text changes. CommonProperties.xClearPreferredSizeCache(this); base.OnTextChanged(e); } ///[To be supplied.] ////// /// Returns the character nearest to the given point. /// public virtual char GetCharFromPosition(Point pt) { string t = this.Text; int index = GetCharIndexFromPosition(pt); return (index < 0 || index >= t.Length) ? (char)0 : t[index]; } ////// /// Returns the index of the character nearest to the given point. /// public virtual int GetCharIndexFromPosition(Point pt) { int longPoint = NativeMethods.Util.MAKELONG(pt.X, pt.Y); int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, longPoint); index = NativeMethods.Util.LOWORD(index); if (index < 0) { index = 0; } else { string t = this.Text; // EM_CHARFROMPOS will return an invalid number if the last character in the RichEdit // is a newline. // if (index >= t.Length) { index = Math.Max(t.Length - 1, 0); } } return index; } ////// /// Returns the number of the line containing a specified character position /// in a textbox. Note that this returns the physical line number /// and not the conceptual line number. For example, if the first conceptual /// line (line number 0) word-wraps and extends to the second line, and if /// you pass the index of a overflowed character, GetLineFromCharIndex would /// return 1 and not 0. /// public virtual int GetLineFromCharIndex(int index) { return (int)SendMessage(NativeMethods.EM_LINEFROMCHAR, index, 0); } ////// /// Returns the location of the character at the given index. /// public virtual Point GetPositionFromCharIndex(int index) { if (index < 0 || index >= Text.Length) return Point.Empty; int i = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_POSFROMCHAR, index, 0); return new Point(NativeMethods.Util.LOWORD(i), NativeMethods.Util.HIWORD(i)); } ////// /// Returns the index of the first character of a given line. Returns -1 of lineNumber is invalid. /// public int GetFirstCharIndexFromLine(int lineNumber) { if (lineNumber < 0) { throw new ArgumentOutOfRangeException("lineNumber", SR.GetString(SR.InvalidArgument, "lineNumber", lineNumber.ToString(CultureInfo.CurrentCulture))); } return (int)SendMessage(NativeMethods.EM_LINEINDEX, lineNumber, 0); } ////// /// Returns the index of the first character of the line where the caret is. /// public int GetFirstCharIndexOfCurrentLine() { return (int)SendMessage(NativeMethods.EM_LINEINDEX, -1, 0); } ////// /// Ensures that the caret is visible in the TextBox window, by scrolling the /// TextBox control surface if necessary. /// public void ScrollToCaret() { if (IsHandleCreated) { if (String.IsNullOrEmpty(this.WindowText)) { // If there is no text, then there is no place to go. return; } bool scrolled = false; object editOle = null; IntPtr editOlePtr = IntPtr.Zero; try { if (UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETOLEINTERFACE, 0, out editOle) != 0) { editOlePtr = Marshal.GetIUnknownForObject(editOle); if (editOlePtr != IntPtr.Zero) { IntPtr iTextDocument = IntPtr.Zero; Guid iiTextDocumentGuid = typeof(UnsafeNativeMethods.ITextDocument).GUID; try { Marshal.QueryInterface(editOlePtr, ref iiTextDocumentGuid, out iTextDocument); UnsafeNativeMethods.ITextDocument textDocument = Marshal.GetObjectForIUnknown(iTextDocument) as UnsafeNativeMethods.ITextDocument; if (textDocument != null) { int selStart, selLength; // vsWhidbey 372764: when the user calls RichTextBox::ScrollToCaret we want the RichTextBox to show as // much text as possible. // Here is how we do that: // 1. We scroll the RichTextBox all the way to the bottom so the last line of text is the last visible line. // 2. We get the first visible line. // 3. If the first visible line is smaller than the start of the selection, then we are done: // The selection fits inside the RichTextBox display rectangle. // 4. Otherwise, scroll the selection to the top of the RichTextBox. GetSelectionStartAndLength( out selStart, out selLength ); int selStartLine = GetLineFromCharIndex(selStart); // 1. Scroll the RichTextBox all the way to the bottom UnsafeNativeMethods.ITextRange textRange = textDocument.Range(this.WindowText.Length - 1, this.WindowText.Length - 1); textRange.ScrollIntoView(0); // 0 ==> tomEnd // 2. Get the first visible line. int firstVisibleLine = (int) SendMessage(NativeMethods.EM_GETFIRSTVISIBLELINE, 0, 0); // 3. If the first visible line is smaller than the start of the selection, we are done; if (firstVisibleLine <= selStartLine) { // we are done } else { // 4. Scroll the selection to the top of the RichTextBox textRange = textDocument.Range(selStart, selStart + selLength); textRange.ScrollIntoView(32); // 32 ==> tomStart } scrolled = true; } } finally { if (iTextDocument != IntPtr.Zero) { Marshal.Release(iTextDocument); } } } } } finally { if (editOlePtr != IntPtr.Zero) { Marshal.Release(editOlePtr); } } if (!scrolled) { SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0); } } else { textBoxFlags[scrollToCaretOnHandleCreated] = true; } } ////// /// public void DeselectAll() { this.SelectionLength = 0; } ////// Sets the SelectionLength to 0. /// ////// /// public void Select(int start, int length) { if (start < 0){ throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidArgument, "start", start.ToString(CultureInfo.CurrentCulture))); } int textLen = TextLength; if (start > textLen) { //We shouldn't allow positive length if you're starting at the end, but //should allow negative length. long longLength = Math.Min(0, (long)length + start - textLen); if (longLength < int.MinValue) { length = int.MinValue; } else { length = (int)longLength; } start = textLen; } SelectInternal(start, length, textLen); } ////// Selects a range of text in the text box. /// ////// Performs the actual select without doing arg checking. /// /// Send in -1 for the textLen parameter if you don't have the text /// length cached when calling this method. It will be computed. /// But if you do have it cached, please pass it in. This will avoid /// the expensive call to the TextLength property. /// internal virtual void SelectInternal(int start, int length, int textLen) { //if our handle is created - send message... if (IsHandleCreated) { int s, e; AdjustSelectionStartAndEnd(start, length, out s, out e, textLen); SendMessage(NativeMethods.EM_SETSEL, s, e); // } else { //otherwise, wait until handle is created to send this message. //Store the indices until then... this.selectionStart = start; this.selectionLength = length; textBoxFlags[setSelectionOnHandleCreated] = true; } } ////// /// public void SelectAll() { int textLen = TextLength; SelectInternal(0, textLen, textLen); } ////// Selects all text in the text box. /// ////// /// Overrides Control.setBoundsCore to enforce autoSize. /// ///protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { if (!integralHeightAdjust && height != Height) requestedHeight = height; if (textBoxFlags[autoSize] && !textBoxFlags[multiline]) height = PreferredHeight; base.SetBoundsCore(x, y, width, height, specified); } private static void Swap(ref int n1, ref int n2) { int temp = n2; n2 = n1; n1 = temp; } // // Send in -1 if you don't have the text length cached // when calling this method. It will be computed. If not, // please pass in the text length as the last parameter. // This will avoid the expensive call to the TextLength // property. internal void AdjustSelectionStartAndEnd(int selStart, int selLength, out int start, out int end, int textLen) { start = selStart; end = 0; if (start <= -1) { start = -1; } else { int textLength; if (textLen >= 0) { textLength = textLen; } else { textLength = this.TextLength; } if (start > textLength) { start = textLength; } checked { try { end = start + selLength; } catch (OverflowException) { //Since we overflowed, cap at the max/min value: we'll correct the value below end = start > 0 ? int.MaxValue : int.MinValue; } } // Make sure end is in range if (end < 0) { end = 0; } else if (end > textLength) { end = textLength; } if (this.SelectionUsesDbcsOffsetsInWin9x && Marshal.SystemDefaultCharSize == 1) { // EDIT control expects selection values to be byte offsets instead of character offsets, // this makes a difference in unicode code pages like Japanese. // We need to adjust the offsets. ToDbcsOffsets(WindowText, ref start, ref end); } } } // Called by CreateHandle or OnHandleCreated internal void SetSelectionOnHandle() { Debug.Assert(IsHandleCreated, "Don't call this method until the handle is created."); if (textBoxFlags[setSelectionOnHandleCreated]) { textBoxFlags[setSelectionOnHandleCreated] = false; int start, end; AdjustSelectionStartAndEnd(this.selectionStart, this.selectionLength, out start, out end, -1); SendMessage(NativeMethods.EM_SETSEL, start, end); } } /// /// Converts byte offsset to unicode offsets. /// When procssing WM_GETSEL/WM_SETSEL, EDIT control works with byte offsets instead of character positions /// as opposed to RICHEDIT which does it always as character positions. /// This method is used when handling the WM_GETSEL message. /// static void ToUnicodeOffsets(string str, ref int start, ref int end) { Encoding e = Encoding.Default; // Acutally, we may get away with this call if we can get the bytes from Win9x. Dont know if it is possible. // This can be expensive since start/end could be small, but str.Length can be quite big. byte[] bytes = e.GetBytes(str); bool swap = start > end; if (swap) { Swap(ref start, ref end); } // Make sure start and end are within the string // if (start < 0){ start = 0; } if (start > bytes.Length){ start = bytes.Length; } if (end > bytes.Length){ end = bytes.Length; } // IMPORTANT: Avoid off-by-1 errors! // The end value passed in is the character immediately after the last character selected. int newStart = start == 0 ? 0 : e.GetCharCount(bytes, 0, start); end = newStart + e.GetCharCount(bytes, start, end - start); start = newStart; if (swap){ Swap(ref start, ref end); } } ////// Converts unicode offsset to byte offsets. /// When procssing WM_GETSEL/WM_SETSEL, EDIT control works with byte offsets instead of character positions /// as opposed to RICHEDIT which does it always as character positions. /// This method is used when handling the WM_SETSEL message. /// static internal void ToDbcsOffsets(string str, ref int start, ref int end) { Encoding e = Encoding.Default; bool swap = start > end; if (swap) { Swap(ref start, ref end); } // Make sure start and end are within the string // if (start < 0) { start = 0; } if (start > str.Length) { start = str.Length; } if (end < start) { end = start; } if (end > str.Length) { end = str.Length; } // IMPORTANT: Avoid off-by-1 errors! // The end value passed in is the character immediately after the last character selected. int newStart = start == 0 ? 0 : e.GetByteCount(str.Substring(0, start)); end = newStart + e.GetByteCount(str.Substring(start, end - start)); start = newStart; if (swap) { Swap(ref start, ref end); } } ////// /// Provides some interesting information for the TextBox control in /// String form. /// ///public override string ToString() { string s = base.ToString(); string txt = Text; if (txt.Length > 40) txt = txt.Substring(0, 40) + "..."; return s + ", Text: " + txt.ToString(); } /// /// /// public void Undo() { SendMessage(NativeMethods.EM_UNDO, 0, 0); } internal virtual void UpdateMaxLength() { if (IsHandleCreated) { SendMessage(NativeMethods.EM_LIMITTEXT, maxLength, 0); } } internal override IntPtr InitializeDCForWmCtlColor (IntPtr dc, int msg) { if ((msg == NativeMethods.WM_CTLCOLORSTATIC) && !ShouldSerializeBackColor()) { // Let the Win32 Edit control handle background colors itself. // This is necessary because a disabled edit control will display a different // BackColor than when enabled. return IntPtr.Zero; } else { return base.InitializeDCForWmCtlColor(dc, msg); } } ////// Undoes the last edit operation in the text box. /// ////// /// ///private void WmReflectCommand(ref Message m) { if (!textBoxFlags[codeUpdateText] && !textBoxFlags[creatingHandle]) { if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.EN_CHANGE && CanRaiseTextChangedEvent) { OnTextChanged(EventArgs.Empty); } else if (NativeMethods.Util.HIWORD(m.WParam) == NativeMethods.EN_UPDATE) { // VSWhidbey 94719: force update to the Modified property, which will trigger // ModifiedChanged event handlers bool force = this.Modified; } } } /// /// /// ///void WmSetFont(ref Message m) { base.WndProc(ref m); if (!textBoxFlags[multiline]) { SendMessage(NativeMethods.EM_SETMARGINS, NativeMethods.EC_LEFTMARGIN | NativeMethods.EC_RIGHTMARGIN, 0); } } void WmGetDlgCode(ref Message m) { base.WndProc(ref m); if (AcceptsTab) { Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "TextBox wants tabs"); m.Result = (IntPtr)((int)m.Result | NativeMethods.DLGC_WANTTAB); } else { Debug.WriteLineIf(Control.ControlKeyboardRouting.TraceVerbose, "TextBox doesn't want tabs"); m.Result = (IntPtr)((int)m.Result & ~(NativeMethods.DLGC_WANTTAB | NativeMethods.DLGC_WANTALLKEYS)); } } /// /// Handles the WM_CONTEXTMENU message based on this /// table: /// ShortcutsEnabled #1 #2 #3 /// Yes strip context system /// No strip context N/A /// ///private void WmTextBoxContextMenu(ref Message m) { if (ContextMenu != null || ContextMenuStrip != null) { int x = NativeMethods.Util.SignedLOWORD(m.LParam); int y = NativeMethods.Util.SignedHIWORD(m.LParam); Point client; bool keyboardActivated = false; // lparam will be exactly -1 when the user invokes the context menu // with the keyboard. // if (unchecked((int)(long)m.LParam) == -1) { keyboardActivated = true; client = new Point(Width/2, Height/2); } else { client = PointToClientInternal(new Point(x, y)); } // // VisualStudio7 # 156, only show the context menu when clicked in the client area if (ClientRectangle.Contains( client )) { if (ContextMenu != null) { ContextMenu.Show(this, client); } else if (ContextMenuStrip != null) { ContextMenuStrip.ShowInternal(this, client, keyboardActivated); } else { Debug.Fail("contextmenu and contextmenustrip are both null... hmm how did we get here?"); DefWndProc( ref m ); } } } } /// /// /// /// The control's window procedure. Inheriting classes can override this /// to add extra functionality, but should not forget to call /// base.wndProc(m); to ensure the control continues to function properly. /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_LBUTTONDBLCLK: this.doubleClickFired = true; base.WndProc(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: WmReflectCommand(ref m); break; case NativeMethods.WM_GETDLGCODE: WmGetDlgCode(ref m); break; case NativeMethods.WM_SETFONT: WmSetFont(ref m); break; case NativeMethods.WM_CONTEXTMENU: if (ShortcutsEnabled) { //calling base will find ContextMenus in this order: // 1) ContextMenu 2) ContextMenuStrip 3) SystemMenu base.WndProc(ref m); } else { // we'll handle this message so we can hide the // SystemMenu if Context and Strip menus are null WmTextBoxContextMenu(ref m); } break; default: base.WndProc(ref m); break; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SelectedCellsCollection.cs
- UpdateCommand.cs
- CodeBinaryOperatorExpression.cs
- ProviderSettings.cs
- EtwProvider.cs
- FontFamily.cs
- ArraySortHelper.cs
- ResumeStoryboard.cs
- ToolStripStatusLabel.cs
- ButtonAutomationPeer.cs
- LicFileLicenseProvider.cs
- selecteditemcollection.cs
- TypeInfo.cs
- COAUTHINFO.cs
- ComponentGlyph.cs
- SynchronizingStream.cs
- BordersPage.cs
- ComplexLine.cs
- IndicCharClassifier.cs
- DataTableTypeConverter.cs
- InputLanguageCollection.cs
- ReachDocumentReferenceCollectionSerializerAsync.cs
- SecurityUtils.cs
- MimeTypeMapper.cs
- PartitionResolver.cs
- SqlClientWrapperSmiStreamChars.cs
- DataGridPagingPage.cs
- DesignerFrame.cs
- MyContact.cs
- XamlSerializer.cs
- DataSet.cs
- Menu.cs
- OleDbConnection.cs
- SQLDateTime.cs
- ConstructorExpr.cs
- Configuration.cs
- FixedDocumentSequencePaginator.cs
- ReadOnlyDataSource.cs
- SqlVersion.cs
- WsatAdminException.cs
- ImageAttributes.cs
- Color.cs
- DataTableTypeConverter.cs
- PackWebRequest.cs
- HandlerFactoryWrapper.cs
- BitmapImage.cs
- GetPageNumberCompletedEventArgs.cs
- MarginsConverter.cs
- TypeUnloadedException.cs
- ChannelServices.cs
- Oid.cs
- HttpListenerResponse.cs
- Intellisense.cs
- Keywords.cs
- XmlUtf8RawTextWriter.cs
- SimpleBitVector32.cs
- TypeConverterHelper.cs
- IssuedTokenServiceElement.cs
- CellNormalizer.cs
- CodeTypeMemberCollection.cs
- StrongNameKeyPair.cs
- RadioButton.cs
- BuildTopDownAttribute.cs
- QilSortKey.cs
- MetadataItemEmitter.cs
- StylusCollection.cs
- ReadOnlyDataSource.cs
- PixelShader.cs
- PeerNameRecord.cs
- SapiRecoInterop.cs
- EventLogHandle.cs
- OutputWindow.cs
- SafeFileHandle.cs
- TabControlEvent.cs
- EntityContainerEmitter.cs
- DataGridCheckBoxColumn.cs
- RichTextBoxDesigner.cs
- InternalDispatchObject.cs
- Pens.cs
- AliasedExpr.cs
- HtmlElement.cs
- TableRow.cs
- SortKey.cs
- PathFigure.cs
- XmlSchemaComplexContent.cs
- WmpBitmapEncoder.cs
- SqlNodeTypeOperators.cs
- ToolBarButtonClickEvent.cs
- QualifiedCellIdBoolean.cs
- coordinatorfactory.cs
- LinkButton.cs
- WeakEventManager.cs
- followingsibling.cs
- WebPartsSection.cs
- ReferenceEqualityComparer.cs
- MainMenu.cs
- MSAAEventDispatcher.cs
- XmlUtil.cs
- HttpChannelHelper.cs
- DbConnectionPoolIdentity.cs