Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / WinForms / Managed / System / WinForms / MaskedTextBox.cs / 1 / MaskedTextBox.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using System; using System.Text; using System.ComponentModel; using System.Runtime.InteropServices; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Diagnostics; using System.Collections; using System.Collections.Specialized; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms.Layout; using System.Windows.Forms.VisualStyles; ////// MaskedTextBox control definition class. /// Uses the services from the System.ComponentModel.MaskedTextBoxProvider class. /// See spec at http://dotnetclient/whidbey/Specs/MaskEdit.doc /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), DefaultEvent("MaskInputRejected"), DefaultBindingProperty("Text"), DefaultProperty("Mask"), Designer("System.Windows.Forms.Design.MaskedTextBoxDesigner, " + AssemblyRef.SystemDesign), SRDescription(SR.DescriptionMaskedTextBox) ] public class MaskedTextBox : TextBoxBase { // Consider: The MaskedTextBox control, when initialized with a non-null/empty mask, processes all // WM_CHAR messages and always sets the text using the SetWindowText Windows function in the furthest base // class. This means that the underlying Edit control won't enable Undo operations and the context // menu behavior will be a bit different (for instance Copy option is enabled when PasswordChar is set). // To provide Undo functionality and make the context menu behave like the Edit control, we would have // to implement our own. See http://msdn.microsoft.com/msdnmag/issues/1100/c/default.aspx for more info // about how to do this. See postponed bug VSWhidbey#218402. private const bool forward = true; private const bool backward = false; private const string nullMask = "<>"; // any char/str is OK here. private static readonly object EVENT_MASKINPUTREJECTED = new object(); private static readonly object EVENT_VALIDATIONCOMPLETED = new object(); private static readonly object EVENT_TEXTALIGNCHANGED = new object(); private static readonly object EVENT_ISOVERWRITEMODECHANGED = new object(); private static readonly object EVENT_MASKCHANGED = new object(); // The native edit control's default password char (per thread). See corresponding property for more info. private static char systemPwdChar; // Values to track changes in IME composition string (if any). Having const variables is a bit more efficient // than having an enum (which creates a class). private const byte imeConvertionNone = 0; // no convertion has been performed in the composition string. private const byte imeConvertionUpdate = 1; // the char being composed has been updated but not coverted yet. private const byte imeConvertionCompleted = 2; // the char being composed has been fully converted. ///////// Instance fields // Used for keeping selection when prompt is hidden on leave (text changes). private int lastSelLength; // Used for caret positioning. private int caretTestPos; // Bit mask - Determines when the Korean IME composition string is completed so converted character can be processed. private static int IME_ENDING_COMPOSITION = BitVector32.CreateMask(); // Bit mask - Determines when the Korean IME is completing a composition, used when forcing convertion. private static int IME_COMPLETING = BitVector32.CreateMask(IME_ENDING_COMPOSITION); // Used for handling characters that have a modifier (Ctrl-A, Shift-Del...). private static int HANDLE_KEY_PRESS = BitVector32.CreateMask(IME_COMPLETING); // Bit mask - Used to simulate a null mask. Needed since a MaskedTextProvider object cannot be // initialized with a null mask but we need one even in this case as a backend for // default properties. This is to support creating a MaskedTextBox with the default // constructor, specially at design time. private static int IS_NULL_MASK = BitVector32.CreateMask(HANDLE_KEY_PRESS); // Bit mask - Used in conjuction with get_Text to return the text that is actually set in the native // control. This is required to be able to measure text correctly (GetPreferredSize) and // to compare against during set_Text (to bail if the same and not to raise TextChanged event). private static int QUERY_BASE_TEXT = BitVector32.CreateMask(IS_NULL_MASK); // If true, the input text is rejected whenever a character does not comply with the mask; a MaskInputRejected // event is fired for the failing character. // If false, characters in the input string are processed one by one accepting the ones that comply // with the mask and raising the MaskInputRejected event for the rejected ones. private static int REJECT_INPUT_ON_FIRST_FAILURE = BitVector32.CreateMask( QUERY_BASE_TEXT ); // Bit masks for boolean properties. private static int HIDE_PROMPT_ON_LEAVE = BitVector32.CreateMask(REJECT_INPUT_ON_FIRST_FAILURE); private static int BEEP_ON_ERROR = BitVector32.CreateMask(HIDE_PROMPT_ON_LEAVE); private static int USE_SYSTEM_PASSWORD_CHAR = BitVector32.CreateMask(BEEP_ON_ERROR); private static int INSERT_TOGGLED = BitVector32.CreateMask(USE_SYSTEM_PASSWORD_CHAR); private static int CUTCOPYINCLUDEPROMPT = BitVector32.CreateMask(INSERT_TOGGLED); private static int CUTCOPYINCLUDELITERALS = BitVector32.CreateMask(CUTCOPYINCLUDEPROMPT); ///////// Properties backend fields. See corresponding property comments for more info. private char passwordChar; // control's pwd char, it could be different from the one displayed if using system password. private Type validatingType; private IFormatProvider formatProvider; private MaskedTextProvider maskedTextProvider; private InsertKeyMode insertMode; private HorizontalAlignment textAlign; // Bit vector to represent bool variables. private BitVector32 flagState; ////// Constructs the MaskedTextBox with the specified MaskedTextProvider object. /// public MaskedTextBox() { MaskedTextProvider maskedTextProvider = new MaskedTextProvider(nullMask, CultureInfo.CurrentCulture); this.flagState[IS_NULL_MASK] = true; Initialize(maskedTextProvider); } ////// Constructs the MaskedTextBox with the specified MaskedTextProvider object. /// public MaskedTextBox(string mask) { if (mask == null) { throw new ArgumentNullException(); } MaskedTextProvider maskedTextProvider = new MaskedTextProvider(mask, CultureInfo.CurrentCulture); this.flagState[IS_NULL_MASK] = false; Initialize(maskedTextProvider); } ////// Constructs the MaskedTextBox with the specified MaskedTextProvider object. /// public MaskedTextBox(MaskedTextProvider maskedTextProvider) { if (maskedTextProvider == null) { throw new ArgumentNullException(); } this.flagState[IS_NULL_MASK] = false; Initialize(maskedTextProvider); } ////// Initializes the object with the specified MaskedTextProvider object and default /// property values. /// private void Initialize(MaskedTextProvider maskedTextProvider) { Debug.Assert(maskedTextProvider != null, "Initializing from a null MaskProvider ref."); this.maskedTextProvider = maskedTextProvider; // set the initial display text. if (!this.flagState[IS_NULL_MASK]) { SetWindowText(); } // set default values. this.passwordChar = this.maskedTextProvider.PasswordChar; this.insertMode = InsertKeyMode.Default; this.flagState[HIDE_PROMPT_ON_LEAVE ] = false; this.flagState[BEEP_ON_ERROR ] = false; this.flagState[USE_SYSTEM_PASSWORD_CHAR ] = false; this.flagState[REJECT_INPUT_ON_FIRST_FAILURE] = false; // CutCopyMaskFormat - set same defaults as TextMaskFormat (IncludePromptAndLiterals). // It is a lot easier to handle this flags individually since that's the way the MaskedTextProvider does it. this.flagState[CUTCOPYINCLUDEPROMPT ] = this.maskedTextProvider.IncludePrompt; this.flagState[CUTCOPYINCLUDELITERALS ] = this.maskedTextProvider.IncludeLiterals; // fields for internal use. this.flagState[HANDLE_KEY_PRESS] = true; this.caretTestPos = 0; } /////////////////// Properties /// ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new bool AcceptsTab { get { return false; } set {} } ////// Specifies whether the prompt character should be treated as a valid input character or not. /// The setter resets the underlying MaskedTextProvider object and attempts /// to add the existing input text (if any) using the new mask, failure is ignored. /// This property has no particular effect if no mask has been set. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxAllowPromptAsInputDescr), DefaultValue(true) ] public bool AllowPromptAsInput { get { return this.maskedTextProvider.AllowPromptAsInput; } set { if( value != this.maskedTextProvider.AllowPromptAsInput ) { // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, this.maskedTextProvider.Culture, value, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); SetMaskedTextProvider( newProvider ); } } } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new event EventHandler AcceptsTabChanged { add { } remove { } } ////// Specifies whether only ASCII characters are accepted as valid input. /// This property has no particular effect if no mask has been set. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxAsciiOnlyDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(false) ] public bool AsciiOnly { get { return this.maskedTextProvider.AsciiOnly; } set { if( value != this.maskedTextProvider.AsciiOnly ) { // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, this.maskedTextProvider.Culture, this.maskedTextProvider.AllowPromptAsInput, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, value ); SetMaskedTextProvider( newProvider ); } } } ////// Specifies whether to play a beep when the input is not valid according to the mask. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxBeepOnErrorDescr), DefaultValue(false) ] public bool BeepOnError { get { return this.flagState[BEEP_ON_ERROR]; } set { this.flagState[BEEP_ON_ERROR] = value; } } ////// Gets a value indicating whether the user can undo the previous operation in a text box control. /// Unsupported method/property. /// WndProc ignores EM_CANUNDO. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new bool CanUndo { get { return false; } } ////// 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. /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; // Translate for Rtl if necessary // HorizontalAlignment align = RtlTranslateHorizontal(textAlign); cp.ExStyle &= ~NativeMethods.WS_EX_RIGHT; // WS_EX_RIGHT overrides the ES_XXXX alignment styles switch (align) { case HorizontalAlignment.Left: cp.Style |= NativeMethods.ES_LEFT; break; case HorizontalAlignment.Center: cp.Style |= NativeMethods.ES_CENTER; break; case HorizontalAlignment.Right: cp.Style |= NativeMethods.ES_RIGHT; break; } return cp; } } ////// The culture that determines the value of the localizable mask language separators and placeholders. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxCultureDescr), RefreshProperties(RefreshProperties.Repaint), ] public CultureInfo Culture { get { return this.maskedTextProvider.Culture; } set { if( value == null ) { throw new ArgumentNullException(); } if( !this.maskedTextProvider.Culture.Equals(value) ) { // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, value, this.maskedTextProvider.AllowPromptAsInput, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); SetMaskedTextProvider( newProvider ); } } } ////// Specifies the formatting options for text cut/copited to the clipboard (Whether the mask returned from the Text /// property includes Literals and/or prompt characters). /// When prompt characters are excluded, theyare returned as spaces in the string returned. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxCutCopyMaskFormat), RefreshProperties(RefreshProperties.Repaint), DefaultValue(MaskFormat.IncludeLiterals) ] public MaskFormat CutCopyMaskFormat { get { if( this.flagState[CUTCOPYINCLUDEPROMPT] ) { if( this.flagState[CUTCOPYINCLUDELITERALS] ) { return MaskFormat.IncludePromptAndLiterals; } return MaskFormat.IncludePrompt; } if( this.flagState[CUTCOPYINCLUDELITERALS] ) { return MaskFormat.IncludeLiterals; } return MaskFormat.ExcludePromptAndLiterals; } set { //valid values are 0x0 to 0x3 if (!ClientUtils.IsEnumValid(value, (int)value, (int)MaskFormat.ExcludePromptAndLiterals, (int)MaskFormat.IncludePromptAndLiterals)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(MaskFormat)); } if( value == MaskFormat.IncludePrompt ) { this.flagState[CUTCOPYINCLUDEPROMPT] = true; this.flagState[CUTCOPYINCLUDELITERALS] = false; } else if( value == MaskFormat.IncludeLiterals ) { this.flagState[CUTCOPYINCLUDEPROMPT] = false; this.flagState[CUTCOPYINCLUDELITERALS] = true; } else // value == MaskFormat.IncludePromptAndLiterals || value == MaskFormat.ExcludePromptAndLiterals { bool include = value == MaskFormat.IncludePromptAndLiterals; this.flagState[CUTCOPYINCLUDEPROMPT] = include; this.flagState[CUTCOPYINCLUDELITERALS] = include; } } } ////// Specifies the IFormatProvider to be used when parsing the string to the ValidatingType. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public IFormatProvider FormatProvider { get { return this.formatProvider; } set { this.formatProvider = value; } } ////// Specifies whether the PromptCharacter is displayed when the control loses focus. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxHidePromptOnLeaveDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(false) ] public bool HidePromptOnLeave { get { return this.flagState[HIDE_PROMPT_ON_LEAVE]; } set { if( this.flagState[HIDE_PROMPT_ON_LEAVE] != value ) { this.flagState[HIDE_PROMPT_ON_LEAVE] = value; // If the control is not focused and there are available edit positions (mask not full) we need to // update the displayed text. if( !this.flagState[IS_NULL_MASK]&& !this.Focused && !this.MaskFull && !this.DesignMode ) { SetWindowText(); } } } } ////// Specifies whether to include mask literal characters when formatting the text. /// private bool IncludeLiterals { get { return this.maskedTextProvider.IncludeLiterals; } set { this.maskedTextProvider.IncludeLiterals = value; } } ////// Specifies whether to include the mask prompt character when formatting the text in places /// where an edit char has not being assigned. /// private bool IncludePrompt { get { return this.maskedTextProvider.IncludePrompt; } set { this.maskedTextProvider.IncludePrompt = value; } } ////// Specifies the text insertion mode of the text box. This can be used to simulated the Access masked text /// control behavior where insertion is set to TextInsertionMode.AlwaysOverwrite /// This property has no particular effect if no mask has been set. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxInsertKeyModeDescr), DefaultValue(InsertKeyMode.Default) ] public InsertKeyMode InsertKeyMode { get { return this.insertMode; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)InsertKeyMode.Default, (int)InsertKeyMode.Overwrite)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(InsertKeyMode)); } if (this.insertMode != value) { bool isOverwrite = this.IsOverwriteMode; this.insertMode = value; if (isOverwrite != this.IsOverwriteMode) { OnIsOverwriteModeChanged(EventArgs.Empty); } } } } ////// Overridden to handle unsupported RETURN key. /// protected override bool IsInputKey(Keys keyData) { if ((keyData & Keys.KeyCode) == Keys.Return) { return false; } return base.IsInputKey(keyData); } ////// Specifies whether text insertion mode in 'on' or not. /// [ Browsable(false) ] public bool IsOverwriteMode { get { if( this.flagState[IS_NULL_MASK]) { return false; // EditBox always inserts. } switch (this.insertMode) { case InsertKeyMode.Overwrite: return true; case InsertKeyMode.Insert: return false; case InsertKeyMode.Default: // Note that the insert key state should be per process and its initial state insert, this is the // behavior of apps like WinWord, WordPad and VS; so we have to keep track of it and not query its // system value. //return Control.IsKeyLocked(Keys.Insert); return this.flagState[INSERT_TOGGLED]; default: Debug.Fail("Invalid InsertKeyMode. This code path should have never been executed."); return false; } } } ////// Event to notify when the insert mode has changed. This is required for data binding. /// [ SRCategory(SR.CatPropertyChanged), SRDescription(SR.MaskedTextBoxIsOverwriteModeChangedDescr) ] public event EventHandler IsOverwriteModeChanged { add { Events.AddHandler(EVENT_ISOVERWRITEMODECHANGED, value); } remove { Events.RemoveHandler(EVENT_ISOVERWRITEMODECHANGED, value); } } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new string[] Lines { get { string[] lines; this.flagState[QUERY_BASE_TEXT] = true; try { lines = base.Lines; } finally { this.flagState[QUERY_BASE_TEXT] = false; } return lines; } set {} } ////// The mask applied to this control. The setter resets the underlying MaskedTextProvider object and attempts /// to add the existing input text (if any) using the new mask, failure is ignored. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxMaskDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(""), MergableProperty(false), Localizable(true), Editor("System.Windows.Forms.Design.MaskPropertyEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) ] public string Mask { get { return this.flagState[IS_NULL_MASK]? string.Empty : this.maskedTextProvider.Mask; } set { // // We dont' do anything if: // 1. IsNullOrEmpty( value )->[Reset control] && this.flagState[IS_NULL_MASK]==>Already Reset. // 2. !IsNullOrEmpty( value )->[Set control] && !this.flagState[IS_NULL_MASK][control is set] && [value is the same]==>No need to update. // if( this.flagState[IS_NULL_MASK] == string.IsNullOrEmpty( value ) && (this.flagState[IS_NULL_MASK] || value == this.maskedTextProvider.Mask) ) { return; } string text = null; string newMask = value; // We need to update the this.flagState[IS_NULL_MASK]field before raising any events (when setting the maskedTextProvider) so // querying for properties from an event handler returns the right value (i.e: Text). if( string.IsNullOrEmpty( value ) ) // Resetting the control, the native edit control will be in charge. { // Need to get the formatted & unformatted text before resetting the mask, they'll be used to determine whether we need to // raise the TextChanged event. string formattedText = TextOutput; string unformattedText = this.maskedTextProvider.ToString(false, false); this.flagState[IS_NULL_MASK] = true; if( this.maskedTextProvider.IsPassword ) { SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); } // Set the window text to the unformatted text before raising events. Also, TextChanged needs to be raised after MaskChanged so // pass false to SetWindowText 'raiseTextChanged' param. SetWindowText(unformattedText, false, false ); EventArgs e = EventArgs.Empty; OnMaskChanged(e); if( unformattedText != formattedText ) { OnTextChanged(e); } newMask = nullMask; } else // Setting control to a new value. { foreach( char c in value ) { if( !MaskedTextProvider.IsValidMaskChar( c ) ) { // Same message as in SR.MaskedTextProviderMaskInvalidChar in System.txt throw new ArgumentException( SR.GetString( SR.MaskedTextBoxMaskInvalidChar) ); } } if( this.flagState[IS_NULL_MASK] ) { // If this.IsNullMask, we are setting the mask to a new value; in this case we need to get the text because // the underlying MTP does not have it (used as a property backend only) and pass it to SetMaskedTextProvider // method below to update the provider. text = this.Text; } } // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( newMask, this.maskedTextProvider.Culture, this.maskedTextProvider.AllowPromptAsInput, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); //text == null when setting to a different mask value or when resetting the mask to null. //text != null only when setting the mask from null to some value. SetMaskedTextProvider( newProvider, text ); } } ////// Event to notify when the mask has changed. /// [ SRCategory(SR.CatPropertyChanged), SRDescription(SR.MaskedTextBoxMaskChangedDescr) ] public event EventHandler MaskChanged { add { Events.AddHandler(EVENT_MASKCHANGED, value); } remove { Events.RemoveHandler(EVENT_MASKCHANGED, value); } } ////// Specifies whether the test string required input positions, as specified by the mask, have /// all been assigned. /// [ Browsable(false) ] public bool MaskCompleted { get { return this.maskedTextProvider.MaskCompleted; } } ////// Specifies whether all inputs (required and optional) have been provided into the mask successfully. /// [ Browsable(false) ] public bool MaskFull { get { return this.maskedTextProvider.MaskFull; } } ////// Returns a copy of the control's internal MaskedTextProvider. This is useful for user's to provide /// cloning semantics for the control (we don't want to do it) w/o incurring in any perf penalty since /// some of the properties require recreating the underlying provider when they are changed. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public MaskedTextProvider MaskedTextProvider { get { return this.flagState[IS_NULL_MASK] ? null : (MaskedTextProvider) this.maskedTextProvider.Clone(); } } ////// Event to notify when an input has been rejected according to the mask. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxMaskInputRejectedDescr) ] public event MaskInputRejectedEventHandler MaskInputRejected { add { Events.AddHandler(EVENT_MASKINPUTREJECTED, value); } remove { Events.RemoveHandler(EVENT_MASKINPUTREJECTED, value); } } ////// Unsupported method/property. /// WndProc ignores EM_LIMITTEXT & this is a virtual method. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public override int MaxLength { get{ return base.MaxLength; } set{} } ////// Unsupported method/property. /// virtual method. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public override bool Multiline { get { return false; } set {} } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new event EventHandler MultilineChanged { add { } remove { } } ////// Specifies the character to be used in the formatted string in place of editable characters, if /// set to any printable character, the text box becomes a password text box, to reset it use the null /// character. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxPasswordCharDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue('\0') // This property is shadowed by MaskedTextBoxDesigner. ] public char PasswordChar { get { // The password char could be the one set in the control or the system password char, // in any case the maskedTextProvider has the correct one. return this.maskedTextProvider.PasswordChar; } set { if( !MaskedTextProvider.IsValidPasswordChar(value) ) // null character accepted (resets value) { // Same message as in SR.MaskedTextProviderInvalidCharError. throw new ArgumentException(SR.GetString(SR.MaskedTextBoxInvalidCharError) ); } if( this.passwordChar != value ) { if( value == this.maskedTextProvider.PromptChar ) { // Prompt and password chars must be different. throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); } this.passwordChar = value; // UseSystemPasswordChar take precedence over PasswordChar...Let's check. if (!this.UseSystemPasswordChar) { this.maskedTextProvider.PasswordChar = value; if( this.flagState[IS_NULL_MASK]) { SetEditControlPasswordChar(value); } else { SetWindowText(); } VerifyImeRestrictedModeChanged(); } } } } ////// Determines if the control is in password protect mode. /// internal override bool PasswordProtect { get { if( this.maskedTextProvider != null ) // could be queried during object construction. { return this.maskedTextProvider.IsPassword; } return base.PasswordProtect; } } ////// Specifies the prompt character to be used in the formatted string for unsupplied characters. /// [ SRCategory(SR.CatAppearance), SRDescription(SR.MaskedTextBoxPromptCharDescr), RefreshProperties(RefreshProperties.Repaint), Localizable(true), DefaultValue('_') ] public char PromptChar { get { return this.maskedTextProvider.PromptChar; } set { if( !MaskedTextProvider.IsValidInputChar(value) ) { // This message is the same as the one in SR.MaskedTextProviderInvalidCharError. throw new ArgumentException(SR.GetString(SR.MaskedTextBoxInvalidCharError) ); } if( this.maskedTextProvider.PromptChar != value ) { // We need to check maskedTextProvider password char in case it is using the system password. if( value == this.passwordChar || value == this.maskedTextProvider.PasswordChar ) { // Prompt and password chars must be different. throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); } // Recreate masked text provider to be consistent with AllowPromptAsInput - current text may have chars with same value as new prompt. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, this.maskedTextProvider.Culture, this.maskedTextProvider.AllowPromptAsInput, value, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); SetMaskedTextProvider( newProvider ); } } } ////// Overwrite base class' property. /// public new bool ReadOnly { get { return base.ReadOnly; } set { if (this.ReadOnly != value) { // if true, this disables IME in the base class. base.ReadOnly = value; if (!this.flagState[IS_NULL_MASK]) { // Prompt will be hidden. SetWindowText(); } } } } ////// Specifies whether to include the mask prompt character when formatting the text in places /// where an edit char has not being assigned. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxRejectInputOnFirstFailureDescr), DefaultValue(false) ] public bool RejectInputOnFirstFailure { get { return this.flagState[REJECT_INPUT_ON_FIRST_FAILURE]; } set { this.flagState[REJECT_INPUT_ON_FIRST_FAILURE] = value; } } ////// Designe time support for resetting the Culture property. /// /* No longer needed since Culture has been removed from the property browser - Left here for documentation. [EditorBrowsable(EditorBrowsableState.Never)] private void ResetCulture() { this.Culture = CultureInfo.CurrentCulture; }*/ ////// Specifies whether to reset and skip the current position if editable, when the input character /// has the same value as the prompt. This property takes precedence over AllowPromptAsInput. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxResetOnPrompt), DefaultValue(true) ] public bool ResetOnPrompt { get { return this.maskedTextProvider.ResetOnPrompt; } set { this.maskedTextProvider.ResetOnPrompt = value; } } ////// Specifies whether to reset and skip the current position if editable, when the input /// is the space character. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxResetOnSpace), DefaultValue(true) ] public bool ResetOnSpace { get { return this.maskedTextProvider.ResetOnSpace; } set { this.maskedTextProvider.ResetOnSpace = value; } } ////// Specifies whether to skip the current position if non-editable and the input character has /// the same value as the literal at that position. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxSkipLiterals), DefaultValue(true) ] public bool SkipLiterals { get { return this.maskedTextProvider.SkipLiterals; } set { this.maskedTextProvider.SkipLiterals = value; } } ////// The currently selected text (if any) in the control. /// public override string SelectedText { get { if( this.flagState[IS_NULL_MASK]) { return base.SelectedText; } return GetSelectedText(); } set { SetSelectedTextInternal(value, true); } } internal override void SetSelectedTextInternal(string value, bool clearUndo) { if (this.flagState[IS_NULL_MASK]) { base.SetSelectedTextInternal(value, true); // Operates as a regular text box base. return; } PasteInt( value ); } ////// Set the composition string as the result string. /// private void ImeComplete() { this.flagState[IME_COMPLETING] = true; ImeNotify(NativeMethods.CPS_COMPLETE); } ////// Notifies the IMM about changes to the status of the IME input context. /// private void ImeNotify(int action) { HandleRef handle = new HandleRef(this, this.Handle); IntPtr inputContext = UnsafeNativeMethods.ImmGetContext(handle); if (inputContext != IntPtr.Zero) { try { UnsafeNativeMethods.ImmNotifyIME(new HandleRef(null, inputContext), NativeMethods.NI_COMPOSITIONSTR, action, 0); } finally { UnsafeNativeMethods.ImmReleaseContext(handle, new HandleRef(null, inputContext)); } } else { Debug.Fail("Could not get IME input context."); } } ////// Sets the underlying edit control's password char to the one obtained from this.PasswordChar. /// This is used when the control is passworded and this.flagState[IS_NULL_MASK]. /// private void SetEditControlPasswordChar( char pwdChar ) { if (this.IsHandleCreated) { // This message does not return a value. SendMessage(NativeMethods.EM_SETPASSWORDCHAR, pwdChar, 0); Invalidate(); } } ////// The value of the Edit control default password char. /// private char SystemPasswordChar { get { if (MaskedTextBox.systemPwdChar == '\0') { // This is the hard way to get the password char - left here for information. // It is picked up from Comctl32.dll. If VisualStyles is enabled it will get the dot char. /* StringBuilder charVal = new StringBuilder(20); // it could be 0x0000000000009999 format. bool foundRsc = false; int IDS_PASSWORDCHAR = 0x1076; // %ntsdx%\shell\comctrl32\v6\rcids.h // defined in en.rc as: IDS_PASSWORDCHAR "9679" // 0x25cf - Black Circle IntSecurity.UnmanagedCode.Assert(); try { // The GetModuleHandle function returns a handle to a mapped module without incrementing its reference count. // @"C:\windows\winsxs\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.10.0_x-ww_f7fb5805\comctl32.dll if VisulaStyles enabled. IntPtr hModule = UnsafeNativeMethods.GetModuleHandle("comctl32.dll"); Debug.Assert(hModule != IntPtr.Zero, String.Format("Could not get a handle to comctl32.dll - Error: 0x{0:X8}", Marshal.GetLastWin32Error())); foundRsc = UnsafeNativeMethods.LoadString(new HandleRef(null, hModule), IDS_PASSWORDCHAR, charVal, charVal.Capacity); } catch( Exception ex ) { if( ClientUtils.IsSecurityOrCriticalException( ex ) ) { throw; } } finally { CodeAccessPermission.RevertAssert(); } MaskedTextBox.systemPwdChar = foundRsc ? (char) int.Parse(charVal.ToString()) : MaskedTextProvider.DefaultPasswordChar; */ // We need to temporarily create an edit control to get the default password character. // We cannot use this control because we would have to reset the native control's password char to use // the defult one so we can get it; this would change the text displayed in the box (even for a short time) // opening a sec hole. TextBox txtBox = new TextBox(); txtBox.UseSystemPasswordChar = true; // this forces the creation of the control handle. MaskedTextBox.systemPwdChar = txtBox.PasswordChar; txtBox.Dispose(); } return MaskedTextBox.systemPwdChar; } } ////// The Text setter validates the input char by char, raising the MaskInputRejected event for invalid chars. /// The Text getter returns the formatted text according to the IncludeLiterals and IncludePrompt properties. /// [ Editor("System.Windows.Forms.Design.MaskedTextBoxTextEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), SRCategory(SR.CatAppearance), RefreshProperties(RefreshProperties.Repaint), Bindable(true), DefaultValue(""), // This property is shadowed by MaskedTextBoxDesigner. Localizable(true) ] public override string Text { get { if( this.flagState[IS_NULL_MASK] || this.flagState[QUERY_BASE_TEXT]) { return base.Text; } return TextOutput; } set { if (this.flagState[IS_NULL_MASK]) { base.Text = value; return; } if (string.IsNullOrEmpty(value)) { // reset the input text. Delete(Keys.Delete, 0, this.maskedTextProvider.Length); } else { if( this.RejectInputOnFirstFailure ) { MaskedTextResultHint hint; string oldText = TextOutput; if (this.maskedTextProvider.Set(value, out this.caretTestPos, out hint)) { //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect ) if( TextOutput != oldText ) { SetText(); } this.SelectionStart = ++this.caretTestPos; } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); } } else { Replace(value, /*startPosition*/ 0, /*selectionLen*/ this.maskedTextProvider.Length); } } } } ////// Returns the length of the displayed text. See VSW#502543. /// [Browsable( false )] public override int TextLength { get { if( this.flagState[IS_NULL_MASK] ) { return base.TextLength; } // In Win9x systems TextBoxBase.TextLength calls Text.Length directly and does not query the window for the actual text length. // If TextMaskFormat is set to a anything different from IncludePromptAndLiterals or HidePromptOnLeave is true the return value // may be incorrect because the Text property value and the display text may be different. We need to handle this here. return GetFormattedDisplayString().Length; } } ////// The formatted text, it is what the Text getter returns when a mask has been applied to the control. /// The text format follows the IncludeLiterals and IncludePrompt properties (See MaskedTextProvider.ToString()). /// private string TextOutput { get { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); return this.maskedTextProvider.ToString(); } } ////// Gets or sets how text is aligned in the control. /// Note: This code is duplicated in TextBox for simplicity. /// [ Localizable(true), SRCategory(SR.CatAppearance), DefaultValue(HorizontalAlignment.Left), SRDescription(SR.TextBoxTextAlignDescr) ] public HorizontalAlignment TextAlign { get { return textAlign; } set { if (textAlign != value) { //verify that 'value' is a valid enum type... //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); } textAlign = value; RecreateHandle(); OnTextAlignChanged(EventArgs.Empty); } } } ////// Event to notify the text alignment has changed. /// [ SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnTextAlignChangedDescr) ] public event EventHandler TextAlignChanged { add { Events.AddHandler(EVENT_TEXTALIGNCHANGED, value); } remove { Events.RemoveHandler(EVENT_TEXTALIGNCHANGED, value); } } ////// Specifies the formatting options for text output (Whether the mask returned from the Text /// property includes Literals and/or prompt characters). /// When prompt characters are excluded, theyare returned as spaces in the string returned. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxTextMaskFormat), RefreshProperties(RefreshProperties.Repaint), DefaultValue(MaskFormat.IncludeLiterals) ] public MaskFormat TextMaskFormat { get { if( this.IncludePrompt ) { if( this.IncludeLiterals ) { return MaskFormat.IncludePromptAndLiterals; } return MaskFormat.IncludePrompt; } if( this.IncludeLiterals ) { return MaskFormat.IncludeLiterals; } return MaskFormat.ExcludePromptAndLiterals; } set { if( this.TextMaskFormat == value ) { return; } //valid values are 0x0 to 0x3 if (!ClientUtils.IsEnumValid(value, (int)value, (int)MaskFormat.ExcludePromptAndLiterals, (int)MaskFormat.IncludePromptAndLiterals)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(MaskFormat)); } // Changing the TextMaskFormat will likely change the 'output' text (Text getter value). Cache old value to // verify it against the new value and raise OnTextChange if needed. string oldText = this.flagState[IS_NULL_MASK] ? null : TextOutput; if( value == MaskFormat.IncludePrompt ) { this.IncludePrompt = true; this.IncludeLiterals = false; } else if( value == MaskFormat.IncludeLiterals ) { this.IncludePrompt = false; this.IncludeLiterals = true; } else // value == MaskFormat.IncludePromptAndLiterals || value == MaskFormat.ExcludePromptAndLiterals { bool include = value == MaskFormat.IncludePromptAndLiterals; this.IncludePrompt = include; this.IncludeLiterals = include; } if( oldText != null && oldText != TextOutput ) { OnTextChanged(EventArgs.Empty); } } } ////// Provides some interesting information for the TextBox control in String form. /// Returns the test string (no password, including literals and prompt). /// public override string ToString() { if( this.flagState[IS_NULL_MASK] ) { return base.ToString(); } // base.ToString will call Text, we want to always display prompt and literals. bool includePrompt = this.IncludePrompt; bool includeLits = this.IncludeLiterals; string str; try { this.IncludePrompt = this.IncludeLiterals = true; str = base.ToString(); } finally { this.IncludePrompt = includePrompt; this.IncludeLiterals = includeLits; } return str; } ////// Event to notify when the validating object completes parsing the formatted text. /// [ SRCategory(SR.CatFocus), SRDescription(SR.MaskedTextBoxTypeValidationCompletedDescr) ] public event TypeValidationEventHandler TypeValidationCompleted { add { Events.AddHandler(EVENT_VALIDATIONCOMPLETED, value); } remove { Events.RemoveHandler(EVENT_VALIDATIONCOMPLETED, value); } } ////// Indicates if the text in the edit control should appear as the default password character. /// This property has precedence over the PasswordChar property. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxUseSystemPasswordCharDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(false) ] public bool UseSystemPasswordChar { get { return this.flagState[USE_SYSTEM_PASSWORD_CHAR]; } set { if (value != this.flagState[USE_SYSTEM_PASSWORD_CHAR]) { if (value) { if( this.SystemPasswordChar == this.PromptChar ) { // Prompt and password chars must be different. throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); } this.maskedTextProvider.PasswordChar = this.SystemPasswordChar; } else { // this.passwordChar could be '\0', in which case we are resetting the display to show the input char. this.maskedTextProvider.PasswordChar = this.passwordChar; } this.flagState[USE_SYSTEM_PASSWORD_CHAR] = value; if( this.flagState[IS_NULL_MASK]) { SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); } else { SetWindowText(); } VerifyImeRestrictedModeChanged(); } } } ////// Type of the object to be used to parse the text when the user leaves the control. /// A ValidatingType object must implement a method with one fo the following signature: /// public static Object Parse(string) /// public static Object Parse(string, IFormatProvider) /// See DateTime.Parse(...) for an example. /// [ Browsable(false), DefaultValue(null) ] public Type ValidatingType { get { return this.validatingType; } set { if( this.validatingType != value ) { this.validatingType = value; } } } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new bool WordWrap { get { return false; } set {} } ////////////// Methods ////// Clears information about the most recent operation from the undo buffer of the control. /// Unsupported property/method. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new void ClearUndo() { } ////// Creates a handle for this control. This method is called by the .NET Framework, this should /// not be called. Inheriting classes should always call base.createHandle when overriding this method. /// Overridden to be able to set the control text with the masked (passworded) value when recreating /// handle, since the underlying native edit control is not aware of it. /// [ EditorBrowsable(EditorBrowsableState.Advanced), UIPermission(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows) ] protected override void CreateHandle() { if (!this.flagState[IS_NULL_MASK] && RecreatingHandle) { // update cached text value in Control. Don't preserve caret, cannot query for selection start at this time. SetWindowText(GetFormattedDisplayString(), false, false); } base.CreateHandle(); } /// ////// Deletes characters from the control's text according to the key pressed (Delete/Backspace). /// Returns true if something gets actually deleted, false otherwise. /// private void Delete(Keys keyCode, int startPosition, int selectionLen) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); Debug.Assert( keyCode == Keys.Delete || keyCode == Keys.Back, "Delete called with keyCode == " + keyCode.ToString() ); Debug.Assert( startPosition >= 0 && ((startPosition + selectionLen) <= this.maskedTextProvider.Length), "Invalid position range." ); // On backspace, moving the start postion back by one has the same effect as delete. If text is selected, there is no // need for moving the position back. this.caretTestPos = startPosition; if( selectionLen == 0 ) { if( keyCode == Keys.Back ) { if( startPosition == 0 ) // At beginning of string, backspace does nothing. { return; } startPosition--; // so it can be treated as delete. } else // (keyCode == Keys.Delete) { if( (startPosition + selectionLen) == this.maskedTextProvider.Length ) // At end of string, delete does nothing. { return; } } } int tempPos; int endPos = selectionLen > 0 ? startPosition + selectionLen - 1 : startPosition; MaskedTextResultHint hint; string oldText = TextOutput; if (this.maskedTextProvider.RemoveAt(startPosition, endPos, out tempPos, out hint)) { //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect) // Text was changed. if( TextOutput != oldText ) { SetText(); this.caretTestPos = startPosition; } else { // If succeeded but nothing removed, the caret should move as follows: // 1. If selectionLen > 0, or on back and hint == SideEffect: move to selectionStart. // 2. If hint == NoEffect, On Delete move to next edit position, if any or not already in one. // On back move to the next edit postion at the left if no more assigned position at the right, // in such case find an assigned position and move one past or one position left if no assigned pos found // (taken care by 'startPosition--' above). // 3. If hint == SideEffect, on Back move like arrow key, (startPosition is already moved, startPosition-- above). if( selectionLen > 0 ) { this.caretTestPos = startPosition; } else { if( hint == MaskedTextResultHint.NoEffect ) // Case 2. { if( keyCode == Keys.Delete ) { this.caretTestPos = this.maskedTextProvider.FindEditPositionFrom(startPosition, forward); } else { if( this.maskedTextProvider.FindAssignedEditPositionFrom( startPosition, forward ) == MaskedTextProvider.InvalidIndex ) { // No assigned position at the right, nothing to shift then move to the next assigned position at the // left (if any). this.caretTestPos = this.maskedTextProvider.FindAssignedEditPositionFrom(startPosition, backward); } else { // there are assigned positions at the right so move to an edit position at the left to get ready for // removing the character on it or just shifting the characters at the right this.caretTestPos = this.maskedTextProvider.FindEditPositionFrom(startPosition, backward); } if( this.caretTestPos != MaskedTextProvider.InvalidIndex ) { this.caretTestPos++; // backspace gets ready to remove one position past the edit position. } } if( this.caretTestPos == MaskedTextProvider.InvalidIndex ) { this.caretTestPos = startPosition; } } else // (hint == MaskedTextProvider.OperationHint.SideEffect) { if( keyCode == Keys.Back ) // Case 3. { this.caretTestPos = startPosition; } } } } } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(tempPos, hint)); } // Reposition caret. Call base.SelectInternal for perf reasons. //this.SelectionLength = 0; //this.SelectionStart = this.caretTestPos; // new caret position. base.SelectInternal( this.caretTestPos, 0, this.maskedTextProvider.Length ); return; } ////// Returns the character nearest to the given point. /// public override char GetCharFromPosition(Point pt) { char ch; this.flagState[QUERY_BASE_TEXT] = true; try { ch = base.GetCharFromPosition(pt); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return ch; } ////// Returns the index of the character nearest to the given point. /// public override int GetCharIndexFromPosition(Point pt) { int index; this.flagState[QUERY_BASE_TEXT] = true; try { index = base.GetCharIndexFromPosition(pt); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return index; } ////// Returns the position of the last input character (or if available, the next edit position). /// This is used by base.AppendText. /// internal override int GetEndPosition() { if( this.flagState[IS_NULL_MASK]) { return base.GetEndPosition(); } int pos = this.maskedTextProvider.FindEditPositionFrom( this.maskedTextProvider.LastAssignedPosition + 1, forward ); if( pos == MaskedTextProvider.InvalidIndex ) { pos = this.maskedTextProvider.LastAssignedPosition + 1; } return pos; } ////// Unsupported method/property. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new int GetFirstCharIndexOfCurrentLine() { return 0; } ////// Unsupported method/property. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new int GetFirstCharIndexFromLine(int lineNumber) { return 0; } ////// Gets the string in the text box following the formatting parameters includePrompt and includeLiterals and /// honoring the PasswordChar property. /// private string GetFormattedDisplayString() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); bool includePrompt; if (this.ReadOnly) // Always hide prompt. { includePrompt = false; } else if (this.DesignMode) // Not RO and at design time, always show prompt. { includePrompt = true; } else // follow HidePromptOnLeave property. { includePrompt = !(this.HidePromptOnLeave && !this.Focused); } return this.maskedTextProvider.ToString(/*ignorePwdChar */ false, includePrompt, /*includeLiterals*/ true, 0, this.maskedTextProvider.Length); } ////// Unsupported method/property. /// virtual method. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public override int GetLineFromCharIndex(int index) { return 0; } ////// Returns the location of the character at the given index. /// public override Point GetPositionFromCharIndex(int index) { Point pos; this.flagState[QUERY_BASE_TEXT] = true; try { pos = base.GetPositionFromCharIndex(index); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return pos; } ////// Need to override this method so when get_Text is called we return the text that is actually /// painted in the control so measuring text works on the actual text and not the formatted one. /// internal override Size GetPreferredSizeCore(Size proposedConstraints) { Size size; this.flagState[QUERY_BASE_TEXT] = true; try { size = base.GetPreferredSizeCore( proposedConstraints ); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return size; } ////// The selected text in the control according to the CutCopyMaskFormat properties (IncludePrompt/IncludeLiterals). /// This is used in Cut/Copy operations (SelectedText). /// The prompt character is always replaced with a blank character. /// private string GetSelectedText() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); int selStart, selLength; base.GetSelectionStartAndLength( out selStart, out selLength ); if( selLength == 0 ) { return string.Empty; } bool includePrompt = (CutCopyMaskFormat & MaskFormat.IncludePrompt ) != 0; bool includeLiterals = (CutCopyMaskFormat & MaskFormat.IncludeLiterals) != 0; return this.maskedTextProvider.ToString( /*ignorePasswordChar*/ true, includePrompt, includeLiterals, selStart, selLength ); } ///protected override void OnBackColorChanged(EventArgs e) { base.OnBackColorChanged(e); // VSWhidbey 465708. Force repainting of the entire window frame if (Application.RenderWithVisualStyles && this.IsHandleCreated && this.BorderStyle == BorderStyle.Fixed3D) { SafeNativeMethods.RedrawWindow(new HandleRef(this, this.Handle), null, NativeMethods.NullHandleRef, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_FRAME); } } /// /// Overridden to update the newly created handle with the settings of the PasswordChar properties /// if no mask has been set. /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); base.SetSelectionOnHandle(); if( this.flagState[IS_NULL_MASK]&& this.maskedTextProvider.IsPassword ) { SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); } } ////// Raises the IsOverwriteModeChanged event. /// [ EditorBrowsable(EditorBrowsableState.Advanced) ] protected virtual void OnIsOverwriteModeChanged(EventArgs e) { EventHandler eh = Events[EVENT_ISOVERWRITEMODECHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ////// Raises the protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if( this.flagState[IS_NULL_MASK]) { // Operates as a regular text box base. return; } Keys keyCode = e.KeyCode; // Special-case Return & Esc since they generate invalid characters we should not process OnKeyPress. if( keyCode == Keys.Return || keyCode == Keys.Escape ) { this.flagState[HANDLE_KEY_PRESS] = false; } // Insert is toggled when not modified with some other key (ctrl, shift...). Note that shift-Insert is // same as paste. if (keyCode == Keys.Insert && e.Modifiers == Keys.None && this.insertMode == InsertKeyMode.Default) { this.flagState[INSERT_TOGGLED] = !this.flagState[INSERT_TOGGLED]; OnIsOverwriteModeChanged(EventArgs.Empty); return; } if (e.Control && char.IsLetter((char)keyCode)) { switch (keyCode) { // Unsupported keys should not be handled to allow generatating the corresponding message // which is handled in the WndProc. //case Keys.Z: // ctrl-z == Undo. //case Keys.Y: // ctrl-y == Redo. // e.Handled = true; // return; // Note: Ctrl-Insert (Copy -Shortcut.CtrlIns) and Shft-Insert (Paste - Shortcut.ShiftIns) are // handled by the base class and behavior depend on ShortcutsEnabled property. // Special cases: usually cases where the native edit control would modify the mask. case Keys.H: // ctrl-h == Backspace == '\b' keyCode = Keys.Back; // handle it below. break; default: // Next OnKeyPress should not be handled to allow Ctrl-event. /// to be processed in the // base class so corresponding messages can be generated (WM_CUT/WM_COPY/WM_PASTE). // Combined characters don't generate OnKeyDown by themselves but they generate OnKeyPress. this.flagState[HANDLE_KEY_PRESS] = false; return; } } if ( keyCode == Keys.Delete || keyCode == Keys.Back ) // Deletion keys. { if (!this.ReadOnly) { int selectionLen; int startPosition; base.GetSelectionStartAndLength( out startPosition, out selectionLen ); switch (e.Modifiers) { case Keys.Shift: if( keyCode == Keys.Delete ) { keyCode = Keys.Back; } goto default; case Keys.Control: if( selectionLen == 0 ) // In other case, the selected text should be deleted. { if( keyCode == Keys.Delete ) // delete to the end of the string. { selectionLen = this.maskedTextProvider.Length - startPosition; } else // ( keyCode == Keys.Back ) // delete to the beginning of the string. { selectionLen = startPosition == this.maskedTextProvider.Length /*at end of text*/ ? startPosition : startPosition + 1; startPosition = 0; } } goto default; default: if( !this.flagState[HANDLE_KEY_PRESS] ) { this.flagState[HANDLE_KEY_PRESS] = true; } break; } // // Handle special case when using Korean IME and ending a composition. // /* This code is no longer needed after fixing bug#517013 - Left here for reference DON'T DELETE. if ((ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) && this.flagState[IME_ENDING_COMPOSITION]) { // Korean IME & Edit control weirdness in action: // When pressing Del/Esc/Enter/BckSpc during a composition, the character is converted and a // corresponding WM_IME_CHAR in placed in the app queue. if(keyCode == Keys.Back && selectionLen == 0) { // After the WM_IME_CHAR message is processed, a WM_CHAR message is queued for the backspace; the // edit control processes the message deleting the previous character. Since we don't pass this // message to the edit control we need to do it ourselves (move position one ahead because it is // set at the composition window which is inserted in the test string. startPosition++; // Note: If the converted character is invalid and there is a character already placed in the previous // position, it will be deleted. THIS IS BY DESIGN: It is exactly like the user performing the convertion // and then pressing backspace, there's no way to differenciate these two scenarios. // See VSWhidbey#200690 for the sequence of messages generated. } } */ Delete(keyCode, startPosition, selectionLen); e.SuppressKeyPress = true; } } } /// /// Raises the protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); if( this.flagState[IS_NULL_MASK]) { // Operates as a regular text box base. return; } // This key may be a combined key involving a letter, like Ctrl-A; let the native control handle it. if( !this.flagState[HANDLE_KEY_PRESS] ) { this.flagState[HANDLE_KEY_PRESS] = true; // When the combined key involves a letter, the final character is not a letter. There are some // Ctrl combined keys that generate a letter and can be confusing; we do not mean to pass those // characters to the underlying Edit control. These combinations are: Ctrl-F<#> and Ctrl-Atl-event. /// if (!char.IsLetter(e.KeyChar)) { return; } } if( !this.ReadOnly) { // At this point the character needs to be processed ... MaskedTextResultHint hint; int selectionStart; int selectionLen; base.GetSelectionStartAndLength( out selectionStart, out selectionLen ); string oldText = TextOutput; if (PlaceChar(e.KeyChar, selectionStart, selectionLen, this.IsOverwriteMode, out hint)) { //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect ) if( TextOutput != oldText ) { SetText(); // Now set the text in the display. } this.SelectionStart = ++this.caretTestPos; // caretTestPos is updated in PlaceChar. if (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) { // Korean IMEs complete composition when a character has been fully converted, so the composition string // is only one-character long; once composed we block the IME if there ins't more room in the test string. int editPos = this.maskedTextProvider.FindUnassignedEditPositionFrom(this.caretTestPos, forward); if (editPos == MaskedTextProvider.InvalidIndex) { ImeComplete(); // Force completion of compostion. } } } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); // caretTestPos is updated in PlaceChar. } if( selectionLen > 0 ) { this.SelectionLength = 0; } e.Handled = true; } } /// /// protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); // KeyUp is the last message to be processed so it is the best place to reset these flags. if (this.flagState[IME_COMPLETING]) { this.flagState[IME_COMPLETING] = false; } if( this.flagState[IME_ENDING_COMPOSITION] ) { this.flagState[IME_ENDING_COMPOSITION] = false; } } ///Raises the ///event. /// Raises the MaskChanged event. /// [ EditorBrowsable(EditorBrowsableState.Advanced) ] protected virtual void OnMaskChanged(EventArgs e) { EventHandler eh = Events[EVENT_MASKCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ////// Raises the MaskInputRejected event. /// private void OnMaskInputRejected(MaskInputRejectedEventArgs e) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if (this.BeepOnError) { System.Media.SoundPlayer sp = new System.Media.SoundPlayer(); sp.Play(); } MaskInputRejectedEventHandler eh = Events[EVENT_MASKINPUTREJECTED] as MaskInputRejectedEventHandler; if (eh != null) { eh(this, e); } } ////// Unsupported method/property. /// virtual method. /// [ EditorBrowsable(EditorBrowsableState.Never) ] protected override void OnMultilineChanged(EventArgs e) { } ////// Raises the TextAlignChanged event. /// protected virtual void OnTextAlignChanged(EventArgs e) { EventHandler eh = Events[EVENT_TEXTALIGNCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ////// Raises the TypeValidationCompleted event. /// private void OnTypeValidationCompleted(TypeValidationEventArgs e) { TypeValidationEventHandler eh = Events[EVENT_VALIDATIONCOMPLETED] as TypeValidationEventHandler; if (eh != null) { eh(this, e); } } ////// Raises the System.Windows.Forms.Control.Validating event. /// Overridden here to be able to control the order validating events are /// raised [TypeValidationCompleted - Validating - Validated - Leave - KillFocus] /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected override void OnValidating(CancelEventArgs e) { // Note: It seems impractical to perform type validation here if the control is read only but we need // to be consistent with other TextBoxBase controls which don't check for RO; and we don't want // to fix them to avoid introducing breaking changes. PerformTypeValidation(e); base.OnValidating(e); } ////// Raises the TextChanged event and related Input/Output text events when mask is null. /// Overriden here to be able to control order of text changed events. /// protected override void OnTextChanged(EventArgs e) { // A text changed event handler will most likely query for the Text value, we need to return the // formatted one. bool queryBaseText = this.flagState[QUERY_BASE_TEXT]; this.flagState[QUERY_BASE_TEXT] = false; try { base.OnTextChanged(e); } finally { this.flagState[QUERY_BASE_TEXT] = queryBaseText; } } ////// Replaces the current selection in the text box specified by the startPosition and selectionLen parameters /// with the contents of the supplied string. /// private void Replace(string text, int startPosition, int selectionLen) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); Debug.Assert(text != null, "text is null."); // Clone the MaskedTextProvider so text properties are not modified until the paste operation is // completed. This is needed in case one of these properties is retreived in a MaskedInputRejected // event handler (clipboard text is attempted to be set into the input text char by char). MaskedTextProvider clonedProvider = (MaskedTextProvider) this.maskedTextProvider.Clone(); // Cache the current caret position so we restore it in case the text does not change. VSW#498875. int currentCaretPos = this.caretTestPos; // First replace characters in the selection (if any and if any edit positions) until completed, or the test position falls // outside the selection range, or there's no more room in the test string for editable characters. // Then insert any remaining characters from the input. MaskedTextResultHint hint = MaskedTextResultHint.NoEffect; int endPos = startPosition + selectionLen - 1; if( this.RejectInputOnFirstFailure ) { bool succeeded; succeeded = (startPosition > endPos) ? clonedProvider.InsertAt(text, startPosition, out this.caretTestPos, out hint ) : clonedProvider.Replace(text, startPosition, endPos, out this.caretTestPos, out hint); if( !succeeded ) { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); } } else { // temp hint used to preserve the 'primary' operation hint (no side effects). MaskedTextResultHint tempHint = hint; int testPos; foreach (char ch in text) { if( !this.maskedTextProvider.VerifyEscapeChar( ch, startPosition )) // char won't be escaped, find and edit position for it. { // Observe that we look for a position w/o respecting the selection length, because the input text could be larger than // the number of edit positions in the selection. testPos = clonedProvider.FindEditPositionFrom(startPosition, forward); if( testPos == MaskedTextProvider.InvalidIndex ) { // this will continue to execute (fail) until the end of the text so we fire the event for each remaining char. OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); continue; } startPosition = testPos; } int length = endPos >= startPosition ? 1 : 0; // if length > 0 we are (re)placing the input char in the current startPosition, otherwise we are inserting the input. bool replace = length > 0; if (PlaceChar(clonedProvider, ch, startPosition, length, replace, out tempHint)) { // caretTestPos is updated in PlaceChar call. startPosition = this.caretTestPos + 1; // place char will insert or replace a single character so the hint must be success, and that will be the final operation // result hint. if (tempHint == MaskedTextResultHint.Success && hint != tempHint) { hint = tempHint; } } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, tempHint)); } } if (selectionLen > 0) { // At this point we have processed all characters from the input text (if any) but still need to // remove remaining characters from the selected text (if editable and valid chars). if (startPosition <= endPos) { if (!clonedProvider.RemoveAt(startPosition, endPos, out this.caretTestPos, out tempHint)) { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, tempHint)); } // If 'replace' is not actually performed (maybe the input is empty which means 'remove', hint will be whatever // the 'remove' operation result hint is. if (hint == MaskedTextResultHint.NoEffect && hint != tempHint) { hint = tempHint; } } } } bool updateText = TextOutput != clonedProvider.ToString(); // Always set the mtp, the formatted text could be the same but the assigned positions may be different. this.maskedTextProvider = clonedProvider; // Update text if needed. if( updateText ) { SetText(); // Update caret position. this.caretTestPos = startPosition; base.SelectInternal( this.caretTestPos, 0, this.maskedTextProvider.Length ); } else { this.caretTestPos = currentCaretPos; } return; } ////// Pastes specified text over the currently selected text (if any) shifting upper characters if /// input is longer than selected text, and/or removing remaining characters from the selection if /// input contains less characters. /// private void PasteInt( string text ) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); int selStart, selLength; base.GetSelectionStartAndLength(out selStart, out selLength); if( string.IsNullOrEmpty(text) ) { Delete( Keys.Delete, selStart, selLength ); } else { Replace(text, selStart, selLength); } } ////// Performs validation of the input string using the provided ValidatingType object (if any). /// Returns an object created from the formatted text. /// If the CancelEventArgs param is not null, it is assumed the control is leaving focus and /// the validation event chain is being executed (TypeValidationCompleted - Validating - Validated...); /// the value of the CancelEventArgs.Cancel property is the same as the TypeValidationEventArgs.Cancel /// on output (Cancel provides proper handling of focus shifting at the Control class level). /// Note: The text being validated does not include prompt chars. /// private object PerformTypeValidation(CancelEventArgs e) { object parseRetVal = null; if (this.validatingType != null) { string message = null; if (!this.flagState[IS_NULL_MASK]&& this.maskedTextProvider.MaskCompleted == false) { message = SR.GetString(SR.MaskedTextBoxIncompleteMsg); } else { string textValue; if( !this.flagState[IS_NULL_MASK]) // replace prompt with space. { textValue = this.maskedTextProvider.ToString(/*includePrompt*/ false, this.IncludeLiterals); } else { textValue = base.Text; } try { parseRetVal = Formatter.ParseObject( textValue, // data this.validatingType, // targetType typeof(string), // sourceType null, // targetConverter null, // sourceConverter this.formatProvider, // formatInfo null, // nullValue Formatter.GetDefaultDataSourceNullValue(this.validatingType)); // dataSourceNullValue } catch (Exception exception) { if (ClientUtils.IsSecurityOrCriticalException(exception)) { throw; } if (exception.InnerException != null) // Outer exception is a generic TargetInvocationException. { exception = exception.InnerException; } message = exception.GetType().ToString() + ": " + exception.Message; } } bool isValidInput = false; if (message == null) { isValidInput = true; message = SR.GetString(SR.MaskedTextBoxTypeValidationSucceeded); } TypeValidationEventArgs tve = new TypeValidationEventArgs(this.validatingType, isValidInput, parseRetVal, message); OnTypeValidationCompleted(tve); if( e != null ) { e.Cancel = tve.Cancel; } } return parseRetVal; } ////// Insert or replaces the specified character into the control's text and updates the caret position. /// If overwrite is true, it replaces the character at the selection start position. /// private bool PlaceChar(char ch, int startPosition, int length, bool overwrite, out MaskedTextResultHint hint) { return PlaceChar(this.maskedTextProvider, ch, startPosition, length, overwrite, out hint ); } ////// Override version to be able to perform the operation on a cloned provider. /// private bool PlaceChar(MaskedTextProvider provider, char ch, int startPosition, int length, bool overwrite, out MaskedTextResultHint hint) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); this.caretTestPos = startPosition; if (startPosition < this.maskedTextProvider.Length) { if (length > 0) // Replacing selection with input char. { int endPos = startPosition + length - 1; return provider.Replace(ch, startPosition, endPos, out this.caretTestPos, out hint); } else { if (overwrite) { // overwrite character at next edit position from startPosition (inclusive). return provider.Replace(ch, startPosition, out this.caretTestPos, out hint); } else // insert. { return provider.InsertAt(ch, startPosition, out this.caretTestPos, out hint); } } } hint = MaskedTextResultHint.UnavailableEditPosition; return false; } ////// [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { // // The base class should be called first because it implements ShortcutsEnabled, // which takes precedence over Ctrl+A // bool msgProcessed = base.ProcessCmdKey(ref msg, keyData); if (!msgProcessed) { if ((int)keyData == (int)Shortcut.CtrlA) { base.SelectAll(); msgProcessed = true; // This prevents generating a WM_CHAR for 'A'. } } return msgProcessed; } ///: /// Processes a command key. This method is called during message /// pre-processing to handle command keys. Command keys are keys that always /// take precedence over regular input keys. Examples of command keys /// include accelerators and menu shortcuts. The method must return true to /// indicate that it has processed the command key, or false to indicate /// that the key is not a command key. /// /// processCmdKey() first checks if the control has a context menu, and if /// so calls the menu's processCmdKey() to check for menu shortcuts. If the /// command key isn't a menu shortcut, and if the control has a parent, the /// key is passed to the parent's processCmdKey() method. The net effect is /// that command keys are "bubbled" up the control hierarchy. /// /// When overriding processCmdKey(), a control should return true to /// indicate that it has processed the key. For keys that aren't processed by /// the control, the result of "base.processCmdKey()" should be returned. /// /// Controls will seldom, if ever, need to override this method. /// /// /// Implements the handling of Ctrl+A (select all). Note: Code copied from TextBox. ////// We need to override this method so we can handle input language changes properly. Control /// doesn't handle the WM_CHAR messages generated after WM_IME_CHAR messages, it passes them /// to DefWndProc (the characters would be displayed in the text box always). /// /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected internal override bool ProcessKeyMessage(ref Message m) { // call base's method so the WM_CHAR and other messages are processed; this gives Control the // chance to flush all pending WM_CHAR processing after WM_IME_CHAR messages are generated. bool msgProcessed = base.ProcessKeyMessage(ref m); if (this.flagState[IS_NULL_MASK]) { return msgProcessed; // Operates as a regular text box base. } // If this WM_CHAR message is sent after WM_IME_CHAR, we ignore it since we already processed // the corresponding WM_IME_CHAR message. if( m.Msg == NativeMethods.WM_CHAR && base.ImeWmCharsToIgnore > 0 ) { return true; // meaning, we handled the message so it is not passed to the default WndProc. } return msgProcessed; } ////// Designe time support for resetting Culture property.. /// private void ResetCulture() { this.Culture = CultureInfo.CurrentCulture; } ////// Unsupported method/property. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new void ScrollToCaret() { } ////// Sets the underlying MaskedTextProvider object. Used when the control is initialized /// and one of its properties, backed up by the MaskedTextProvider, changes; this requires /// recreating the provider because it is immutable. /// private void SetMaskedTextProvider( MaskedTextProvider newProvider ) { SetMaskedTextProvider( newProvider, null); } ////// Overload to allow for passing the text when the mask is being changed from null, /// in this case the maskedTextProvider holds backend info only (not the text). /// private void SetMaskedTextProvider( MaskedTextProvider newProvider, string textOnInitializingMask ) { Debug.Assert( newProvider != null, "Initializing from a null MaskProvider ref." ); // Set R/W properties. newProvider.IncludePrompt = this.maskedTextProvider.IncludePrompt; newProvider.IncludeLiterals = this.maskedTextProvider.IncludeLiterals; newProvider.SkipLiterals = this.maskedTextProvider.SkipLiterals; newProvider.ResetOnPrompt = this.maskedTextProvider.ResetOnPrompt; newProvider.ResetOnSpace = this.maskedTextProvider.ResetOnSpace; // If mask not initialized and not initializing it, the new provider is just a property backend. // Change won't have any effect in text. if( this.flagState[IS_NULL_MASK] && textOnInitializingMask == null) { this.maskedTextProvider = newProvider; return; } int testPos = 0; bool raiseOnMaskInputRejected = false; // Raise if new provider rejects old text. MaskedTextResultHint hint = MaskedTextResultHint.NoEffect; MaskedTextProvider oldProvider = this.maskedTextProvider; // Attempt to add previous text. // If the mask is the same, we need to preserve the caret and character positions if the text is added successfully. bool preserveCharPos = oldProvider.Mask == newProvider.Mask; // Cache text output text before setting the new provider to determine whether we need to raise the TextChanged event. string oldText; // NOTE: Whenever changing the MTP, the text is lost if any character in the old text violates the new provider's mask. if( textOnInitializingMask != null ) // Changing Mask (from null), which is the only RO property that requires passing text. { oldText = textOnInitializingMask; raiseOnMaskInputRejected = !newProvider.Set( textOnInitializingMask, out testPos, out hint ); } else { oldText = TextOutput; // We need to attempt to set the input characters one by one in the edit positions so they are not // escaped. int assignedCount = oldProvider.AssignedEditPositionCount; int srcPos = 0; int dstPos = 0; while( assignedCount > 0 ) { srcPos = oldProvider.FindAssignedEditPositionFrom( srcPos, forward ); Debug.Assert( srcPos != MaskedTextProvider.InvalidIndex, "InvalidIndex unexpected at this time." ); if (preserveCharPos) { dstPos = srcPos; } else { dstPos = newProvider.FindEditPositionFrom(dstPos, forward); if (dstPos == MaskedTextProvider.InvalidIndex) { newProvider.Clear(); testPos = newProvider.Length; hint = MaskedTextResultHint.UnavailableEditPosition; break; } } if( !newProvider.Replace( oldProvider[srcPos], dstPos, out testPos, out hint )) { preserveCharPos = false; newProvider.Clear(); break; } srcPos++; dstPos++; assignedCount--; } raiseOnMaskInputRejected = !MaskedTextProvider.GetOperationResultFromHint(hint); } // Set provider. this.maskedTextProvider = newProvider; if( this.flagState[IS_NULL_MASK] ) { this.flagState[IS_NULL_MASK] = false; } // Raising events need to be done only after the new provider has been set so the MTB is in a state where properties // can be queried from event handlers safely. if( raiseOnMaskInputRejected ) { OnMaskInputRejected(new MaskInputRejectedEventArgs(testPos, hint)); } if( newProvider.IsPassword ) { // Reset native edit control so the MaskedTextBox will take control over the characters that // need to be replaced with the password char (the input text characters). // MTB takes over. SetEditControlPasswordChar('\0'); } EventArgs e = EventArgs.Empty; if (textOnInitializingMask != null /*changing mask from null*/ || oldProvider.Mask != newProvider.Mask) { OnMaskChanged(e); } SetWindowText(GetFormattedDisplayString(), oldText != TextOutput, preserveCharPos); } ////// Sets the control's text to the formatted text obtained from the underlying MaskedTextProvider. /// TextChanged is raised always, this assumes the display or the output text changed. /// The caret position is lost (unless cached somewhere else like when lossing the focus). /// This is the common way of changing the text in the control. /// private void SetText() { SetWindowText(GetFormattedDisplayString(), true, false); } ////// Sets the control's text to the formatted text obtained from the underlying MaskedTextProvider. /// TextChanged is not raised. [PasswordChar] /// The caret position is preserved. /// private void SetWindowText() { SetWindowText(GetFormattedDisplayString(), false, true); } ////// Sets the text directly in the underlying edit control to the value specified. /// The 'raiseTextChangedEvent' param determines whether TextChanged event is raised or not. /// The 'preserveCaret' param determines whether an attempt to preserve the caret position should be made or not /// after the call to SetWindowText (WindowText) is performed. /// private void SetWindowText(string text, bool raiseTextChangedEvent, bool preserveCaret) { this.flagState[QUERY_BASE_TEXT] = true; try { if( preserveCaret ) { this.caretTestPos = this.SelectionStart; } WindowText = text; // this calls Win32::SetWindowText directly, no OnTextChanged raised. if( raiseTextChangedEvent ) { OnTextChanged(EventArgs.Empty); } if( preserveCaret ) { this.SelectionStart = this.caretTestPos; } } finally { this.flagState[QUERY_BASE_TEXT] = false; } } ////// Designe time support for checking if Culture value in the designer should be serialized. /// private bool ShouldSerializeCulture() { return !CultureInfo.CurrentCulture.Equals(this.Culture); } ////// Undoes the last edit operation in the text box. /// Unsupported property/method. /// WndProc ignores EM_UNDO. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new void Undo() { } ////// Forces type validation. Returns the validated text value. /// public object ValidateText() { return PerformTypeValidation(null); } ////// Deletes all input characters in the current selection. /// private bool WmClear() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if( !this.ReadOnly ) { int selStart, selLength; base.GetSelectionStartAndLength( out selStart, out selLength ); Delete(Keys.Delete, selStart, selLength); return true; } return false; } ////// Copies current selection text to the clipboard, formatted according to the IncludeLiterals properties but /// ignoring the prompt character. /// Returns true if the operation succeeded, false otherwise. /// private bool WmCopy() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if (this.maskedTextProvider.IsPassword) // cannot copy password to clipboard. { return false; } string text = GetSelectedText(); try { // SECREVIEW : Copy needs to work even if the OwnClipboard permission isn't available. We need to assert here. // This assert is ok, observe that if the control is in password mode it won't reach this point, // see code above. // Be careful if code is added after the security assert, you may need to put it in a try-finally // block to revert the security assert. IntSecurity.ClipboardWrite.Assert(); if (text.Length == 0) { Clipboard.Clear(); } else { Clipboard.SetText(text); } } catch (Exception ex) { // Note: Sometimes the above operation throws but it successfully sets the // data in the clipboard. This usually happens when the Application's Main // is not attributed with [STAThread]. if (ClientUtils.IsSecurityOrCriticalException(ex)) { throw; } } return true; } ////// Processes the WM_IME_COMPOSITION message when using Korean IME. /// Korean IME uses the control's caret as the composition string (it processes only one character at a time), /// we need to have special message handling for it. /// Returns true if the message is handled, false otherwise. /// private bool WmImeComposition(ref Message m) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); #if DEBUG if (this.ReadOnly || this.maskedTextProvider.IsPassword) { // This should have been already handled by the ReadOnly, PasswordChar and ImeMode properties. Debug.Assert(this.ImeMode == ImeMode.Disable, "IME enabled when in RO or Pwd mode."); } #endif // Non-Korean IMEs complete compositon when all characters in the string has been composed (when user hits enter); // Currently, we don't support checking the composition string characters because it would require similar logic // as the MaskedTextBox itself. if (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) { byte imeConvertionType = imeConvertionNone; // Check if there's an update to the compositon string: if ((m.LParam.ToInt32() & NativeMethods.GCS_COMPSTR) != 0) { // The character in the composition has been updated but not yet converted. imeConvertionType = imeConvertionUpdate; } else if ((m.LParam.ToInt32() & NativeMethods.GCS_RESULTSTR) != 0) { // The character(s) in the composition has been fully converted. imeConvertionType = imeConvertionCompleted; } // Process any update in the composition string. if (imeConvertionType != imeConvertionNone) { if (this.flagState[IME_ENDING_COMPOSITION]) { // If IME is completing the convertion, we don't want to process further characters. return this.flagState[IME_COMPLETING]; } } } return false; //message not handled. } ////// Processes the WM_IME_STARTCOMPOSITION message. /// Returns true if the message is handled, false otherwise. /// private bool WmImeStartComposition() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); // Position the composition window in a valid place. int startPosition, selectionLen; base.GetSelectionStartAndLength( out startPosition, out selectionLen ); int startEditPos = this.maskedTextProvider.FindEditPositionFrom( startPosition, forward ); if( startEditPos != MaskedTextProvider.InvalidIndex ) { if (selectionLen > 0 && (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable)) { // Korean IME: We need to delete the selected text and reposition the caret so the IME processes one // character only, otherwise it would overwrite the selection with the caret (composition string), // deleting a portion of the mask. int endEditPos = this.maskedTextProvider.FindEditPositionFrom(startPosition + selectionLen - 1, backward); if (endEditPos >= startEditPos) { selectionLen = endEditPos - startEditPos + 1; Delete(Keys.Delete, startEditPos, selectionLen); } else { ImeComplete(); OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); return true; } } // update caret position. if( startPosition != startEditPos ) { this.caretTestPos = startEditPos; this.SelectionStart = this.caretTestPos; } this.SelectionLength = 0; } else { ImeComplete(); OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); return true; } return false; } ////// Processes the WM_PASTE message. Copies the text from the clipboard, if is valid, /// formatted according to the mask applied to this control. /// Returns true if the operation succeeded, false otherwise. /// private void WmPaste() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if( this.ReadOnly ) { return; } // Get the text from the clipboard. string text; try { IntSecurity.ClipboardRead.Assert(); text = Clipboard.GetText(); } catch (Exception ex) { if (ClientUtils.IsSecurityOrCriticalException(ex)) { throw; } Debug.Fail(ex.ToString()); return; } PasteInt( text ); } private void WmPrint(ref Message m) { base.WndProc(ref m); if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { IntSecurity.UnmanagedCode.Assert(); try { using (Graphics g = Graphics.FromHdc(m.WParam)) { Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); rect.Inflate(-1, -1); g.DrawRectangle(SystemPens.Window, rect); } } finally { CodeAccessPermission.RevertAssert(); } } } ////// We need to override the WndProc method to have full control over what characters can be /// displayed in the text box; particularly, we have special handling when IME is turned on. /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { // Handle messages for special cases (unsupported operations or cases where mask doesn not matter). switch (m.Msg) { case NativeMethods.WM_PRINT: WmPrint(ref m); return; case NativeMethods.WM_CONTEXTMENU: case NativeMethods.EM_CANUNDO: base.ClearUndo(); // resets undo buffer. base.WndProc(ref m); return; case NativeMethods.EM_SCROLLCARET: // No scroll for single-line control. case NativeMethods.EM_LIMITTEXT: // Max/Min text is defined by the mask. case NativeMethods.EM_UNDO: case NativeMethods.WM_UNDO: return; default: break; // continue. } if( this.flagState[IS_NULL_MASK]) { base.WndProc(ref m); // Operates as a regular text box base. return; } switch (m.Msg) { case NativeMethods.WM_IME_STARTCOMPOSITION: if( WmImeStartComposition() ) { break; } goto default; case NativeMethods.WM_IME_ENDCOMPOSITION: this.flagState[IME_ENDING_COMPOSITION] = true; goto default; case NativeMethods.WM_IME_COMPOSITION: if( WmImeComposition( ref m ) ) { break; } goto default; case NativeMethods.WM_CUT: if (!this.ReadOnly && WmCopy()) { WmClear(); } break; case NativeMethods.WM_COPY: WmCopy(); break; case NativeMethods.WM_PASTE: WmPaste(); break; case NativeMethods.WM_CLEAR: WmClear(); break; case NativeMethods.WM_KILLFOCUS: base.WndProc(ref m); WmKillFocus(); break; case NativeMethods.WM_SETFOCUS: WmSetFocus(); base.WndProc(ref m); break; default: base.WndProc(ref m); break; } } ////// Processes the WM_KILLFOCUS message. Updates control's text replacing promp chars with space. /// private void WmKillFocus() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); base.GetSelectionStartAndLength( out this.caretTestPos, out this.lastSelLength ); if (this.HidePromptOnLeave && !this.MaskFull) { SetWindowText(); // Update text w/ no prompt. // We need to update selection info in case the control is queried for it while it doesn't have the focus. base.SelectInternal( this.caretTestPos, this.lastSelLength, this.maskedTextProvider.Length ); } } ////// Processes the WM_SETFOCUS message. Updates control's text with formatted text according to /// the include prompt property. /// private void WmSetFocus() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if (this.HidePromptOnLeave && !this.MaskFull) // Prompt will show up. { SetWindowText(); } // Restore previous selection. Do this always (as opposed to within the condition above as in WmKillFocus) // because HidePromptOnLeave could have changed while the control did not have the focus. base.SelectInternal( this.caretTestPos, this.lastSelLength, this.maskedTextProvider.Length ); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using System; using System.Text; using System.ComponentModel; using System.Runtime.InteropServices; using System.Globalization; using System.Security; using System.Security.Permissions; using System.Diagnostics; using System.Collections; using System.Collections.Specialized; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms.Layout; using System.Windows.Forms.VisualStyles; ////// MaskedTextBox control definition class. /// Uses the services from the System.ComponentModel.MaskedTextBoxProvider class. /// See spec at http://dotnetclient/whidbey/Specs/MaskEdit.doc /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), DefaultEvent("MaskInputRejected"), DefaultBindingProperty("Text"), DefaultProperty("Mask"), Designer("System.Windows.Forms.Design.MaskedTextBoxDesigner, " + AssemblyRef.SystemDesign), SRDescription(SR.DescriptionMaskedTextBox) ] public class MaskedTextBox : TextBoxBase { // Consider: The MaskedTextBox control, when initialized with a non-null/empty mask, processes all // WM_CHAR messages and always sets the text using the SetWindowText Windows function in the furthest base // class. This means that the underlying Edit control won't enable Undo operations and the context // menu behavior will be a bit different (for instance Copy option is enabled when PasswordChar is set). // To provide Undo functionality and make the context menu behave like the Edit control, we would have // to implement our own. See http://msdn.microsoft.com/msdnmag/issues/1100/c/default.aspx for more info // about how to do this. See postponed bug VSWhidbey#218402. private const bool forward = true; private const bool backward = false; private const string nullMask = "<>"; // any char/str is OK here. private static readonly object EVENT_MASKINPUTREJECTED = new object(); private static readonly object EVENT_VALIDATIONCOMPLETED = new object(); private static readonly object EVENT_TEXTALIGNCHANGED = new object(); private static readonly object EVENT_ISOVERWRITEMODECHANGED = new object(); private static readonly object EVENT_MASKCHANGED = new object(); // The native edit control's default password char (per thread). See corresponding property for more info. private static char systemPwdChar; // Values to track changes in IME composition string (if any). Having const variables is a bit more efficient // than having an enum (which creates a class). private const byte imeConvertionNone = 0; // no convertion has been performed in the composition string. private const byte imeConvertionUpdate = 1; // the char being composed has been updated but not coverted yet. private const byte imeConvertionCompleted = 2; // the char being composed has been fully converted. ///////// Instance fields // Used for keeping selection when prompt is hidden on leave (text changes). private int lastSelLength; // Used for caret positioning. private int caretTestPos; // Bit mask - Determines when the Korean IME composition string is completed so converted character can be processed. private static int IME_ENDING_COMPOSITION = BitVector32.CreateMask(); // Bit mask - Determines when the Korean IME is completing a composition, used when forcing convertion. private static int IME_COMPLETING = BitVector32.CreateMask(IME_ENDING_COMPOSITION); // Used for handling characters that have a modifier (Ctrl-A, Shift-Del...). private static int HANDLE_KEY_PRESS = BitVector32.CreateMask(IME_COMPLETING); // Bit mask - Used to simulate a null mask. Needed since a MaskedTextProvider object cannot be // initialized with a null mask but we need one even in this case as a backend for // default properties. This is to support creating a MaskedTextBox with the default // constructor, specially at design time. private static int IS_NULL_MASK = BitVector32.CreateMask(HANDLE_KEY_PRESS); // Bit mask - Used in conjuction with get_Text to return the text that is actually set in the native // control. This is required to be able to measure text correctly (GetPreferredSize) and // to compare against during set_Text (to bail if the same and not to raise TextChanged event). private static int QUERY_BASE_TEXT = BitVector32.CreateMask(IS_NULL_MASK); // If true, the input text is rejected whenever a character does not comply with the mask; a MaskInputRejected // event is fired for the failing character. // If false, characters in the input string are processed one by one accepting the ones that comply // with the mask and raising the MaskInputRejected event for the rejected ones. private static int REJECT_INPUT_ON_FIRST_FAILURE = BitVector32.CreateMask( QUERY_BASE_TEXT ); // Bit masks for boolean properties. private static int HIDE_PROMPT_ON_LEAVE = BitVector32.CreateMask(REJECT_INPUT_ON_FIRST_FAILURE); private static int BEEP_ON_ERROR = BitVector32.CreateMask(HIDE_PROMPT_ON_LEAVE); private static int USE_SYSTEM_PASSWORD_CHAR = BitVector32.CreateMask(BEEP_ON_ERROR); private static int INSERT_TOGGLED = BitVector32.CreateMask(USE_SYSTEM_PASSWORD_CHAR); private static int CUTCOPYINCLUDEPROMPT = BitVector32.CreateMask(INSERT_TOGGLED); private static int CUTCOPYINCLUDELITERALS = BitVector32.CreateMask(CUTCOPYINCLUDEPROMPT); ///////// Properties backend fields. See corresponding property comments for more info. private char passwordChar; // control's pwd char, it could be different from the one displayed if using system password. private Type validatingType; private IFormatProvider formatProvider; private MaskedTextProvider maskedTextProvider; private InsertKeyMode insertMode; private HorizontalAlignment textAlign; // Bit vector to represent bool variables. private BitVector32 flagState; ////// Constructs the MaskedTextBox with the specified MaskedTextProvider object. /// public MaskedTextBox() { MaskedTextProvider maskedTextProvider = new MaskedTextProvider(nullMask, CultureInfo.CurrentCulture); this.flagState[IS_NULL_MASK] = true; Initialize(maskedTextProvider); } ////// Constructs the MaskedTextBox with the specified MaskedTextProvider object. /// public MaskedTextBox(string mask) { if (mask == null) { throw new ArgumentNullException(); } MaskedTextProvider maskedTextProvider = new MaskedTextProvider(mask, CultureInfo.CurrentCulture); this.flagState[IS_NULL_MASK] = false; Initialize(maskedTextProvider); } ////// Constructs the MaskedTextBox with the specified MaskedTextProvider object. /// public MaskedTextBox(MaskedTextProvider maskedTextProvider) { if (maskedTextProvider == null) { throw new ArgumentNullException(); } this.flagState[IS_NULL_MASK] = false; Initialize(maskedTextProvider); } ////// Initializes the object with the specified MaskedTextProvider object and default /// property values. /// private void Initialize(MaskedTextProvider maskedTextProvider) { Debug.Assert(maskedTextProvider != null, "Initializing from a null MaskProvider ref."); this.maskedTextProvider = maskedTextProvider; // set the initial display text. if (!this.flagState[IS_NULL_MASK]) { SetWindowText(); } // set default values. this.passwordChar = this.maskedTextProvider.PasswordChar; this.insertMode = InsertKeyMode.Default; this.flagState[HIDE_PROMPT_ON_LEAVE ] = false; this.flagState[BEEP_ON_ERROR ] = false; this.flagState[USE_SYSTEM_PASSWORD_CHAR ] = false; this.flagState[REJECT_INPUT_ON_FIRST_FAILURE] = false; // CutCopyMaskFormat - set same defaults as TextMaskFormat (IncludePromptAndLiterals). // It is a lot easier to handle this flags individually since that's the way the MaskedTextProvider does it. this.flagState[CUTCOPYINCLUDEPROMPT ] = this.maskedTextProvider.IncludePrompt; this.flagState[CUTCOPYINCLUDELITERALS ] = this.maskedTextProvider.IncludeLiterals; // fields for internal use. this.flagState[HANDLE_KEY_PRESS] = true; this.caretTestPos = 0; } /////////////////// Properties /// ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new bool AcceptsTab { get { return false; } set {} } ////// Specifies whether the prompt character should be treated as a valid input character or not. /// The setter resets the underlying MaskedTextProvider object and attempts /// to add the existing input text (if any) using the new mask, failure is ignored. /// This property has no particular effect if no mask has been set. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxAllowPromptAsInputDescr), DefaultValue(true) ] public bool AllowPromptAsInput { get { return this.maskedTextProvider.AllowPromptAsInput; } set { if( value != this.maskedTextProvider.AllowPromptAsInput ) { // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, this.maskedTextProvider.Culture, value, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); SetMaskedTextProvider( newProvider ); } } } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new event EventHandler AcceptsTabChanged { add { } remove { } } ////// Specifies whether only ASCII characters are accepted as valid input. /// This property has no particular effect if no mask has been set. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxAsciiOnlyDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(false) ] public bool AsciiOnly { get { return this.maskedTextProvider.AsciiOnly; } set { if( value != this.maskedTextProvider.AsciiOnly ) { // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, this.maskedTextProvider.Culture, this.maskedTextProvider.AllowPromptAsInput, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, value ); SetMaskedTextProvider( newProvider ); } } } ////// Specifies whether to play a beep when the input is not valid according to the mask. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxBeepOnErrorDescr), DefaultValue(false) ] public bool BeepOnError { get { return this.flagState[BEEP_ON_ERROR]; } set { this.flagState[BEEP_ON_ERROR] = value; } } ////// Gets a value indicating whether the user can undo the previous operation in a text box control. /// Unsupported method/property. /// WndProc ignores EM_CANUNDO. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new bool CanUndo { get { return false; } } ////// 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. /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; // Translate for Rtl if necessary // HorizontalAlignment align = RtlTranslateHorizontal(textAlign); cp.ExStyle &= ~NativeMethods.WS_EX_RIGHT; // WS_EX_RIGHT overrides the ES_XXXX alignment styles switch (align) { case HorizontalAlignment.Left: cp.Style |= NativeMethods.ES_LEFT; break; case HorizontalAlignment.Center: cp.Style |= NativeMethods.ES_CENTER; break; case HorizontalAlignment.Right: cp.Style |= NativeMethods.ES_RIGHT; break; } return cp; } } ////// The culture that determines the value of the localizable mask language separators and placeholders. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxCultureDescr), RefreshProperties(RefreshProperties.Repaint), ] public CultureInfo Culture { get { return this.maskedTextProvider.Culture; } set { if( value == null ) { throw new ArgumentNullException(); } if( !this.maskedTextProvider.Culture.Equals(value) ) { // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, value, this.maskedTextProvider.AllowPromptAsInput, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); SetMaskedTextProvider( newProvider ); } } } ////// Specifies the formatting options for text cut/copited to the clipboard (Whether the mask returned from the Text /// property includes Literals and/or prompt characters). /// When prompt characters are excluded, theyare returned as spaces in the string returned. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxCutCopyMaskFormat), RefreshProperties(RefreshProperties.Repaint), DefaultValue(MaskFormat.IncludeLiterals) ] public MaskFormat CutCopyMaskFormat { get { if( this.flagState[CUTCOPYINCLUDEPROMPT] ) { if( this.flagState[CUTCOPYINCLUDELITERALS] ) { return MaskFormat.IncludePromptAndLiterals; } return MaskFormat.IncludePrompt; } if( this.flagState[CUTCOPYINCLUDELITERALS] ) { return MaskFormat.IncludeLiterals; } return MaskFormat.ExcludePromptAndLiterals; } set { //valid values are 0x0 to 0x3 if (!ClientUtils.IsEnumValid(value, (int)value, (int)MaskFormat.ExcludePromptAndLiterals, (int)MaskFormat.IncludePromptAndLiterals)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(MaskFormat)); } if( value == MaskFormat.IncludePrompt ) { this.flagState[CUTCOPYINCLUDEPROMPT] = true; this.flagState[CUTCOPYINCLUDELITERALS] = false; } else if( value == MaskFormat.IncludeLiterals ) { this.flagState[CUTCOPYINCLUDEPROMPT] = false; this.flagState[CUTCOPYINCLUDELITERALS] = true; } else // value == MaskFormat.IncludePromptAndLiterals || value == MaskFormat.ExcludePromptAndLiterals { bool include = value == MaskFormat.IncludePromptAndLiterals; this.flagState[CUTCOPYINCLUDEPROMPT] = include; this.flagState[CUTCOPYINCLUDELITERALS] = include; } } } ////// Specifies the IFormatProvider to be used when parsing the string to the ValidatingType. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public IFormatProvider FormatProvider { get { return this.formatProvider; } set { this.formatProvider = value; } } ////// Specifies whether the PromptCharacter is displayed when the control loses focus. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxHidePromptOnLeaveDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(false) ] public bool HidePromptOnLeave { get { return this.flagState[HIDE_PROMPT_ON_LEAVE]; } set { if( this.flagState[HIDE_PROMPT_ON_LEAVE] != value ) { this.flagState[HIDE_PROMPT_ON_LEAVE] = value; // If the control is not focused and there are available edit positions (mask not full) we need to // update the displayed text. if( !this.flagState[IS_NULL_MASK]&& !this.Focused && !this.MaskFull && !this.DesignMode ) { SetWindowText(); } } } } ////// Specifies whether to include mask literal characters when formatting the text. /// private bool IncludeLiterals { get { return this.maskedTextProvider.IncludeLiterals; } set { this.maskedTextProvider.IncludeLiterals = value; } } ////// Specifies whether to include the mask prompt character when formatting the text in places /// where an edit char has not being assigned. /// private bool IncludePrompt { get { return this.maskedTextProvider.IncludePrompt; } set { this.maskedTextProvider.IncludePrompt = value; } } ////// Specifies the text insertion mode of the text box. This can be used to simulated the Access masked text /// control behavior where insertion is set to TextInsertionMode.AlwaysOverwrite /// This property has no particular effect if no mask has been set. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxInsertKeyModeDescr), DefaultValue(InsertKeyMode.Default) ] public InsertKeyMode InsertKeyMode { get { return this.insertMode; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)InsertKeyMode.Default, (int)InsertKeyMode.Overwrite)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(InsertKeyMode)); } if (this.insertMode != value) { bool isOverwrite = this.IsOverwriteMode; this.insertMode = value; if (isOverwrite != this.IsOverwriteMode) { OnIsOverwriteModeChanged(EventArgs.Empty); } } } } ////// Overridden to handle unsupported RETURN key. /// protected override bool IsInputKey(Keys keyData) { if ((keyData & Keys.KeyCode) == Keys.Return) { return false; } return base.IsInputKey(keyData); } ////// Specifies whether text insertion mode in 'on' or not. /// [ Browsable(false) ] public bool IsOverwriteMode { get { if( this.flagState[IS_NULL_MASK]) { return false; // EditBox always inserts. } switch (this.insertMode) { case InsertKeyMode.Overwrite: return true; case InsertKeyMode.Insert: return false; case InsertKeyMode.Default: // Note that the insert key state should be per process and its initial state insert, this is the // behavior of apps like WinWord, WordPad and VS; so we have to keep track of it and not query its // system value. //return Control.IsKeyLocked(Keys.Insert); return this.flagState[INSERT_TOGGLED]; default: Debug.Fail("Invalid InsertKeyMode. This code path should have never been executed."); return false; } } } ////// Event to notify when the insert mode has changed. This is required for data binding. /// [ SRCategory(SR.CatPropertyChanged), SRDescription(SR.MaskedTextBoxIsOverwriteModeChangedDescr) ] public event EventHandler IsOverwriteModeChanged { add { Events.AddHandler(EVENT_ISOVERWRITEMODECHANGED, value); } remove { Events.RemoveHandler(EVENT_ISOVERWRITEMODECHANGED, value); } } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new string[] Lines { get { string[] lines; this.flagState[QUERY_BASE_TEXT] = true; try { lines = base.Lines; } finally { this.flagState[QUERY_BASE_TEXT] = false; } return lines; } set {} } ////// The mask applied to this control. The setter resets the underlying MaskedTextProvider object and attempts /// to add the existing input text (if any) using the new mask, failure is ignored. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxMaskDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(""), MergableProperty(false), Localizable(true), Editor("System.Windows.Forms.Design.MaskPropertyEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)) ] public string Mask { get { return this.flagState[IS_NULL_MASK]? string.Empty : this.maskedTextProvider.Mask; } set { // // We dont' do anything if: // 1. IsNullOrEmpty( value )->[Reset control] && this.flagState[IS_NULL_MASK]==>Already Reset. // 2. !IsNullOrEmpty( value )->[Set control] && !this.flagState[IS_NULL_MASK][control is set] && [value is the same]==>No need to update. // if( this.flagState[IS_NULL_MASK] == string.IsNullOrEmpty( value ) && (this.flagState[IS_NULL_MASK] || value == this.maskedTextProvider.Mask) ) { return; } string text = null; string newMask = value; // We need to update the this.flagState[IS_NULL_MASK]field before raising any events (when setting the maskedTextProvider) so // querying for properties from an event handler returns the right value (i.e: Text). if( string.IsNullOrEmpty( value ) ) // Resetting the control, the native edit control will be in charge. { // Need to get the formatted & unformatted text before resetting the mask, they'll be used to determine whether we need to // raise the TextChanged event. string formattedText = TextOutput; string unformattedText = this.maskedTextProvider.ToString(false, false); this.flagState[IS_NULL_MASK] = true; if( this.maskedTextProvider.IsPassword ) { SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); } // Set the window text to the unformatted text before raising events. Also, TextChanged needs to be raised after MaskChanged so // pass false to SetWindowText 'raiseTextChanged' param. SetWindowText(unformattedText, false, false ); EventArgs e = EventArgs.Empty; OnMaskChanged(e); if( unformattedText != formattedText ) { OnTextChanged(e); } newMask = nullMask; } else // Setting control to a new value. { foreach( char c in value ) { if( !MaskedTextProvider.IsValidMaskChar( c ) ) { // Same message as in SR.MaskedTextProviderMaskInvalidChar in System.txt throw new ArgumentException( SR.GetString( SR.MaskedTextBoxMaskInvalidChar) ); } } if( this.flagState[IS_NULL_MASK] ) { // If this.IsNullMask, we are setting the mask to a new value; in this case we need to get the text because // the underlying MTP does not have it (used as a property backend only) and pass it to SetMaskedTextProvider // method below to update the provider. text = this.Text; } } // Recreate masked text provider since this property is read-only. MaskedTextProvider newProvider = new MaskedTextProvider( newMask, this.maskedTextProvider.Culture, this.maskedTextProvider.AllowPromptAsInput, this.maskedTextProvider.PromptChar, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); //text == null when setting to a different mask value or when resetting the mask to null. //text != null only when setting the mask from null to some value. SetMaskedTextProvider( newProvider, text ); } } ////// Event to notify when the mask has changed. /// [ SRCategory(SR.CatPropertyChanged), SRDescription(SR.MaskedTextBoxMaskChangedDescr) ] public event EventHandler MaskChanged { add { Events.AddHandler(EVENT_MASKCHANGED, value); } remove { Events.RemoveHandler(EVENT_MASKCHANGED, value); } } ////// Specifies whether the test string required input positions, as specified by the mask, have /// all been assigned. /// [ Browsable(false) ] public bool MaskCompleted { get { return this.maskedTextProvider.MaskCompleted; } } ////// Specifies whether all inputs (required and optional) have been provided into the mask successfully. /// [ Browsable(false) ] public bool MaskFull { get { return this.maskedTextProvider.MaskFull; } } ////// Returns a copy of the control's internal MaskedTextProvider. This is useful for user's to provide /// cloning semantics for the control (we don't want to do it) w/o incurring in any perf penalty since /// some of the properties require recreating the underlying provider when they are changed. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public MaskedTextProvider MaskedTextProvider { get { return this.flagState[IS_NULL_MASK] ? null : (MaskedTextProvider) this.maskedTextProvider.Clone(); } } ////// Event to notify when an input has been rejected according to the mask. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxMaskInputRejectedDescr) ] public event MaskInputRejectedEventHandler MaskInputRejected { add { Events.AddHandler(EVENT_MASKINPUTREJECTED, value); } remove { Events.RemoveHandler(EVENT_MASKINPUTREJECTED, value); } } ////// Unsupported method/property. /// WndProc ignores EM_LIMITTEXT & this is a virtual method. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public override int MaxLength { get{ return base.MaxLength; } set{} } ////// Unsupported method/property. /// virtual method. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public override bool Multiline { get { return false; } set {} } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new event EventHandler MultilineChanged { add { } remove { } } ////// Specifies the character to be used in the formatted string in place of editable characters, if /// set to any printable character, the text box becomes a password text box, to reset it use the null /// character. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxPasswordCharDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue('\0') // This property is shadowed by MaskedTextBoxDesigner. ] public char PasswordChar { get { // The password char could be the one set in the control or the system password char, // in any case the maskedTextProvider has the correct one. return this.maskedTextProvider.PasswordChar; } set { if( !MaskedTextProvider.IsValidPasswordChar(value) ) // null character accepted (resets value) { // Same message as in SR.MaskedTextProviderInvalidCharError. throw new ArgumentException(SR.GetString(SR.MaskedTextBoxInvalidCharError) ); } if( this.passwordChar != value ) { if( value == this.maskedTextProvider.PromptChar ) { // Prompt and password chars must be different. throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); } this.passwordChar = value; // UseSystemPasswordChar take precedence over PasswordChar...Let's check. if (!this.UseSystemPasswordChar) { this.maskedTextProvider.PasswordChar = value; if( this.flagState[IS_NULL_MASK]) { SetEditControlPasswordChar(value); } else { SetWindowText(); } VerifyImeRestrictedModeChanged(); } } } } ////// Determines if the control is in password protect mode. /// internal override bool PasswordProtect { get { if( this.maskedTextProvider != null ) // could be queried during object construction. { return this.maskedTextProvider.IsPassword; } return base.PasswordProtect; } } ////// Specifies the prompt character to be used in the formatted string for unsupplied characters. /// [ SRCategory(SR.CatAppearance), SRDescription(SR.MaskedTextBoxPromptCharDescr), RefreshProperties(RefreshProperties.Repaint), Localizable(true), DefaultValue('_') ] public char PromptChar { get { return this.maskedTextProvider.PromptChar; } set { if( !MaskedTextProvider.IsValidInputChar(value) ) { // This message is the same as the one in SR.MaskedTextProviderInvalidCharError. throw new ArgumentException(SR.GetString(SR.MaskedTextBoxInvalidCharError) ); } if( this.maskedTextProvider.PromptChar != value ) { // We need to check maskedTextProvider password char in case it is using the system password. if( value == this.passwordChar || value == this.maskedTextProvider.PasswordChar ) { // Prompt and password chars must be different. throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); } // Recreate masked text provider to be consistent with AllowPromptAsInput - current text may have chars with same value as new prompt. MaskedTextProvider newProvider = new MaskedTextProvider( this.maskedTextProvider.Mask, this.maskedTextProvider.Culture, this.maskedTextProvider.AllowPromptAsInput, value, this.maskedTextProvider.PasswordChar, this.maskedTextProvider.AsciiOnly ); SetMaskedTextProvider( newProvider ); } } } ////// Overwrite base class' property. /// public new bool ReadOnly { get { return base.ReadOnly; } set { if (this.ReadOnly != value) { // if true, this disables IME in the base class. base.ReadOnly = value; if (!this.flagState[IS_NULL_MASK]) { // Prompt will be hidden. SetWindowText(); } } } } ////// Specifies whether to include the mask prompt character when formatting the text in places /// where an edit char has not being assigned. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxRejectInputOnFirstFailureDescr), DefaultValue(false) ] public bool RejectInputOnFirstFailure { get { return this.flagState[REJECT_INPUT_ON_FIRST_FAILURE]; } set { this.flagState[REJECT_INPUT_ON_FIRST_FAILURE] = value; } } ////// Designe time support for resetting the Culture property. /// /* No longer needed since Culture has been removed from the property browser - Left here for documentation. [EditorBrowsable(EditorBrowsableState.Never)] private void ResetCulture() { this.Culture = CultureInfo.CurrentCulture; }*/ ////// Specifies whether to reset and skip the current position if editable, when the input character /// has the same value as the prompt. This property takes precedence over AllowPromptAsInput. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxResetOnPrompt), DefaultValue(true) ] public bool ResetOnPrompt { get { return this.maskedTextProvider.ResetOnPrompt; } set { this.maskedTextProvider.ResetOnPrompt = value; } } ////// Specifies whether to reset and skip the current position if editable, when the input /// is the space character. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxResetOnSpace), DefaultValue(true) ] public bool ResetOnSpace { get { return this.maskedTextProvider.ResetOnSpace; } set { this.maskedTextProvider.ResetOnSpace = value; } } ////// Specifies whether to skip the current position if non-editable and the input character has /// the same value as the literal at that position. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxSkipLiterals), DefaultValue(true) ] public bool SkipLiterals { get { return this.maskedTextProvider.SkipLiterals; } set { this.maskedTextProvider.SkipLiterals = value; } } ////// The currently selected text (if any) in the control. /// public override string SelectedText { get { if( this.flagState[IS_NULL_MASK]) { return base.SelectedText; } return GetSelectedText(); } set { SetSelectedTextInternal(value, true); } } internal override void SetSelectedTextInternal(string value, bool clearUndo) { if (this.flagState[IS_NULL_MASK]) { base.SetSelectedTextInternal(value, true); // Operates as a regular text box base. return; } PasteInt( value ); } ////// Set the composition string as the result string. /// private void ImeComplete() { this.flagState[IME_COMPLETING] = true; ImeNotify(NativeMethods.CPS_COMPLETE); } ////// Notifies the IMM about changes to the status of the IME input context. /// private void ImeNotify(int action) { HandleRef handle = new HandleRef(this, this.Handle); IntPtr inputContext = UnsafeNativeMethods.ImmGetContext(handle); if (inputContext != IntPtr.Zero) { try { UnsafeNativeMethods.ImmNotifyIME(new HandleRef(null, inputContext), NativeMethods.NI_COMPOSITIONSTR, action, 0); } finally { UnsafeNativeMethods.ImmReleaseContext(handle, new HandleRef(null, inputContext)); } } else { Debug.Fail("Could not get IME input context."); } } ////// Sets the underlying edit control's password char to the one obtained from this.PasswordChar. /// This is used when the control is passworded and this.flagState[IS_NULL_MASK]. /// private void SetEditControlPasswordChar( char pwdChar ) { if (this.IsHandleCreated) { // This message does not return a value. SendMessage(NativeMethods.EM_SETPASSWORDCHAR, pwdChar, 0); Invalidate(); } } ////// The value of the Edit control default password char. /// private char SystemPasswordChar { get { if (MaskedTextBox.systemPwdChar == '\0') { // This is the hard way to get the password char - left here for information. // It is picked up from Comctl32.dll. If VisualStyles is enabled it will get the dot char. /* StringBuilder charVal = new StringBuilder(20); // it could be 0x0000000000009999 format. bool foundRsc = false; int IDS_PASSWORDCHAR = 0x1076; // %ntsdx%\shell\comctrl32\v6\rcids.h // defined in en.rc as: IDS_PASSWORDCHAR "9679" // 0x25cf - Black Circle IntSecurity.UnmanagedCode.Assert(); try { // The GetModuleHandle function returns a handle to a mapped module without incrementing its reference count. // @"C:\windows\winsxs\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.10.0_x-ww_f7fb5805\comctl32.dll if VisulaStyles enabled. IntPtr hModule = UnsafeNativeMethods.GetModuleHandle("comctl32.dll"); Debug.Assert(hModule != IntPtr.Zero, String.Format("Could not get a handle to comctl32.dll - Error: 0x{0:X8}", Marshal.GetLastWin32Error())); foundRsc = UnsafeNativeMethods.LoadString(new HandleRef(null, hModule), IDS_PASSWORDCHAR, charVal, charVal.Capacity); } catch( Exception ex ) { if( ClientUtils.IsSecurityOrCriticalException( ex ) ) { throw; } } finally { CodeAccessPermission.RevertAssert(); } MaskedTextBox.systemPwdChar = foundRsc ? (char) int.Parse(charVal.ToString()) : MaskedTextProvider.DefaultPasswordChar; */ // We need to temporarily create an edit control to get the default password character. // We cannot use this control because we would have to reset the native control's password char to use // the defult one so we can get it; this would change the text displayed in the box (even for a short time) // opening a sec hole. TextBox txtBox = new TextBox(); txtBox.UseSystemPasswordChar = true; // this forces the creation of the control handle. MaskedTextBox.systemPwdChar = txtBox.PasswordChar; txtBox.Dispose(); } return MaskedTextBox.systemPwdChar; } } ////// The Text setter validates the input char by char, raising the MaskInputRejected event for invalid chars. /// The Text getter returns the formatted text according to the IncludeLiterals and IncludePrompt properties. /// [ Editor("System.Windows.Forms.Design.MaskedTextBoxTextEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), SRCategory(SR.CatAppearance), RefreshProperties(RefreshProperties.Repaint), Bindable(true), DefaultValue(""), // This property is shadowed by MaskedTextBoxDesigner. Localizable(true) ] public override string Text { get { if( this.flagState[IS_NULL_MASK] || this.flagState[QUERY_BASE_TEXT]) { return base.Text; } return TextOutput; } set { if (this.flagState[IS_NULL_MASK]) { base.Text = value; return; } if (string.IsNullOrEmpty(value)) { // reset the input text. Delete(Keys.Delete, 0, this.maskedTextProvider.Length); } else { if( this.RejectInputOnFirstFailure ) { MaskedTextResultHint hint; string oldText = TextOutput; if (this.maskedTextProvider.Set(value, out this.caretTestPos, out hint)) { //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect ) if( TextOutput != oldText ) { SetText(); } this.SelectionStart = ++this.caretTestPos; } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); } } else { Replace(value, /*startPosition*/ 0, /*selectionLen*/ this.maskedTextProvider.Length); } } } } ////// Returns the length of the displayed text. See VSW#502543. /// [Browsable( false )] public override int TextLength { get { if( this.flagState[IS_NULL_MASK] ) { return base.TextLength; } // In Win9x systems TextBoxBase.TextLength calls Text.Length directly and does not query the window for the actual text length. // If TextMaskFormat is set to a anything different from IncludePromptAndLiterals or HidePromptOnLeave is true the return value // may be incorrect because the Text property value and the display text may be different. We need to handle this here. return GetFormattedDisplayString().Length; } } ////// The formatted text, it is what the Text getter returns when a mask has been applied to the control. /// The text format follows the IncludeLiterals and IncludePrompt properties (See MaskedTextProvider.ToString()). /// private string TextOutput { get { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); return this.maskedTextProvider.ToString(); } } ////// Gets or sets how text is aligned in the control. /// Note: This code is duplicated in TextBox for simplicity. /// [ Localizable(true), SRCategory(SR.CatAppearance), DefaultValue(HorizontalAlignment.Left), SRDescription(SR.TextBoxTextAlignDescr) ] public HorizontalAlignment TextAlign { get { return textAlign; } set { if (textAlign != value) { //verify that 'value' is a valid enum type... //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)HorizontalAlignment.Left, (int)HorizontalAlignment.Center)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(HorizontalAlignment)); } textAlign = value; RecreateHandle(); OnTextAlignChanged(EventArgs.Empty); } } } ////// Event to notify the text alignment has changed. /// [ SRCategory(SR.CatPropertyChanged), SRDescription(SR.RadioButtonOnTextAlignChangedDescr) ] public event EventHandler TextAlignChanged { add { Events.AddHandler(EVENT_TEXTALIGNCHANGED, value); } remove { Events.RemoveHandler(EVENT_TEXTALIGNCHANGED, value); } } ////// Specifies the formatting options for text output (Whether the mask returned from the Text /// property includes Literals and/or prompt characters). /// When prompt characters are excluded, theyare returned as spaces in the string returned. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxTextMaskFormat), RefreshProperties(RefreshProperties.Repaint), DefaultValue(MaskFormat.IncludeLiterals) ] public MaskFormat TextMaskFormat { get { if( this.IncludePrompt ) { if( this.IncludeLiterals ) { return MaskFormat.IncludePromptAndLiterals; } return MaskFormat.IncludePrompt; } if( this.IncludeLiterals ) { return MaskFormat.IncludeLiterals; } return MaskFormat.ExcludePromptAndLiterals; } set { if( this.TextMaskFormat == value ) { return; } //valid values are 0x0 to 0x3 if (!ClientUtils.IsEnumValid(value, (int)value, (int)MaskFormat.ExcludePromptAndLiterals, (int)MaskFormat.IncludePromptAndLiterals)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(MaskFormat)); } // Changing the TextMaskFormat will likely change the 'output' text (Text getter value). Cache old value to // verify it against the new value and raise OnTextChange if needed. string oldText = this.flagState[IS_NULL_MASK] ? null : TextOutput; if( value == MaskFormat.IncludePrompt ) { this.IncludePrompt = true; this.IncludeLiterals = false; } else if( value == MaskFormat.IncludeLiterals ) { this.IncludePrompt = false; this.IncludeLiterals = true; } else // value == MaskFormat.IncludePromptAndLiterals || value == MaskFormat.ExcludePromptAndLiterals { bool include = value == MaskFormat.IncludePromptAndLiterals; this.IncludePrompt = include; this.IncludeLiterals = include; } if( oldText != null && oldText != TextOutput ) { OnTextChanged(EventArgs.Empty); } } } ////// Provides some interesting information for the TextBox control in String form. /// Returns the test string (no password, including literals and prompt). /// public override string ToString() { if( this.flagState[IS_NULL_MASK] ) { return base.ToString(); } // base.ToString will call Text, we want to always display prompt and literals. bool includePrompt = this.IncludePrompt; bool includeLits = this.IncludeLiterals; string str; try { this.IncludePrompt = this.IncludeLiterals = true; str = base.ToString(); } finally { this.IncludePrompt = includePrompt; this.IncludeLiterals = includeLits; } return str; } ////// Event to notify when the validating object completes parsing the formatted text. /// [ SRCategory(SR.CatFocus), SRDescription(SR.MaskedTextBoxTypeValidationCompletedDescr) ] public event TypeValidationEventHandler TypeValidationCompleted { add { Events.AddHandler(EVENT_VALIDATIONCOMPLETED, value); } remove { Events.RemoveHandler(EVENT_VALIDATIONCOMPLETED, value); } } ////// Indicates if the text in the edit control should appear as the default password character. /// This property has precedence over the PasswordChar property. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.MaskedTextBoxUseSystemPasswordCharDescr), RefreshProperties(RefreshProperties.Repaint), DefaultValue(false) ] public bool UseSystemPasswordChar { get { return this.flagState[USE_SYSTEM_PASSWORD_CHAR]; } set { if (value != this.flagState[USE_SYSTEM_PASSWORD_CHAR]) { if (value) { if( this.SystemPasswordChar == this.PromptChar ) { // Prompt and password chars must be different. throw new InvalidOperationException( SR.GetString(SR.MaskedTextBoxPasswordAndPromptCharError) ); } this.maskedTextProvider.PasswordChar = this.SystemPasswordChar; } else { // this.passwordChar could be '\0', in which case we are resetting the display to show the input char. this.maskedTextProvider.PasswordChar = this.passwordChar; } this.flagState[USE_SYSTEM_PASSWORD_CHAR] = value; if( this.flagState[IS_NULL_MASK]) { SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); } else { SetWindowText(); } VerifyImeRestrictedModeChanged(); } } } ////// Type of the object to be used to parse the text when the user leaves the control. /// A ValidatingType object must implement a method with one fo the following signature: /// public static Object Parse(string) /// public static Object Parse(string, IFormatProvider) /// See DateTime.Parse(...) for an example. /// [ Browsable(false), DefaultValue(null) ] public Type ValidatingType { get { return this.validatingType; } set { if( this.validatingType != value ) { this.validatingType = value; } } } ////// Unsupported method/property. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new bool WordWrap { get { return false; } set {} } ////////////// Methods ////// Clears information about the most recent operation from the undo buffer of the control. /// Unsupported property/method. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new void ClearUndo() { } ////// Creates a handle for this control. This method is called by the .NET Framework, this should /// not be called. Inheriting classes should always call base.createHandle when overriding this method. /// Overridden to be able to set the control text with the masked (passworded) value when recreating /// handle, since the underlying native edit control is not aware of it. /// [ EditorBrowsable(EditorBrowsableState.Advanced), UIPermission(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows) ] protected override void CreateHandle() { if (!this.flagState[IS_NULL_MASK] && RecreatingHandle) { // update cached text value in Control. Don't preserve caret, cannot query for selection start at this time. SetWindowText(GetFormattedDisplayString(), false, false); } base.CreateHandle(); } /// ////// Deletes characters from the control's text according to the key pressed (Delete/Backspace). /// Returns true if something gets actually deleted, false otherwise. /// private void Delete(Keys keyCode, int startPosition, int selectionLen) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); Debug.Assert( keyCode == Keys.Delete || keyCode == Keys.Back, "Delete called with keyCode == " + keyCode.ToString() ); Debug.Assert( startPosition >= 0 && ((startPosition + selectionLen) <= this.maskedTextProvider.Length), "Invalid position range." ); // On backspace, moving the start postion back by one has the same effect as delete. If text is selected, there is no // need for moving the position back. this.caretTestPos = startPosition; if( selectionLen == 0 ) { if( keyCode == Keys.Back ) { if( startPosition == 0 ) // At beginning of string, backspace does nothing. { return; } startPosition--; // so it can be treated as delete. } else // (keyCode == Keys.Delete) { if( (startPosition + selectionLen) == this.maskedTextProvider.Length ) // At end of string, delete does nothing. { return; } } } int tempPos; int endPos = selectionLen > 0 ? startPosition + selectionLen - 1 : startPosition; MaskedTextResultHint hint; string oldText = TextOutput; if (this.maskedTextProvider.RemoveAt(startPosition, endPos, out tempPos, out hint)) { //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect) // Text was changed. if( TextOutput != oldText ) { SetText(); this.caretTestPos = startPosition; } else { // If succeeded but nothing removed, the caret should move as follows: // 1. If selectionLen > 0, or on back and hint == SideEffect: move to selectionStart. // 2. If hint == NoEffect, On Delete move to next edit position, if any or not already in one. // On back move to the next edit postion at the left if no more assigned position at the right, // in such case find an assigned position and move one past or one position left if no assigned pos found // (taken care by 'startPosition--' above). // 3. If hint == SideEffect, on Back move like arrow key, (startPosition is already moved, startPosition-- above). if( selectionLen > 0 ) { this.caretTestPos = startPosition; } else { if( hint == MaskedTextResultHint.NoEffect ) // Case 2. { if( keyCode == Keys.Delete ) { this.caretTestPos = this.maskedTextProvider.FindEditPositionFrom(startPosition, forward); } else { if( this.maskedTextProvider.FindAssignedEditPositionFrom( startPosition, forward ) == MaskedTextProvider.InvalidIndex ) { // No assigned position at the right, nothing to shift then move to the next assigned position at the // left (if any). this.caretTestPos = this.maskedTextProvider.FindAssignedEditPositionFrom(startPosition, backward); } else { // there are assigned positions at the right so move to an edit position at the left to get ready for // removing the character on it or just shifting the characters at the right this.caretTestPos = this.maskedTextProvider.FindEditPositionFrom(startPosition, backward); } if( this.caretTestPos != MaskedTextProvider.InvalidIndex ) { this.caretTestPos++; // backspace gets ready to remove one position past the edit position. } } if( this.caretTestPos == MaskedTextProvider.InvalidIndex ) { this.caretTestPos = startPosition; } } else // (hint == MaskedTextProvider.OperationHint.SideEffect) { if( keyCode == Keys.Back ) // Case 3. { this.caretTestPos = startPosition; } } } } } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(tempPos, hint)); } // Reposition caret. Call base.SelectInternal for perf reasons. //this.SelectionLength = 0; //this.SelectionStart = this.caretTestPos; // new caret position. base.SelectInternal( this.caretTestPos, 0, this.maskedTextProvider.Length ); return; } ////// Returns the character nearest to the given point. /// public override char GetCharFromPosition(Point pt) { char ch; this.flagState[QUERY_BASE_TEXT] = true; try { ch = base.GetCharFromPosition(pt); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return ch; } ////// Returns the index of the character nearest to the given point. /// public override int GetCharIndexFromPosition(Point pt) { int index; this.flagState[QUERY_BASE_TEXT] = true; try { index = base.GetCharIndexFromPosition(pt); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return index; } ////// Returns the position of the last input character (or if available, the next edit position). /// This is used by base.AppendText. /// internal override int GetEndPosition() { if( this.flagState[IS_NULL_MASK]) { return base.GetEndPosition(); } int pos = this.maskedTextProvider.FindEditPositionFrom( this.maskedTextProvider.LastAssignedPosition + 1, forward ); if( pos == MaskedTextProvider.InvalidIndex ) { pos = this.maskedTextProvider.LastAssignedPosition + 1; } return pos; } ////// Unsupported method/property. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new int GetFirstCharIndexOfCurrentLine() { return 0; } ////// Unsupported method/property. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new int GetFirstCharIndexFromLine(int lineNumber) { return 0; } ////// Gets the string in the text box following the formatting parameters includePrompt and includeLiterals and /// honoring the PasswordChar property. /// private string GetFormattedDisplayString() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); bool includePrompt; if (this.ReadOnly) // Always hide prompt. { includePrompt = false; } else if (this.DesignMode) // Not RO and at design time, always show prompt. { includePrompt = true; } else // follow HidePromptOnLeave property. { includePrompt = !(this.HidePromptOnLeave && !this.Focused); } return this.maskedTextProvider.ToString(/*ignorePwdChar */ false, includePrompt, /*includeLiterals*/ true, 0, this.maskedTextProvider.Length); } ////// Unsupported method/property. /// virtual method. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public override int GetLineFromCharIndex(int index) { return 0; } ////// Returns the location of the character at the given index. /// public override Point GetPositionFromCharIndex(int index) { Point pos; this.flagState[QUERY_BASE_TEXT] = true; try { pos = base.GetPositionFromCharIndex(index); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return pos; } ////// Need to override this method so when get_Text is called we return the text that is actually /// painted in the control so measuring text works on the actual text and not the formatted one. /// internal override Size GetPreferredSizeCore(Size proposedConstraints) { Size size; this.flagState[QUERY_BASE_TEXT] = true; try { size = base.GetPreferredSizeCore( proposedConstraints ); } finally { this.flagState[QUERY_BASE_TEXT] = false; } return size; } ////// The selected text in the control according to the CutCopyMaskFormat properties (IncludePrompt/IncludeLiterals). /// This is used in Cut/Copy operations (SelectedText). /// The prompt character is always replaced with a blank character. /// private string GetSelectedText() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); int selStart, selLength; base.GetSelectionStartAndLength( out selStart, out selLength ); if( selLength == 0 ) { return string.Empty; } bool includePrompt = (CutCopyMaskFormat & MaskFormat.IncludePrompt ) != 0; bool includeLiterals = (CutCopyMaskFormat & MaskFormat.IncludeLiterals) != 0; return this.maskedTextProvider.ToString( /*ignorePasswordChar*/ true, includePrompt, includeLiterals, selStart, selLength ); } ///protected override void OnBackColorChanged(EventArgs e) { base.OnBackColorChanged(e); // VSWhidbey 465708. Force repainting of the entire window frame if (Application.RenderWithVisualStyles && this.IsHandleCreated && this.BorderStyle == BorderStyle.Fixed3D) { SafeNativeMethods.RedrawWindow(new HandleRef(this, this.Handle), null, NativeMethods.NullHandleRef, NativeMethods.RDW_INVALIDATE | NativeMethods.RDW_FRAME); } } /// /// Overridden to update the newly created handle with the settings of the PasswordChar properties /// if no mask has been set. /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); base.SetSelectionOnHandle(); if( this.flagState[IS_NULL_MASK]&& this.maskedTextProvider.IsPassword ) { SetEditControlPasswordChar(this.maskedTextProvider.PasswordChar); } } ////// Raises the IsOverwriteModeChanged event. /// [ EditorBrowsable(EditorBrowsableState.Advanced) ] protected virtual void OnIsOverwriteModeChanged(EventArgs e) { EventHandler eh = Events[EVENT_ISOVERWRITEMODECHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ////// Raises the protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if( this.flagState[IS_NULL_MASK]) { // Operates as a regular text box base. return; } Keys keyCode = e.KeyCode; // Special-case Return & Esc since they generate invalid characters we should not process OnKeyPress. if( keyCode == Keys.Return || keyCode == Keys.Escape ) { this.flagState[HANDLE_KEY_PRESS] = false; } // Insert is toggled when not modified with some other key (ctrl, shift...). Note that shift-Insert is // same as paste. if (keyCode == Keys.Insert && e.Modifiers == Keys.None && this.insertMode == InsertKeyMode.Default) { this.flagState[INSERT_TOGGLED] = !this.flagState[INSERT_TOGGLED]; OnIsOverwriteModeChanged(EventArgs.Empty); return; } if (e.Control && char.IsLetter((char)keyCode)) { switch (keyCode) { // Unsupported keys should not be handled to allow generatating the corresponding message // which is handled in the WndProc. //case Keys.Z: // ctrl-z == Undo. //case Keys.Y: // ctrl-y == Redo. // e.Handled = true; // return; // Note: Ctrl-Insert (Copy -Shortcut.CtrlIns) and Shft-Insert (Paste - Shortcut.ShiftIns) are // handled by the base class and behavior depend on ShortcutsEnabled property. // Special cases: usually cases where the native edit control would modify the mask. case Keys.H: // ctrl-h == Backspace == '\b' keyCode = Keys.Back; // handle it below. break; default: // Next OnKeyPress should not be handled to allow Ctrl-event. /// to be processed in the // base class so corresponding messages can be generated (WM_CUT/WM_COPY/WM_PASTE). // Combined characters don't generate OnKeyDown by themselves but they generate OnKeyPress. this.flagState[HANDLE_KEY_PRESS] = false; return; } } if ( keyCode == Keys.Delete || keyCode == Keys.Back ) // Deletion keys. { if (!this.ReadOnly) { int selectionLen; int startPosition; base.GetSelectionStartAndLength( out startPosition, out selectionLen ); switch (e.Modifiers) { case Keys.Shift: if( keyCode == Keys.Delete ) { keyCode = Keys.Back; } goto default; case Keys.Control: if( selectionLen == 0 ) // In other case, the selected text should be deleted. { if( keyCode == Keys.Delete ) // delete to the end of the string. { selectionLen = this.maskedTextProvider.Length - startPosition; } else // ( keyCode == Keys.Back ) // delete to the beginning of the string. { selectionLen = startPosition == this.maskedTextProvider.Length /*at end of text*/ ? startPosition : startPosition + 1; startPosition = 0; } } goto default; default: if( !this.flagState[HANDLE_KEY_PRESS] ) { this.flagState[HANDLE_KEY_PRESS] = true; } break; } // // Handle special case when using Korean IME and ending a composition. // /* This code is no longer needed after fixing bug#517013 - Left here for reference DON'T DELETE. if ((ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) && this.flagState[IME_ENDING_COMPOSITION]) { // Korean IME & Edit control weirdness in action: // When pressing Del/Esc/Enter/BckSpc during a composition, the character is converted and a // corresponding WM_IME_CHAR in placed in the app queue. if(keyCode == Keys.Back && selectionLen == 0) { // After the WM_IME_CHAR message is processed, a WM_CHAR message is queued for the backspace; the // edit control processes the message deleting the previous character. Since we don't pass this // message to the edit control we need to do it ourselves (move position one ahead because it is // set at the composition window which is inserted in the test string. startPosition++; // Note: If the converted character is invalid and there is a character already placed in the previous // position, it will be deleted. THIS IS BY DESIGN: It is exactly like the user performing the convertion // and then pressing backspace, there's no way to differenciate these two scenarios. // See VSWhidbey#200690 for the sequence of messages generated. } } */ Delete(keyCode, startPosition, selectionLen); e.SuppressKeyPress = true; } } } /// /// Raises the protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); if( this.flagState[IS_NULL_MASK]) { // Operates as a regular text box base. return; } // This key may be a combined key involving a letter, like Ctrl-A; let the native control handle it. if( !this.flagState[HANDLE_KEY_PRESS] ) { this.flagState[HANDLE_KEY_PRESS] = true; // When the combined key involves a letter, the final character is not a letter. There are some // Ctrl combined keys that generate a letter and can be confusing; we do not mean to pass those // characters to the underlying Edit control. These combinations are: Ctrl-F<#> and Ctrl-Atl-event. /// if (!char.IsLetter(e.KeyChar)) { return; } } if( !this.ReadOnly) { // At this point the character needs to be processed ... MaskedTextResultHint hint; int selectionStart; int selectionLen; base.GetSelectionStartAndLength( out selectionStart, out selectionLen ); string oldText = TextOutput; if (PlaceChar(e.KeyChar, selectionStart, selectionLen, this.IsOverwriteMode, out hint)) { //if( hint == MaskedTextResultHint.Success || hint == MaskedTextResultHint.SideEffect ) if( TextOutput != oldText ) { SetText(); // Now set the text in the display. } this.SelectionStart = ++this.caretTestPos; // caretTestPos is updated in PlaceChar. if (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) { // Korean IMEs complete composition when a character has been fully converted, so the composition string // is only one-character long; once composed we block the IME if there ins't more room in the test string. int editPos = this.maskedTextProvider.FindUnassignedEditPositionFrom(this.caretTestPos, forward); if (editPos == MaskedTextProvider.InvalidIndex) { ImeComplete(); // Force completion of compostion. } } } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); // caretTestPos is updated in PlaceChar. } if( selectionLen > 0 ) { this.SelectionLength = 0; } e.Handled = true; } } /// /// protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); // KeyUp is the last message to be processed so it is the best place to reset these flags. if (this.flagState[IME_COMPLETING]) { this.flagState[IME_COMPLETING] = false; } if( this.flagState[IME_ENDING_COMPOSITION] ) { this.flagState[IME_ENDING_COMPOSITION] = false; } } ///Raises the ///event. /// Raises the MaskChanged event. /// [ EditorBrowsable(EditorBrowsableState.Advanced) ] protected virtual void OnMaskChanged(EventArgs e) { EventHandler eh = Events[EVENT_MASKCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ////// Raises the MaskInputRejected event. /// private void OnMaskInputRejected(MaskInputRejectedEventArgs e) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if (this.BeepOnError) { System.Media.SoundPlayer sp = new System.Media.SoundPlayer(); sp.Play(); } MaskInputRejectedEventHandler eh = Events[EVENT_MASKINPUTREJECTED] as MaskInputRejectedEventHandler; if (eh != null) { eh(this, e); } } ////// Unsupported method/property. /// virtual method. /// [ EditorBrowsable(EditorBrowsableState.Never) ] protected override void OnMultilineChanged(EventArgs e) { } ////// Raises the TextAlignChanged event. /// protected virtual void OnTextAlignChanged(EventArgs e) { EventHandler eh = Events[EVENT_TEXTALIGNCHANGED] as EventHandler; if (eh != null) { eh(this, e); } } ////// Raises the TypeValidationCompleted event. /// private void OnTypeValidationCompleted(TypeValidationEventArgs e) { TypeValidationEventHandler eh = Events[EVENT_VALIDATIONCOMPLETED] as TypeValidationEventHandler; if (eh != null) { eh(this, e); } } ////// Raises the System.Windows.Forms.Control.Validating event. /// Overridden here to be able to control the order validating events are /// raised [TypeValidationCompleted - Validating - Validated - Leave - KillFocus] /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected override void OnValidating(CancelEventArgs e) { // Note: It seems impractical to perform type validation here if the control is read only but we need // to be consistent with other TextBoxBase controls which don't check for RO; and we don't want // to fix them to avoid introducing breaking changes. PerformTypeValidation(e); base.OnValidating(e); } ////// Raises the TextChanged event and related Input/Output text events when mask is null. /// Overriden here to be able to control order of text changed events. /// protected override void OnTextChanged(EventArgs e) { // A text changed event handler will most likely query for the Text value, we need to return the // formatted one. bool queryBaseText = this.flagState[QUERY_BASE_TEXT]; this.flagState[QUERY_BASE_TEXT] = false; try { base.OnTextChanged(e); } finally { this.flagState[QUERY_BASE_TEXT] = queryBaseText; } } ////// Replaces the current selection in the text box specified by the startPosition and selectionLen parameters /// with the contents of the supplied string. /// private void Replace(string text, int startPosition, int selectionLen) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); Debug.Assert(text != null, "text is null."); // Clone the MaskedTextProvider so text properties are not modified until the paste operation is // completed. This is needed in case one of these properties is retreived in a MaskedInputRejected // event handler (clipboard text is attempted to be set into the input text char by char). MaskedTextProvider clonedProvider = (MaskedTextProvider) this.maskedTextProvider.Clone(); // Cache the current caret position so we restore it in case the text does not change. VSW#498875. int currentCaretPos = this.caretTestPos; // First replace characters in the selection (if any and if any edit positions) until completed, or the test position falls // outside the selection range, or there's no more room in the test string for editable characters. // Then insert any remaining characters from the input. MaskedTextResultHint hint = MaskedTextResultHint.NoEffect; int endPos = startPosition + selectionLen - 1; if( this.RejectInputOnFirstFailure ) { bool succeeded; succeeded = (startPosition > endPos) ? clonedProvider.InsertAt(text, startPosition, out this.caretTestPos, out hint ) : clonedProvider.Replace(text, startPosition, endPos, out this.caretTestPos, out hint); if( !succeeded ) { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, hint)); } } else { // temp hint used to preserve the 'primary' operation hint (no side effects). MaskedTextResultHint tempHint = hint; int testPos; foreach (char ch in text) { if( !this.maskedTextProvider.VerifyEscapeChar( ch, startPosition )) // char won't be escaped, find and edit position for it. { // Observe that we look for a position w/o respecting the selection length, because the input text could be larger than // the number of edit positions in the selection. testPos = clonedProvider.FindEditPositionFrom(startPosition, forward); if( testPos == MaskedTextProvider.InvalidIndex ) { // this will continue to execute (fail) until the end of the text so we fire the event for each remaining char. OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); continue; } startPosition = testPos; } int length = endPos >= startPosition ? 1 : 0; // if length > 0 we are (re)placing the input char in the current startPosition, otherwise we are inserting the input. bool replace = length > 0; if (PlaceChar(clonedProvider, ch, startPosition, length, replace, out tempHint)) { // caretTestPos is updated in PlaceChar call. startPosition = this.caretTestPos + 1; // place char will insert or replace a single character so the hint must be success, and that will be the final operation // result hint. if (tempHint == MaskedTextResultHint.Success && hint != tempHint) { hint = tempHint; } } else { OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, tempHint)); } } if (selectionLen > 0) { // At this point we have processed all characters from the input text (if any) but still need to // remove remaining characters from the selected text (if editable and valid chars). if (startPosition <= endPos) { if (!clonedProvider.RemoveAt(startPosition, endPos, out this.caretTestPos, out tempHint)) { OnMaskInputRejected(new MaskInputRejectedEventArgs(this.caretTestPos, tempHint)); } // If 'replace' is not actually performed (maybe the input is empty which means 'remove', hint will be whatever // the 'remove' operation result hint is. if (hint == MaskedTextResultHint.NoEffect && hint != tempHint) { hint = tempHint; } } } } bool updateText = TextOutput != clonedProvider.ToString(); // Always set the mtp, the formatted text could be the same but the assigned positions may be different. this.maskedTextProvider = clonedProvider; // Update text if needed. if( updateText ) { SetText(); // Update caret position. this.caretTestPos = startPosition; base.SelectInternal( this.caretTestPos, 0, this.maskedTextProvider.Length ); } else { this.caretTestPos = currentCaretPos; } return; } ////// Pastes specified text over the currently selected text (if any) shifting upper characters if /// input is longer than selected text, and/or removing remaining characters from the selection if /// input contains less characters. /// private void PasteInt( string text ) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); int selStart, selLength; base.GetSelectionStartAndLength(out selStart, out selLength); if( string.IsNullOrEmpty(text) ) { Delete( Keys.Delete, selStart, selLength ); } else { Replace(text, selStart, selLength); } } ////// Performs validation of the input string using the provided ValidatingType object (if any). /// Returns an object created from the formatted text. /// If the CancelEventArgs param is not null, it is assumed the control is leaving focus and /// the validation event chain is being executed (TypeValidationCompleted - Validating - Validated...); /// the value of the CancelEventArgs.Cancel property is the same as the TypeValidationEventArgs.Cancel /// on output (Cancel provides proper handling of focus shifting at the Control class level). /// Note: The text being validated does not include prompt chars. /// private object PerformTypeValidation(CancelEventArgs e) { object parseRetVal = null; if (this.validatingType != null) { string message = null; if (!this.flagState[IS_NULL_MASK]&& this.maskedTextProvider.MaskCompleted == false) { message = SR.GetString(SR.MaskedTextBoxIncompleteMsg); } else { string textValue; if( !this.flagState[IS_NULL_MASK]) // replace prompt with space. { textValue = this.maskedTextProvider.ToString(/*includePrompt*/ false, this.IncludeLiterals); } else { textValue = base.Text; } try { parseRetVal = Formatter.ParseObject( textValue, // data this.validatingType, // targetType typeof(string), // sourceType null, // targetConverter null, // sourceConverter this.formatProvider, // formatInfo null, // nullValue Formatter.GetDefaultDataSourceNullValue(this.validatingType)); // dataSourceNullValue } catch (Exception exception) { if (ClientUtils.IsSecurityOrCriticalException(exception)) { throw; } if (exception.InnerException != null) // Outer exception is a generic TargetInvocationException. { exception = exception.InnerException; } message = exception.GetType().ToString() + ": " + exception.Message; } } bool isValidInput = false; if (message == null) { isValidInput = true; message = SR.GetString(SR.MaskedTextBoxTypeValidationSucceeded); } TypeValidationEventArgs tve = new TypeValidationEventArgs(this.validatingType, isValidInput, parseRetVal, message); OnTypeValidationCompleted(tve); if( e != null ) { e.Cancel = tve.Cancel; } } return parseRetVal; } ////// Insert or replaces the specified character into the control's text and updates the caret position. /// If overwrite is true, it replaces the character at the selection start position. /// private bool PlaceChar(char ch, int startPosition, int length, bool overwrite, out MaskedTextResultHint hint) { return PlaceChar(this.maskedTextProvider, ch, startPosition, length, overwrite, out hint ); } ////// Override version to be able to perform the operation on a cloned provider. /// private bool PlaceChar(MaskedTextProvider provider, char ch, int startPosition, int length, bool overwrite, out MaskedTextResultHint hint) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); this.caretTestPos = startPosition; if (startPosition < this.maskedTextProvider.Length) { if (length > 0) // Replacing selection with input char. { int endPos = startPosition + length - 1; return provider.Replace(ch, startPosition, endPos, out this.caretTestPos, out hint); } else { if (overwrite) { // overwrite character at next edit position from startPosition (inclusive). return provider.Replace(ch, startPosition, out this.caretTestPos, out hint); } else // insert. { return provider.InsertAt(ch, startPosition, out this.caretTestPos, out hint); } } } hint = MaskedTextResultHint.UnavailableEditPosition; return false; } ////// [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { // // The base class should be called first because it implements ShortcutsEnabled, // which takes precedence over Ctrl+A // bool msgProcessed = base.ProcessCmdKey(ref msg, keyData); if (!msgProcessed) { if ((int)keyData == (int)Shortcut.CtrlA) { base.SelectAll(); msgProcessed = true; // This prevents generating a WM_CHAR for 'A'. } } return msgProcessed; } ///: /// Processes a command key. This method is called during message /// pre-processing to handle command keys. Command keys are keys that always /// take precedence over regular input keys. Examples of command keys /// include accelerators and menu shortcuts. The method must return true to /// indicate that it has processed the command key, or false to indicate /// that the key is not a command key. /// /// processCmdKey() first checks if the control has a context menu, and if /// so calls the menu's processCmdKey() to check for menu shortcuts. If the /// command key isn't a menu shortcut, and if the control has a parent, the /// key is passed to the parent's processCmdKey() method. The net effect is /// that command keys are "bubbled" up the control hierarchy. /// /// When overriding processCmdKey(), a control should return true to /// indicate that it has processed the key. For keys that aren't processed by /// the control, the result of "base.processCmdKey()" should be returned. /// /// Controls will seldom, if ever, need to override this method. /// /// /// Implements the handling of Ctrl+A (select all). Note: Code copied from TextBox. ////// We need to override this method so we can handle input language changes properly. Control /// doesn't handle the WM_CHAR messages generated after WM_IME_CHAR messages, it passes them /// to DefWndProc (the characters would be displayed in the text box always). /// /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected internal override bool ProcessKeyMessage(ref Message m) { // call base's method so the WM_CHAR and other messages are processed; this gives Control the // chance to flush all pending WM_CHAR processing after WM_IME_CHAR messages are generated. bool msgProcessed = base.ProcessKeyMessage(ref m); if (this.flagState[IS_NULL_MASK]) { return msgProcessed; // Operates as a regular text box base. } // If this WM_CHAR message is sent after WM_IME_CHAR, we ignore it since we already processed // the corresponding WM_IME_CHAR message. if( m.Msg == NativeMethods.WM_CHAR && base.ImeWmCharsToIgnore > 0 ) { return true; // meaning, we handled the message so it is not passed to the default WndProc. } return msgProcessed; } ////// Designe time support for resetting Culture property.. /// private void ResetCulture() { this.Culture = CultureInfo.CurrentCulture; } ////// Unsupported method/property. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new void ScrollToCaret() { } ////// Sets the underlying MaskedTextProvider object. Used when the control is initialized /// and one of its properties, backed up by the MaskedTextProvider, changes; this requires /// recreating the provider because it is immutable. /// private void SetMaskedTextProvider( MaskedTextProvider newProvider ) { SetMaskedTextProvider( newProvider, null); } ////// Overload to allow for passing the text when the mask is being changed from null, /// in this case the maskedTextProvider holds backend info only (not the text). /// private void SetMaskedTextProvider( MaskedTextProvider newProvider, string textOnInitializingMask ) { Debug.Assert( newProvider != null, "Initializing from a null MaskProvider ref." ); // Set R/W properties. newProvider.IncludePrompt = this.maskedTextProvider.IncludePrompt; newProvider.IncludeLiterals = this.maskedTextProvider.IncludeLiterals; newProvider.SkipLiterals = this.maskedTextProvider.SkipLiterals; newProvider.ResetOnPrompt = this.maskedTextProvider.ResetOnPrompt; newProvider.ResetOnSpace = this.maskedTextProvider.ResetOnSpace; // If mask not initialized and not initializing it, the new provider is just a property backend. // Change won't have any effect in text. if( this.flagState[IS_NULL_MASK] && textOnInitializingMask == null) { this.maskedTextProvider = newProvider; return; } int testPos = 0; bool raiseOnMaskInputRejected = false; // Raise if new provider rejects old text. MaskedTextResultHint hint = MaskedTextResultHint.NoEffect; MaskedTextProvider oldProvider = this.maskedTextProvider; // Attempt to add previous text. // If the mask is the same, we need to preserve the caret and character positions if the text is added successfully. bool preserveCharPos = oldProvider.Mask == newProvider.Mask; // Cache text output text before setting the new provider to determine whether we need to raise the TextChanged event. string oldText; // NOTE: Whenever changing the MTP, the text is lost if any character in the old text violates the new provider's mask. if( textOnInitializingMask != null ) // Changing Mask (from null), which is the only RO property that requires passing text. { oldText = textOnInitializingMask; raiseOnMaskInputRejected = !newProvider.Set( textOnInitializingMask, out testPos, out hint ); } else { oldText = TextOutput; // We need to attempt to set the input characters one by one in the edit positions so they are not // escaped. int assignedCount = oldProvider.AssignedEditPositionCount; int srcPos = 0; int dstPos = 0; while( assignedCount > 0 ) { srcPos = oldProvider.FindAssignedEditPositionFrom( srcPos, forward ); Debug.Assert( srcPos != MaskedTextProvider.InvalidIndex, "InvalidIndex unexpected at this time." ); if (preserveCharPos) { dstPos = srcPos; } else { dstPos = newProvider.FindEditPositionFrom(dstPos, forward); if (dstPos == MaskedTextProvider.InvalidIndex) { newProvider.Clear(); testPos = newProvider.Length; hint = MaskedTextResultHint.UnavailableEditPosition; break; } } if( !newProvider.Replace( oldProvider[srcPos], dstPos, out testPos, out hint )) { preserveCharPos = false; newProvider.Clear(); break; } srcPos++; dstPos++; assignedCount--; } raiseOnMaskInputRejected = !MaskedTextProvider.GetOperationResultFromHint(hint); } // Set provider. this.maskedTextProvider = newProvider; if( this.flagState[IS_NULL_MASK] ) { this.flagState[IS_NULL_MASK] = false; } // Raising events need to be done only after the new provider has been set so the MTB is in a state where properties // can be queried from event handlers safely. if( raiseOnMaskInputRejected ) { OnMaskInputRejected(new MaskInputRejectedEventArgs(testPos, hint)); } if( newProvider.IsPassword ) { // Reset native edit control so the MaskedTextBox will take control over the characters that // need to be replaced with the password char (the input text characters). // MTB takes over. SetEditControlPasswordChar('\0'); } EventArgs e = EventArgs.Empty; if (textOnInitializingMask != null /*changing mask from null*/ || oldProvider.Mask != newProvider.Mask) { OnMaskChanged(e); } SetWindowText(GetFormattedDisplayString(), oldText != TextOutput, preserveCharPos); } ////// Sets the control's text to the formatted text obtained from the underlying MaskedTextProvider. /// TextChanged is raised always, this assumes the display or the output text changed. /// The caret position is lost (unless cached somewhere else like when lossing the focus). /// This is the common way of changing the text in the control. /// private void SetText() { SetWindowText(GetFormattedDisplayString(), true, false); } ////// Sets the control's text to the formatted text obtained from the underlying MaskedTextProvider. /// TextChanged is not raised. [PasswordChar] /// The caret position is preserved. /// private void SetWindowText() { SetWindowText(GetFormattedDisplayString(), false, true); } ////// Sets the text directly in the underlying edit control to the value specified. /// The 'raiseTextChangedEvent' param determines whether TextChanged event is raised or not. /// The 'preserveCaret' param determines whether an attempt to preserve the caret position should be made or not /// after the call to SetWindowText (WindowText) is performed. /// private void SetWindowText(string text, bool raiseTextChangedEvent, bool preserveCaret) { this.flagState[QUERY_BASE_TEXT] = true; try { if( preserveCaret ) { this.caretTestPos = this.SelectionStart; } WindowText = text; // this calls Win32::SetWindowText directly, no OnTextChanged raised. if( raiseTextChangedEvent ) { OnTextChanged(EventArgs.Empty); } if( preserveCaret ) { this.SelectionStart = this.caretTestPos; } } finally { this.flagState[QUERY_BASE_TEXT] = false; } } ////// Designe time support for checking if Culture value in the designer should be serialized. /// private bool ShouldSerializeCulture() { return !CultureInfo.CurrentCulture.Equals(this.Culture); } ////// Undoes the last edit operation in the text box. /// Unsupported property/method. /// WndProc ignores EM_UNDO. /// [ EditorBrowsable(EditorBrowsableState.Never) ] public new void Undo() { } ////// Forces type validation. Returns the validated text value. /// public object ValidateText() { return PerformTypeValidation(null); } ////// Deletes all input characters in the current selection. /// private bool WmClear() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if( !this.ReadOnly ) { int selStart, selLength; base.GetSelectionStartAndLength( out selStart, out selLength ); Delete(Keys.Delete, selStart, selLength); return true; } return false; } ////// Copies current selection text to the clipboard, formatted according to the IncludeLiterals properties but /// ignoring the prompt character. /// Returns true if the operation succeeded, false otherwise. /// private bool WmCopy() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if (this.maskedTextProvider.IsPassword) // cannot copy password to clipboard. { return false; } string text = GetSelectedText(); try { // SECREVIEW : Copy needs to work even if the OwnClipboard permission isn't available. We need to assert here. // This assert is ok, observe that if the control is in password mode it won't reach this point, // see code above. // Be careful if code is added after the security assert, you may need to put it in a try-finally // block to revert the security assert. IntSecurity.ClipboardWrite.Assert(); if (text.Length == 0) { Clipboard.Clear(); } else { Clipboard.SetText(text); } } catch (Exception ex) { // Note: Sometimes the above operation throws but it successfully sets the // data in the clipboard. This usually happens when the Application's Main // is not attributed with [STAThread]. if (ClientUtils.IsSecurityOrCriticalException(ex)) { throw; } } return true; } ////// Processes the WM_IME_COMPOSITION message when using Korean IME. /// Korean IME uses the control's caret as the composition string (it processes only one character at a time), /// we need to have special message handling for it. /// Returns true if the message is handled, false otherwise. /// private bool WmImeComposition(ref Message m) { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); #if DEBUG if (this.ReadOnly || this.maskedTextProvider.IsPassword) { // This should have been already handled by the ReadOnly, PasswordChar and ImeMode properties. Debug.Assert(this.ImeMode == ImeMode.Disable, "IME enabled when in RO or Pwd mode."); } #endif // Non-Korean IMEs complete compositon when all characters in the string has been composed (when user hits enter); // Currently, we don't support checking the composition string characters because it would require similar logic // as the MaskedTextBox itself. if (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable) { byte imeConvertionType = imeConvertionNone; // Check if there's an update to the compositon string: if ((m.LParam.ToInt32() & NativeMethods.GCS_COMPSTR) != 0) { // The character in the composition has been updated but not yet converted. imeConvertionType = imeConvertionUpdate; } else if ((m.LParam.ToInt32() & NativeMethods.GCS_RESULTSTR) != 0) { // The character(s) in the composition has been fully converted. imeConvertionType = imeConvertionCompleted; } // Process any update in the composition string. if (imeConvertionType != imeConvertionNone) { if (this.flagState[IME_ENDING_COMPOSITION]) { // If IME is completing the convertion, we don't want to process further characters. return this.flagState[IME_COMPLETING]; } } } return false; //message not handled. } ////// Processes the WM_IME_STARTCOMPOSITION message. /// Returns true if the message is handled, false otherwise. /// private bool WmImeStartComposition() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); // Position the composition window in a valid place. int startPosition, selectionLen; base.GetSelectionStartAndLength( out startPosition, out selectionLen ); int startEditPos = this.maskedTextProvider.FindEditPositionFrom( startPosition, forward ); if( startEditPos != MaskedTextProvider.InvalidIndex ) { if (selectionLen > 0 && (ImeModeConversion.InputLanguageTable == ImeModeConversion.KoreanTable)) { // Korean IME: We need to delete the selected text and reposition the caret so the IME processes one // character only, otherwise it would overwrite the selection with the caret (composition string), // deleting a portion of the mask. int endEditPos = this.maskedTextProvider.FindEditPositionFrom(startPosition + selectionLen - 1, backward); if (endEditPos >= startEditPos) { selectionLen = endEditPos - startEditPos + 1; Delete(Keys.Delete, startEditPos, selectionLen); } else { ImeComplete(); OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); return true; } } // update caret position. if( startPosition != startEditPos ) { this.caretTestPos = startEditPos; this.SelectionStart = this.caretTestPos; } this.SelectionLength = 0; } else { ImeComplete(); OnMaskInputRejected(new MaskInputRejectedEventArgs(startPosition, MaskedTextResultHint.UnavailableEditPosition)); return true; } return false; } ////// Processes the WM_PASTE message. Copies the text from the clipboard, if is valid, /// formatted according to the mask applied to this control. /// Returns true if the operation succeeded, false otherwise. /// private void WmPaste() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if( this.ReadOnly ) { return; } // Get the text from the clipboard. string text; try { IntSecurity.ClipboardRead.Assert(); text = Clipboard.GetText(); } catch (Exception ex) { if (ClientUtils.IsSecurityOrCriticalException(ex)) { throw; } Debug.Fail(ex.ToString()); return; } PasteInt( text ); } private void WmPrint(ref Message m) { base.WndProc(ref m); if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { IntSecurity.UnmanagedCode.Assert(); try { using (Graphics g = Graphics.FromHdc(m.WParam)) { Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); rect.Inflate(-1, -1); g.DrawRectangle(SystemPens.Window, rect); } } finally { CodeAccessPermission.RevertAssert(); } } } ////// We need to override the WndProc method to have full control over what characters can be /// displayed in the text box; particularly, we have special handling when IME is turned on. /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { // Handle messages for special cases (unsupported operations or cases where mask doesn not matter). switch (m.Msg) { case NativeMethods.WM_PRINT: WmPrint(ref m); return; case NativeMethods.WM_CONTEXTMENU: case NativeMethods.EM_CANUNDO: base.ClearUndo(); // resets undo buffer. base.WndProc(ref m); return; case NativeMethods.EM_SCROLLCARET: // No scroll for single-line control. case NativeMethods.EM_LIMITTEXT: // Max/Min text is defined by the mask. case NativeMethods.EM_UNDO: case NativeMethods.WM_UNDO: return; default: break; // continue. } if( this.flagState[IS_NULL_MASK]) { base.WndProc(ref m); // Operates as a regular text box base. return; } switch (m.Msg) { case NativeMethods.WM_IME_STARTCOMPOSITION: if( WmImeStartComposition() ) { break; } goto default; case NativeMethods.WM_IME_ENDCOMPOSITION: this.flagState[IME_ENDING_COMPOSITION] = true; goto default; case NativeMethods.WM_IME_COMPOSITION: if( WmImeComposition( ref m ) ) { break; } goto default; case NativeMethods.WM_CUT: if (!this.ReadOnly && WmCopy()) { WmClear(); } break; case NativeMethods.WM_COPY: WmCopy(); break; case NativeMethods.WM_PASTE: WmPaste(); break; case NativeMethods.WM_CLEAR: WmClear(); break; case NativeMethods.WM_KILLFOCUS: base.WndProc(ref m); WmKillFocus(); break; case NativeMethods.WM_SETFOCUS: WmSetFocus(); base.WndProc(ref m); break; default: base.WndProc(ref m); break; } } ////// Processes the WM_KILLFOCUS message. Updates control's text replacing promp chars with space. /// private void WmKillFocus() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); base.GetSelectionStartAndLength( out this.caretTestPos, out this.lastSelLength ); if (this.HidePromptOnLeave && !this.MaskFull) { SetWindowText(); // Update text w/ no prompt. // We need to update selection info in case the control is queried for it while it doesn't have the focus. base.SelectInternal( this.caretTestPos, this.lastSelLength, this.maskedTextProvider.Length ); } } ////// Processes the WM_SETFOCUS message. Updates control's text with formatted text according to /// the include prompt property. /// private void WmSetFocus() { Debug.Assert( !this.flagState[IS_NULL_MASK], "This method must be called when a Mask is provided." ); if (this.HidePromptOnLeave && !this.MaskFull) // Prompt will show up. { SetWindowText(); } // Restore previous selection. Do this always (as opposed to within the condition above as in WmKillFocus) // because HidePromptOnLeave could have changed while the control did not have the focus. base.SelectInternal( this.caretTestPos, this.lastSelLength, this.maskedTextProvider.Length ); } } } // 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
- DataService.cs
- ShapingWorkspace.cs
- XmlIncludeAttribute.cs
- NeedSkipTokenVisitor.cs
- WebDisplayNameAttribute.cs
- StyleBamlRecordReader.cs
- PeerNearMe.cs
- ContentDisposition.cs
- DateTimeConverter2.cs
- CrossContextChannel.cs
- ResXResourceWriter.cs
- ResXDataNode.cs
- SchemaImporterExtension.cs
- OutputCacheEntry.cs
- QueryExtender.cs
- LambdaCompiler.Generated.cs
- DesignerAutoFormatStyle.cs
- SerialReceived.cs
- SafeArrayRankMismatchException.cs
- StaticFileHandler.cs
- ImageListImageEditor.cs
- OledbConnectionStringbuilder.cs
- Graphics.cs
- MasterPage.cs
- ZipFileInfoCollection.cs
- ConstraintCollection.cs
- LineGeometry.cs
- PingOptions.cs
- Drawing.cs
- SettingsPropertyNotFoundException.cs
- ExpandableObjectConverter.cs
- LinkTarget.cs
- DataGrid.cs
- AsymmetricKeyExchangeDeformatter.cs
- DataMemberConverter.cs
- RuleEngine.cs
- IncrementalCompileAnalyzer.cs
- Size.cs
- PartialTrustVisibleAssemblyCollection.cs
- CompilationUnit.cs
- X500Name.cs
- SafeSystemMetrics.cs
- indexingfiltermarshaler.cs
- PointCollectionValueSerializer.cs
- ClrPerspective.cs
- Util.cs
- DateTimeFormatInfo.cs
- MergeFilterQuery.cs
- CompilationUtil.cs
- AnnotationStore.cs
- EmptyStringExpandableObjectConverter.cs
- TypedElement.cs
- WsdlBuildProvider.cs
- ContextMarshalException.cs
- AnimatedTypeHelpers.cs
- DataGridViewRowErrorTextNeededEventArgs.cs
- VirtualDirectoryMappingCollection.cs
- Memoizer.cs
- EntityModelBuildProvider.cs
- LogManagementAsyncResult.cs
- TraceContextEventArgs.cs
- FileLogRecordStream.cs
- AbsoluteQuery.cs
- CheckableControlBaseAdapter.cs
- UIElementIsland.cs
- ThrowHelper.cs
- ProxyHwnd.cs
- SqlMethodAttribute.cs
- DtdParser.cs
- MetadataArtifactLoader.cs
- DefaultAsyncDataDispatcher.cs
- RunInstallerAttribute.cs
- DataServiceKeyAttribute.cs
- UshortList2.cs
- SqlFunctionAttribute.cs
- HtmlFormAdapter.cs
- XmlAutoDetectWriter.cs
- CollectionEditor.cs
- ReadWriteObjectLock.cs
- ReadOnlyTernaryTree.cs
- HostTimeoutsElement.cs
- EventEntry.cs
- TypeSystem.cs
- SessionParameter.cs
- StaticResourceExtension.cs
- Point3DAnimationBase.cs
- EventMappingSettings.cs
- UnknownBitmapEncoder.cs
- TransportListener.cs
- HttpDebugHandler.cs
- Region.cs
- RightsManagementEncryptedStream.cs
- ClientBuildManagerCallback.cs
- SqlLiftWhereClauses.cs
- RequestCacheEntry.cs
- OLEDB_Util.cs
- ErrorInfoXmlDocument.cs
- ValidatingPropertiesEventArgs.cs
- GeometryDrawing.cs
- EventRecord.cs