913 lines
23 KiB
C#
Raw Normal View History

//
// MaskedTextBox.cs
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2007 Novell, Inc.
//
// Authors:
// Rolf Bjarne Kvinge (RKvinge@novell.com)
//
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
namespace System.Windows.Forms
{
[ClassInterface (ClassInterfaceType.AutoDispatch)]
[ComVisible (true)]
[DefaultProperty ("Mask")]
[DefaultEvent ("MaskInputRejected")]
[Designer ("System.Windows.Forms.Design.MaskedTextBoxDesigner, " + Consts.AssemblySystem_Design)]
[DefaultBindingProperty ("Text")]
public class MaskedTextBox : TextBoxBase
{
#region Locals
private MaskedTextProvider provider;
private bool beep_on_error;
private IFormatProvider format_provider;
private bool hide_prompt_on_leave;
private InsertKeyMode insert_key_mode;
private bool insert_key_overwriting;
private bool reject_input_on_first_failure;
private HorizontalAlignment text_align;
private MaskFormat cut_copy_mask_format;
private bool use_system_password_char;
private Type validating_type;
private bool is_empty_mask;
private bool setting_text;
#endregion
#region Events
static object AcceptsTabChangedEvent = new object ();
static object IsOverwriteModeChangedEvent = new object ();
static object MaskChangedEvent = new object ();
static object MaskInputRejectedEvent = new object ();
static object MultilineChangedEvent = new object ();
static object TextAlignChangedEvent = new object ();
static object TypeValidationCompletedEvent = new object ();
// This event is never raised by MaskedTextBox.
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler AcceptsTabChanged {
add { Events.AddHandler (AcceptsTabChangedEvent, value);}
remove { Events.RemoveHandler (AcceptsTabChangedEvent, value);}
}
public event EventHandler IsOverwriteModeChanged {
add { Events.AddHandler (IsOverwriteModeChangedEvent, value); }
remove { Events.RemoveHandler (IsOverwriteModeChangedEvent, value); }
}
public event EventHandler MaskChanged {
add { Events.AddHandler (MaskChangedEvent, value); }
remove { Events.RemoveHandler (MaskChangedEvent, value); }
}
public event MaskInputRejectedEventHandler MaskInputRejected {
add { Events.AddHandler (MaskInputRejectedEvent, value); }
remove { Events.RemoveHandler (MaskInputRejectedEvent, value); }
}
// This event is never raised by MaskedTextBox.
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler MultilineChanged {
add { Events.AddHandler (MultilineChangedEvent, value); }
remove { Events.RemoveHandler (MultilineChangedEvent, value); }
}
public event EventHandler TextAlignChanged {
add { Events.AddHandler (TextAlignChangedEvent, value); }
remove { Events.RemoveHandler (TextAlignChangedEvent, value); }
}
public event TypeValidationEventHandler TypeValidationCompleted {
add { Events.AddHandler (TypeValidationCompletedEvent, value); }
remove { Events.RemoveHandler (TypeValidationCompletedEvent, value); }
}
#endregion
#region Constructors
public MaskedTextBox ()
{
provider = new MaskedTextProvider ("<>", CultureInfo.CurrentCulture);
is_empty_mask = true;
Init ();
}
public MaskedTextBox (MaskedTextProvider maskedTextProvider)
{
if (maskedTextProvider == null) {
throw new ArgumentNullException ();
}
provider = maskedTextProvider;
is_empty_mask = false;
Init ();
}
public MaskedTextBox (string mask)
{
if (mask == null) {
throw new ArgumentNullException ();
}
provider = new MaskedTextProvider (mask, CultureInfo.CurrentCulture);
is_empty_mask = false;
Init ();
}
private void Init ()
{
BackColor = SystemColors.Window;
cut_copy_mask_format = MaskFormat.IncludeLiterals;
insert_key_overwriting = false;
UpdateVisibleText ();
}
#endregion
#region Public and protected methods
[EditorBrowsable (EditorBrowsableState.Never)]
public new void ClearUndo ()
{
// Do nothing, not supported by MTB
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
[UIPermission (SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows)]
protected override void CreateHandle ()
{
base.CreateHandle ();
}
public override char GetCharFromPosition (Point pt)
{
return base.GetCharFromPosition (pt);
}
public override int GetCharIndexFromPosition (Point pt)
{
return base.GetCharIndexFromPosition (pt);
}
[EditorBrowsable (EditorBrowsableState.Never)]
public new int GetFirstCharIndexFromLine (int lineNumber)
{
return 0;
}
[EditorBrowsable (EditorBrowsableState.Never)]
public new int GetFirstCharIndexOfCurrentLine ()
{
return 0;
}
[EditorBrowsable (EditorBrowsableState.Never)]
public override int GetLineFromCharIndex (int index)
{
return 0;
}
public override Point GetPositionFromCharIndex (int index)
{
return base.GetPositionFromCharIndex (index);
}
protected override bool IsInputKey (Keys keyData)
{
return base.IsInputKey (keyData);
}
protected override void OnBackColorChanged (EventArgs e)
{
base.OnBackColorChanged (e);
}
protected override void OnHandleCreated (EventArgs e)
{
base.OnHandleCreated (e);
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected virtual void OnIsOverwriteModeChanged (EventArgs e)
{
EventHandler eh = (EventHandler) Events [IsOverwriteModeChangedEvent];
if (eh != null)
eh (this, e);
}
protected override void OnKeyDown (KeyEventArgs e)
{
// Only handle Delete or Insert here
if (e.KeyCode == Keys.Insert && insert_key_mode == InsertKeyMode.Default) {
// switch the internal overwriting mode, not the public one
insert_key_overwriting = !insert_key_overwriting;
OnIsOverwriteModeChanged (EventArgs.Empty);
e.Handled = true;
return;
}
if (e.KeyCode != Keys.Delete || is_empty_mask) {
base.OnKeyDown (e);
return;
}
int testPosition, endSelection;
MaskedTextResultHint resultHint;
bool result;
// Use a slightly different approach than the one used for backspace
endSelection = SelectionLength == 0 ? SelectionStart : SelectionStart + SelectionLength - 1;
result = provider.RemoveAt (SelectionStart, endSelection, out testPosition, out resultHint);
PostprocessKeyboardInput (result, testPosition, testPosition, resultHint);
e.Handled = true;
}
protected override void OnKeyPress (KeyPressEventArgs e)
{
if (is_empty_mask) {
base.OnKeyPress (e);
return;
}
int testPosition, editPosition;
MaskedTextResultHint resultHint;
bool result;
if (e.KeyChar == '\b') {
if (SelectionLength == 0)
result = provider.RemoveAt (SelectionStart - 1, SelectionStart - 1, out testPosition, out resultHint);
else
result = provider.RemoveAt (SelectionStart, SelectionStart + SelectionLength - 1, out testPosition, out resultHint);
editPosition = testPosition;
} else if (IsOverwriteMode || SelectionLength > 0) { // Replace
int start = provider.FindEditPositionFrom (SelectionStart, true);
int end = SelectionLength > 0 ? SelectionStart + SelectionLength - 1 : start;
result = provider.Replace (e.KeyChar, start, end, out testPosition, out resultHint);
editPosition = testPosition + 1;
} else {
// Move chars to the right
result = provider.InsertAt (e.KeyChar, SelectionStart, out testPosition, out resultHint);
editPosition = testPosition + 1;
}
PostprocessKeyboardInput (result, editPosition, testPosition, resultHint);
e.Handled = true;
}
void PostprocessKeyboardInput (bool result, int newPosition, int testPosition, MaskedTextResultHint resultHint)
{
if (!result)
OnMaskInputRejected (new MaskInputRejectedEventArgs (testPosition, resultHint));
else {
if (newPosition != MaskedTextProvider.InvalidIndex)
SelectionStart = newPosition;
else
SelectionStart = provider.Length;
UpdateVisibleText ();
}
}
protected override void OnKeyUp (KeyEventArgs e)
{
base.OnKeyUp (e);
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected virtual void OnMaskChanged (EventArgs e)
{
EventHandler eh = (EventHandler) Events [MaskChangedEvent];
if (eh != null)
eh (this, e);
}
private void OnMaskInputRejected (MaskInputRejectedEventArgs e)
{
MaskInputRejectedEventHandler eh = (MaskInputRejectedEventHandler) Events [MaskInputRejectedEvent];
if (eh != null)
eh (this, e);
}
[EditorBrowsable (EditorBrowsableState.Never)]
protected override void OnMultilineChanged (EventArgs e)
{
EventHandler eh = (EventHandler)Events [MultilineChangedEvent];
if (eh != null)
eh (this, e);
}
protected virtual void OnTextAlignChanged (EventArgs e)
{
EventHandler eh = (EventHandler)Events [TextAlignChangedEvent];
if (eh != null)
eh (this, e);
}
protected override void OnTextChanged (EventArgs e)
{
base.OnTextChanged (e);
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected override void OnValidating (CancelEventArgs e)
{
base.OnValidating (e);
}
//[SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
{
return base.ProcessCmdKey (ref msg, keyData);
}
//[SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected internal override bool ProcessKeyMessage (ref Message m)
{
return base.ProcessKeyMessage (ref m);
}
[EditorBrowsable (EditorBrowsableState.Never)]
public new void ScrollToCaret ()
{
// MSDN: this method is overridden to perform no actions.
}
public override string ToString ()
{
return base.ToString () + ", Text: " + provider.ToString (false, false);
}
[EditorBrowsable (EditorBrowsableState.Never)]
public new void Undo ()
{
// Do nothing, not supported by MTB.
}
public object ValidateText ()
{
throw new NotImplementedException ();
}
//[SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc (ref Message m)
{
switch ((Msg) m.Msg) {
default:
base.WndProc (ref m);
return;
}
}
#endregion
#region Properties
[EditorBrowsable (EditorBrowsableState.Never)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
public new bool AcceptsTab {
get {
return false;
}
set {
}
}
[DefaultValue (true)]
public bool AllowPromptAsInput {
get {
return provider.AllowPromptAsInput;
}
set {
provider = new MaskedTextProvider (provider.Mask, provider.Culture, value, provider.PromptChar, provider.PasswordChar, provider.AsciiOnly);
UpdateVisibleText ();
}
}
[RefreshProperties (RefreshProperties.Repaint)]
[DefaultValue (false)]
public bool AsciiOnly {
get {
return provider.AsciiOnly;
}
set {
provider = new MaskedTextProvider (provider.Mask, provider.Culture, provider.AllowPromptAsInput, provider.PromptChar, provider.PasswordChar, value);
UpdateVisibleText ();
}
}
[DefaultValue (false)]
public bool BeepOnError {
get {
return beep_on_error;
}
set {
beep_on_error = value;
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new bool CanUndo {
get {
return false;
}
}
protected override CreateParams CreateParams {
//[SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
get {
return base.CreateParams;
}
}
[RefreshProperties (RefreshProperties.Repaint)]
public CultureInfo Culture {
get {
return provider.Culture;
}
set {
provider = new MaskedTextProvider (provider.Mask, value, provider.AllowPromptAsInput, provider.PromptChar, provider.PasswordChar, provider.AsciiOnly);
UpdateVisibleText ();
}
}
[RefreshProperties (RefreshProperties.Repaint)]
[DefaultValue (MaskFormat.IncludeLiterals)]
public MaskFormat CutCopyMaskFormat {
get {
return cut_copy_mask_format;
}
set {
if (!Enum.IsDefined (typeof (MaskFormat), value)) {
throw new InvalidEnumArgumentException ("value", (int)value, typeof (MaskFormat));
}
cut_copy_mask_format = value;
}
}
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
public IFormatProvider FormatProvider {
get {
return format_provider;
}
set {
format_provider = value;
}
}
[RefreshProperties (RefreshProperties.Repaint)]
[DefaultValue (false)]
public bool HidePromptOnLeave {
get {
return hide_prompt_on_leave;
}
set {
hide_prompt_on_leave = value;
}
}
[DefaultValue (InsertKeyMode.Default)]
public InsertKeyMode InsertKeyMode {
get {
return insert_key_mode;
}
set {
if (!Enum.IsDefined (typeof (InsertKeyMode), value)) {
throw new InvalidEnumArgumentException ("value", (int)value, typeof (InsertKeyMode));
}
insert_key_mode = value;
}
}
[Browsable (false)]
public bool IsOverwriteMode {
get {
if (insert_key_mode == InsertKeyMode.Default) {
return insert_key_overwriting;
} else {
return insert_key_mode == InsertKeyMode.Overwrite;
}
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
public new string [] Lines {
get {
string text = Text;
if (text == null || text == string.Empty)
return new string [] {};
return Text.Split (new string [] {"\r\n", "\r", "\n"}, StringSplitOptions.None);
}
set {
// Do nothing, not supported by MTB.
}
}
[Localizable (true)]
[Editor ("System.Windows.Forms.Design.MaskPropertyEditor, " + Consts.AssemblySystem_Design, typeof (UITypeEditor))]
[RefreshProperties (RefreshProperties.Repaint)]
[MergablePropertyAttribute (false)]
[DefaultValue ("")]
public string Mask {
get {
if (is_empty_mask)
return string.Empty;
return provider.Mask;
}
set {
is_empty_mask = (value == string.Empty || value == null);
if (is_empty_mask) {
value = "<>";
}
provider = new MaskedTextProvider (value, provider.Culture, provider.AllowPromptAsInput, provider.PromptChar, provider.PasswordChar, provider.AsciiOnly);
ReCalculatePasswordChar ();
UpdateVisibleText ();
}
}
[Browsable (false)]
public bool MaskCompleted {
get {
return provider.MaskCompleted;
}
}
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public MaskedTextProvider MaskedTextProvider {
get {
if (is_empty_mask)
return null;
return provider.Clone () as MaskedTextProvider;
}
}
[Browsable (false)]
public bool MaskFull {
get {
return provider.MaskFull;
}
}
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public override int MaxLength {
get {
return base.MaxLength;
}
set {
// Do nothing, MTB doesn't support this.
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public override bool Multiline {
get {
return false;
}
set {
// Do nothing, MTB doesn't support this.
}
}
[RefreshProperties (RefreshProperties.Repaint)]
[DefaultValue ('\0')]
public char PasswordChar {
get {
if (use_system_password_char)
return '*';
return provider.PasswordChar;
}
set {
provider.PasswordChar = value;
if (value != '\0') {
provider.IsPassword = true;
} else
provider.IsPassword = false;
ReCalculatePasswordChar (true);
CalculateDocument ();
UpdateVisibleText ();
}
}
[Localizable (true)]
[DefaultValue ('_')]
[RefreshProperties (RefreshProperties.Repaint)]
public char PromptChar {
get {
return provider.PromptChar;
}
set {
provider.PromptChar = value;
UpdateVisibleText ();
}
}
public new bool ReadOnly {
get {
return base.ReadOnly;
}
set {
base.ReadOnly = value;
}
}
[DefaultValue (false)]
public bool RejectInputOnFirstFailure {
get {
return reject_input_on_first_failure;
}
set {
reject_input_on_first_failure = value;
}
}
[DefaultValue (true)]
public bool ResetOnPrompt {
get {
return provider.ResetOnPrompt;
}
set {
provider.ResetOnPrompt = value;
}
}
[DefaultValue (true)]
public bool ResetOnSpace {
get {
return provider.ResetOnSpace;
}
set {
provider.ResetOnSpace = value;
}
}
public override string SelectedText {
get {
return base.SelectedText;
}
set {
base.SelectedText = value;
UpdateVisibleText ();
}
}
[DefaultValue (true)]
public bool SkipLiterals {
get {
return provider.SkipLiterals;
}
set {
provider.SkipLiterals = value;
}
}
[Bindable (true)]
[Editor ("System.Windows.Forms.Design.MaskedTextBoxTextEditor, " + Consts.AssemblySystem_Design, typeof (UITypeEditor))]
[Localizable (true)]
[RefreshProperties (RefreshProperties.Repaint)]
[DefaultValue ("")]
public override string Text {
get {
if (is_empty_mask || setting_text)
return base.Text;
// The base constructor may call Text before we get to create a provider,
// so it may be null even though it's not an empty mask.
if (provider == null)
return string.Empty;
return provider.ToString ();
}
set {
if (is_empty_mask) {
setting_text = true;
base.Text = value;
setting_text = false;
} else {
InputText (value);
}
UpdateVisibleText ();
}
}
[DefaultValue (HorizontalAlignment.Left)]
[Localizable (true)]
public HorizontalAlignment TextAlign {
get {
return text_align;
}
set {
if (text_align != value) {
if (!Enum.IsDefined (typeof (HorizontalAlignment), value)) {
throw new InvalidEnumArgumentException ("value", (int) value, typeof (HorizontalAlignment));
}
text_align = value;
OnTextAlignChanged (EventArgs.Empty);
}
}
}
[Browsable (false)]
public override int TextLength {
get {
return Text.Length;
}
}
[DefaultValue (MaskFormat.IncludeLiterals)]
[RefreshProperties (RefreshProperties.Repaint)]
public MaskFormat TextMaskFormat {
get {
if (provider.IncludePrompt && provider.IncludeLiterals) {
return MaskFormat.IncludePromptAndLiterals;
} else if (provider.IncludeLiterals) {
return MaskFormat.IncludeLiterals;
} else if (provider.IncludePrompt) {
return MaskFormat.IncludePrompt;
} else {
return MaskFormat.ExcludePromptAndLiterals;
}
}
set {
if (!Enum.IsDefined (typeof (MaskFormat), value)) {
throw new InvalidEnumArgumentException ("value", (int)value, typeof (MaskFormat));
}
provider.IncludeLiterals = (value & MaskFormat.IncludeLiterals) == MaskFormat.IncludeLiterals;
provider.IncludePrompt = (value & MaskFormat.IncludePrompt) == MaskFormat.IncludePrompt;
}
}
[DefaultValue (false)]
[RefreshProperties (RefreshProperties.Repaint)]
public bool UseSystemPasswordChar {
get {
return use_system_password_char;
}
set {
if (use_system_password_char != value) {
use_system_password_char = value;
if (use_system_password_char)
PasswordChar = PasswordChar;
else
PasswordChar = '\0';
}
}
}
[DefaultValue (null)]
[Browsable (false)]
public Type ValidatingType {
get {
return validating_type;
}
set {
validating_type = value;
}
}
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new bool WordWrap {
get {
return false;
}
set {
// Do nothing, not supported by MTB
}
}
#endregion
#region Internal and private members
private void ReCalculatePasswordChar ()
{
ReCalculatePasswordChar (PasswordChar != '\0');
}
private void ReCalculatePasswordChar (bool using_password)
{
if (using_password)
if (is_empty_mask)
document.PasswordChar = PasswordChar.ToString ();
else
document.PasswordChar = string.Empty;
}
internal override void OnPaintInternal (PaintEventArgs pevent)
{
base.OnPaintInternal (pevent);
//pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
//TextRenderer.DrawText (pevent.Graphics, Text, Font, ClientRectangle, ForeColor, TextFormatFlags.SingleLine);
//pevent.Handled = true;
}
internal override Color ChangeBackColor (Color backColor)
{
return backColor;
}
private void UpdateVisibleText ()
{
string text = null;
if (is_empty_mask || setting_text)
text = base.Text;
else
if (provider == null)
text = string.Empty;
else
text = provider.ToDisplayString ();
setting_text = true;
if (base.Text != text) {
int selstart = SelectionStart;
base.Text = text;
SelectionStart = selstart;
}
setting_text = false;
}
private void InputText (string text)
{
string input = text;
int testPosition;
MaskedTextResultHint resultHint;
bool result;
if (RejectInputOnFirstFailure) {
result = provider.Set (input, out testPosition, out resultHint);
if (!result)
OnMaskInputRejected (new MaskInputRejectedEventArgs (testPosition, resultHint));
} else {
provider.Clear ();
testPosition = 0;
// Unfortunately we can't break if we reach the end of the mask, since
// .net iterates over _all_ the chars in the input
for (int i = 0; i < input.Length; i++) {
char c = input [i];
result = provider.InsertAt (c, testPosition, out testPosition, out resultHint);
if (result)
testPosition++; // Move to the next free position
else
OnMaskInputRejected (new MaskInputRejectedEventArgs (testPosition, resultHint));
}
}
}
#endregion
}
}