Click here to Skip to main content
Click here to Skip to main content

DataGridViewColumn Hosting MaskedTextBox

, 12 May 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
How to host a MaskedTextBox in a DataGridViewColumn

Introduction

You want to use a MaskedTextBox in your DataGridViewColumn? Here it is. Add a DataGridViewMaskedTextColumn to your DataGridView and set the Mask string. Then you get a MaskedTextBox using this Mask as EditingControl.

Background

The .NET Framework contains for DataGridView columns a standard TextBox only. By data binding, it's no problem to restrict the input to numbers, e.g. TextBox.CharacterCasing can be set by DataGridView.EditingControlShowing event. More complex restrictions are available in MaskedTextBox. Therefore it's useful to host a MaskedTextBox in a DataGridViewColumn.

Yildirim Kocdag's solution Generic DataGridView (updated 24th January, 2008) contains a MaskedTextColumn, but there's a problem if one needs to add two (or more) MaskedTextColumns with different Mask strings: In this case, the control keeps one mask only because the DataGridView contains one specific EditingControl for all columns.

Microsoft's example How to: Host Controls in Windows Forms DataGridView Cells gives a simple way how to create your own DataGridViewColumn class. But it's insufficient because the Mask property needs to implement the Clone method, while the example doesn't need to. I found my solution after I studied all the original DataGridView classes using Lutz Roeder's Reflector.

Using the Code

The MaskedTextColumn solution consists of three combined classes, added by an attribute class.

The ReferencedDescriptionAttribute class is used in the designer to call an existing description. See details here.

The MaskedText classes are included into your own ExtendedControls.dll assembly, or you store the prepared DLL directly. Add a reference to this assembly, and you can use the DataGridViewMaskedTextColumn. You need to use this class only; the DataGridViewMaskedTextCell class and the DataGridViewMaskedTextControl class are used automatically by the framework itself.

Working by Code

Add a DataGridViewMaskedTextColumn to your DataGridView like any other DataGridViewColumn and set the Mask property:

using JThomas.Controls;

//  create a new column and set a mask using a minimum of 4 and a maximum of 7 digits
DataGridViewMaskedTextColumn column = new DataGridViewMaskedTextColumn("0000999");
column.DataPropertyName = "Number";
column.HeaderText = "Nummer";     //  German text
column.Name = "column";
column.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
column.Width = 70;

//  add this column to the DataGridView dgv
dgv.Columns.Add(column);

Working by Designer

After adding the reference to the ExtendedControls.dll assembly, you can open the IDE and select a DataGridViewMaskedTextColumn as DataGridViewColumn – either in the Edit Columns window, like in the image above, or in the Add Column window. There you can set the Mask string.

Mask Details

Check all Mask possibilities in the SDK documentation for the MaskedTextBox.Mask property.

How It Works

As shown in Microsoft's example, we have to define three classes.

DataGridViewMaskedTextColumn

This class is derived from DataGridViewColumn in the same way as the original DataGridViewTextBoxColumn class. We must implement our own derivation because the MaxInputLength property is not valid in MaskedTextBox instances. It contains the standard constructor and another constructor setting the Mask string directly:

//  use the Bitmap of MaskedTextBox
[System.Drawing.ToolboxBitmap(typeof(System.Windows.Forms.MaskedTextBox))]
public class DataGridViewMaskedTextColumn : DataGridViewColumn
{
        public DataGridViewMaskedTextColumn() : this(String.Empty)   {}

        public DataGridViewMaskedTextColumn(string maskString)
                : base(new DataGridViewMaskedTextCell())
        {
                SortMode = DataGridViewColumnSortMode.Automatic;
                Mask = maskString;
        }

Some methods and properties are derived like in DataGridViewTextBoxColumn class:

public override string ToString() { ... }
public override DataGridViewCell CellTemplate { get; set; }
public new DataGridViewColumnSortMode SortMode { get; set; }
private DataGridViewMaskedTextCell MaskedTextCellTemplate { get; }

We must add the Clone method for using the Mask property:

public override object Clone()
{
    DataGridViewMaskedTextColumn col = (DataGridViewMaskedTextColumn)base.Clone();
    col.Mask = Mask;
    col.CellTemplate = (DataGridViewMaskedTextCell)this.CellTemplate.Clone();
    return col;
}

The main work is to use the Mask string in each DataGridViewMaskedTextCell of this column. Therefore we may not only define a simple getter and setter, but must store the value in each contained DataGridViewCell like DataGridViewTextBoxColumn does for its MaxInputLength property.

[Category("Masking")]
//  use the MaskedTextBox.Mask description
[JThomas.Extensions.ReferencedDescription(typeof
    (System.Windows.Forms.MaskedTextBox),"Mask")]
public string Mask
{
    get     { ... }
    set     {
    if (Mask != value)
    {
        MaskedTextCellTemplate.Mask = value;
        if (base.DataGridView != null)
        {
            DataGridViewRowCollection rows = base.DataGridView.Rows;
            int count = rows.Count;
            for (int i = 0; i < count; i++)
            {
                DataGridViewMaskedTextCell cell
                    = rows.SharedRow(i).Cells[base.Index]
                    as DataGridViewMaskedTextCell;
                if (cell != null)
                    cell.Mask = value;
            }
        }
    }
    }
}

DataGridViewMaskedTextCell

This class can be derived from the DataGridViewTextBoxCell class because the behaviour in an inactive cell doesn't differ. It's only necessary to override some methods and properties and adapt to the current classes and names. The main work is to add the Clone method and the Mask property in a simple way.

public override object Clone()
{
    DataGridViewMaskedTextCell cell = base.Clone() as DataGridViewMaskedTextCell;
    cell.Mask = this.Mask;
    return cell;
}

private string mask;

public string Mask {
    get { return mask == null ? String.Empty : mask; }
    set { mask = value; }
}

DataGridViewMaskedTextControl

This class must be derived from the MaskedTextBox class implementing the IDataGridViewEditingControl interface. Most members are adapted in the same way like Microsoft's example does. It's only necessary to use string values instead of object and MaskedTextBox.Text property instead of DateTimePicker.Value property.

The most important definitions are the following ones:

    public object EditingControlFormattedValue
    {
        get    {    return Text;        }
        set    {    if (value is string)
                Text = (string)value;
        }
    }

    public void ApplyCellStyleToEditingControl(
        DataGridViewCellStyle dataGridViewCellStyle)
    {
        Font = dataGridViewCellStyle.Font;
        //    get the current cell to use the specific mask string
        DataGridViewMaskedTextCell cell
            = dataGridView.CurrentCell as DataGridViewMaskedTextCell;
        if (cell != null) {
            Mask = cell.Mask;
        }
    }

    public void PrepareEditingControlForEdit(bool selectAll)
    {
        if (selectAll)
            SelectAll();
        else {
            SelectionStart = 0;
            SelectionLength = 0;
        }
    }

    // MaskedTextBox event
    protected override void OnTextChanged(System.EventArgs e)
    {
        base.OnTextChanged(e);
        EditingControlValueChanged = true;
        if (EditingControlDataGridView != null) {
            EditingControlDataGridView.CurrentCell.Value = Text;
        }
    }

Points of Interest

TextBox.CharacterCasing doesn't need a DataGridViewMaskedTextColumn, but can be embedded as follows:

void DgvEditingControlShowing
    (object sender, DataGridViewEditingControlShowingEventArgs e)
{
        if (e.Control is TextBox) {
                TextBox box = e.Control as TextBox;
                // example: 2<sup>nd</sup> column requires upper case
                box.CharacterCasing
                        = dgv.CurrentCell.ColumnIndex == 1
                        ? CharacterCasing.Upper
                        : CharacterCasing.Normal;
        }
}  

It would be possible to insert a DefaultValue property into the DataGridViewMaskedText solution. On the other side, it's useful to connect default values to data instances. You should better use DataColumn.DefaultValue property, or DataTable.TableNewRow event, or DataGridView.RowsAdded event, or BindingNavigator.AddNewItem.Click event, or something else like that.

History

  • 05/04/2008: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

J.Thomas

Germany Germany
No Biography provided

Comments and Discussions

 
GeneralBug found PinmemberDennis Betten10-Dec-08 0:11 
GeneralRe: Bug found PinmemberDennis Betten10-Dec-08 3:58 
GeneralRe: Bug found Pinmemberdebeysm12-Apr-11 2:31 
GeneralRe: Bug found Pinmemberdongvan21-Sep-11 10:25 
GeneralRe: Bug found PinmemberStacyOW22-Aug-14 5:16 
QuestionDifferences between this solution and MS MaskedTextBoxColumn? Pinmemberkaborka15-Jul-08 12:58 
AnswerRe: differences are not important PinmemberJ.Thomas16-Jul-08 5:21 
Generalterrible that theire still is no good inputmask in java and visual studio ... like theire was in clipper ... Pinmemberjckoenen20-Jun-08 14:59 
I have found a good articel voor inputmask in java ... its called textgroup.
 
Within a textgroup you have textfields.
every textfield has a number (first to last) so you know were to go after tab-key or down-key or after last-character is inputted Wink | ;-)
every textfield can have a mask (99/99/9999 for example for date)
every textfield can have a validator or array of validators. A date has 3 validaters .. 1=day, 2=month, 3=year
 
Stupid that Visual Studio 2008 / java etcetera has no control to make it easy to make such a textgroup.
 
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package a;
 
/**
*
* @author okt07
*/
/*
public class TextGroup {
 
}
*/
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;
 
/*
** Group multiple MaskTextFields and JLabels to act like
** a single component.
**
** 1) A MaskTextField is a customized JTextField.
** 2) The focus manager will only set focus on the first
** text field added to the group.
** 3 The up/down/right/left arrow keys can be used to
** move within the group.
** 4) The getText() method returns the text of all
** components added to the group.
** 5) The getData() method returns the text of the
** text fields added to the group.
*/
public class TextGroup extends JPanel implements KeyListener {
 
/**
*
*/
public final static int TAB_NEXT = 1;
public final static int TAB_PREVIOUS = -1;
public final static Border DEFAULT_BORDER = UIManager.getBorder("TextField.border");
public final static Color DEFAULT_BACKGROUND = UIManager.getColor("TextField.background");
public final static Font DEFAULT_FONT = new Font("monospaced", Font.PLAIN, 12);
public final static Color DEFAULT_MASK_COLOR = Color.gray;
public final static boolean DEFAULT_DISPLAY_PARTIAL_MASK = true;
private static Border labelBorder = new EmptyBorder(0, 5, 0, 5);
private String delimiters;
private Color maskColor = DEFAULT_MASK_COLOR;
private boolean displayPartialMask = DEFAULT_DISPLAY_PARTIAL_MASK;
private MaskTextField firstField;
private List maskTextFields;
private StringBuffer text = new StringBuffer(16);
 
/*
** Convenience constructors
*/
// yyyy/mm/dd, "/" maar ook: "(###)___-____", "()-" =3delimiters
public TextGroup(String mask, String delimiters) {
this(mask, delimiters, null, null);
}
 
TextGroup(String mask, String delimiters, FieldValidator validator) {
this(mask, delimiters, validator, null);
}
 
TextGroup(String mask, String delimiters, FieldValidator[] validators) {
this(mask, delimiters, null, validators);
}
 
/*
** Complete constructer
*/
TextGroup(String mask, String delimiters, FieldValidator validator, FieldValidator[] validators) {
this.delimiters = delimiters;
setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
setBorder(DEFAULT_BORDER);
setBackground(DEFAULT_BACKGROUND);
setFont(DEFAULT_FONT);
maskTextFields = new ArrayList();
 
// Parse the mask into mask text fields and labels
 
StringTokenizer st = new StringTokenizer(mask, delimiters, true);
int tokenCount = 0;
 
while (st.hasMoreTokens()) {
String token = st.nextToken();
 
if (delimiters.indexOf(token) == -1) {
FieldValidator fv = getNextValidator(tokenCount, validator, validators);
addTextField(token, fv);
tokenCount++;
} else {
addLabel(token);
}
}
}
 
/*
** When a single FieldValidator is available it is used for all text fields.
** When multiple FieldValidators are available use validator corresponding to current token.
*/
private FieldValidator getNextValidator(int tokenCount, FieldValidator validator, FieldValidator[] validators) {
if (validator != null) {
return validator;
}
 
if (validators != null && tokenCount < validators.length) {
return validators[tokenCount];
} else {
return null;
}
}
 
/*
** Create a mask text field and add it to the panel
*/
@SuppressWarnings("unchecked")
MaskTextField addTextField(String mask, FieldValidator validator) {
MaskTextField textField = new MaskTextField(mask.length(), mask);
// uncomment the following line when using JDK1.3
// textField.setFocusTraversable( false );
textField.setFont(null);
textField.setBorder(null);
textField.setOpaque(false);
textField.setForeground(null);
textField.setParent(this);
textField.setValidator(validator);
textField.addKeyListener(this);
//jk:
textField.setColumns(textField.getColumns());
System.out.println(textField.getColumns());
System.out.println(mask);
 
// First mask field created can accept focus from the Focus Manager
 
if (maskTextFields.size() == 0) {
textField.setFocusTraversable(true);
}
 
maskTextFields.add(textField);
 
super.add(textField);
return textField;
}
 
/*
** Create a label and add it to the panel
*/
public JLabel addLabel(String delimiter) {
JLabel label = new JLabel(delimiter);
super.add(label);
 
label.setBorder(labelBorder);
label.setFont(null);
 
return label;
}
 
/*
**
*/
public void setText(String delimitedData) {
// Parse out the data from the delimited string
 
StringTokenizer st = new StringTokenizer(delimitedData, delimiters, true);
int tokenCount = 0;
 
while (st.hasMoreTokens()) {
String token = st.nextToken();
 
if (delimiters.indexOf(token) == -1) {
if (tokenCount < maskTextFields.size()) {
setText(tokenCount, token);
}
 
tokenCount++;
}
}
}
 
public void setText(int position, String text) {
if (position < maskTextFields.size()) {
((MaskTextField) maskTextFields.get(position)).setText(text);
}
}
 
/*
** Return the text with delimiters
*/
public String getText() {
return getComponentText(true);
}
 
public String getText(int position) {
if (position < maskTextFields.size()) {
return ((MaskTextField) maskTextFields.get(position)).getText();
} else {
return "";
}
}
 
/*
** Return the text without delimiters
*/
public String getData() {
return getComponentText(false);
}
 
/*
** Methods to control the mask text color
*/
public void setMaskColor(Color color) {
maskColor = color;
}
 
public Color getMaskColor() {
return maskColor;
}
 
/*
** Control how the background mask is rendered
** true: as characters are entered a partial mask is displayed
** false: once the first character is entered no mask is displayed
*/
public void setDisplayPartialMask(boolean value) {
displayPartialMask = value;
}
 
public boolean isDisplayPartialMask() {
return displayPartialMask;
}
 
/*
** Append the text from the individual components
** to form the text for the group component.
*/
private String getComponentText(boolean delimiterRequired) {
text.setLength(0);
Component[] components = getComponents();
 
for (int i = 0; i < components.length; i++) {
if (components[i] instanceof JTextField) {
JTextField textField = (JTextField) components[i];
text.append(textField.getText());
}
 
if (delimiterRequired && components[i] instanceof JLabel) {
JLabel label = (JLabel) components[i];
text.append(label.getText());
}
}
 
return text.toString();
}
 
/*
** Set focus on the next/previous text field
*/
public void tab(JTextField textField, int direction) {
Component[] components = getComponents();
 
// find the text field that currently has focus
 
int i = 0;
 
while (components[i] != textField) {
i++;
}
 
// find the next/previous text field on the panel
 
do {
i = (i + direction + components.length) % components.length;
} while (!(components[i] instanceof JTextField));
 
// set focus on the text field
 
components[i].requestFocus();
}
 
// Implement KeyListener
// Override the default behaviour of certain keys
 
public void keyPressed(KeyEvent e) {
JTextField textField = (JTextField) e.getSource();
 
switch (e.getKeyCode()) {
// tab to next text field
 
case KeyEvent.VK_UP:
tab(textField, TAB_NEXT);
break;
 
// tab to previous text field
 
case KeyEvent.VK_DOWN:
tab(textField, TAB_PREVIOUS);
break;
 
// move to next character or tab to next field
 
case KeyEvent.VK_RIGHT:
if (textField.getCaretPosition() == textField.getDocument().getLength()) {
tab(textField, TAB_NEXT);
e.consume();
}
break;
 
// move to previous character or tab to previous field
 
case KeyEvent.VK_LEFT:
if (textField.getCaretPosition() == 0) {
tab(textField, TAB_PREVIOUS);
e.consume();
}
break;
}
}
 
public void keyTyped(KeyEvent e) {
}
 
public void keyReleased(KeyEvent e) {
}
 
/*
** Test the class
*/
public static void main(String[] args) {
JFrame frame = new JFrame();
//frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(0, 2));
//JPanel panel = new JPanel();
frame.setContentPane(panel);
 
// add date with no validation
final TextGroup date1 = new TextGroup("YYYY/MM/DD", "/");
date1.setBackground(Color.yellow);
date1.setForeground(Color.red);
panel.add(new JLabel("Date - not validated:"));
panel.add(date1);
 
JButton textButton = new JButton("Validate Date");
textButton.addActionListener(new ActionListener() {
 
public void actionPerformed(ActionEvent e) {
System.out.println(date1.getText(0)); // get text from first field
System.out.println(date1.getData()); // get text, excluding delimiters
String text = date1.getText(); // get text, including delimeters
System.out.print(text);
try {
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
df.setLenient(false);
df.parse(text);
System.out.println(" is valid");
} catch (Exception e2) {
System.out.println(" is invalid");
}
}
});
panel.add(new JLabel(""));
panel.add(textButton);
 
// add date with numeric validation
FieldValidator[] validators = new FieldValidator[3];
validators[0] = new IntegerValidator(1900, 2010);
validators[1] = new IntegerValidator(1, 12, 2);
validators[2] = new IntegerValidator(1, 31, 2);
TextGroup date2 = new TextGroup("YYYY/MM/DD", "/", validators);
date2.setDisplayPartialMask(false);
date2.setMaskColor(Color.red);
panel.add(new JLabel("Date - validated:"));
panel.add(date2);
 
// add phone number validate for integer characters
FieldValidator numeric = new IntegerValidator();
TextGroup phone = new TextGroup("(###)___-____", "()-", numeric);
panel.add(new JLabel("Phone Number:"));
panel.add(phone);
 
// add IP address
IntegerValidator ipv = new IntegerValidator(1, 255);
TextGroup ip = new TextGroup(" . . . ", ".", ipv);
ip.setText("1.2.3");
ip.setText(3, "4");
panel.add(new JLabel("IP Address:"));
panel.add(ip);
 
// add signed integer field
IntegerValidator signed = new IntegerValidator(-100, 100);
signed.setSignRequired(IntegerValidator.ALWAYS);
TextGroup signedField = new TextGroup("+ ", "", signed);
panel.add(new JLabel("Signed Integer:"));
panel.add(signedField);
 
// add postal code as stand alone MaskTextField
MaskTextField postalCode = new MaskTextField(6, "");
postalCode.setValidator(numeric);
panel.add(new JLabel("Postal Code:"));
panel.add(postalCode);
 
// add stand alone MaskTextField
MaskTextField name = new MaskTextField(20, "First Name");
name.setMaskColor(Color.blue);
name.setDisplayPartialMask(false);
panel.add(new JLabel("Masked First Name:"));
panel.add(name);
 
frame.pack();
frame.setVisible(true);
}
}
 
/*
** Customized JTextField with the following properties:
**
** 1) Text automatically selected when focus gained
** 2) Control for the number of characters allowed
** 3) Automatic tab when text field is full
** 4) Optional mask displayed in text field before entering data
** 5) Optional character validation can be added
** 6) ToolTip will display validation error message
*/
class MaskTextField extends JTextField
implements DocumentListener, FocusListener, KeyListener {
 
private TextGroup parent;
private String mask;
private Color maskColor = TextGroup.DEFAULT_MASK_COLOR;
private boolean displayPartialMask = TextGroup.DEFAULT_DISPLAY_PARTIAL_MASK;
private boolean isFocusTraversable = true;
private FieldValidator validator;
private StringBuffer textBuffer = new StringBuffer();
 
public MaskTextField(int size, String mask) {
super(size);
setMask(mask);
 
if (mask.trim().length() == 0) {
setMask(null);
}
 
getDocument().addDocumentListener(this);
addFocusListener(this);
addKeyListener(this);
}
 
/*
** Parent is set when this MaskTextField is part of a group
*/
public void setParent(TextGroup parent) {
this.parent = parent;
}
 
/*
** Text displayed in the background of the component
*/
public void setMask(String mask) {
this.mask = mask;
}
 
public String getMask() {
return mask;
}
 
/*
** Methods to control the mask text color
*/
public void setMaskColor(Color color) {
maskColor = color;
}
 
public Color getMaskColor() {
if (parent != null) {
return parent.getMaskColor();
} else {
return maskColor;
}
}
 
/*
** Control how the background mask is rendered
*/
public void setDisplayPartialMask(boolean value) {
displayPartialMask = value;
}
 
public boolean isDisplayPartialMask() {
if (parent != null) {
return parent.isDisplayPartialMask();
} else {
return displayPartialMask;
}
}
 
/*
** Add class to perform simple validation of typed characters
*/
public void setValidator(FieldValidator validator) {
this.validator = validator;
}
 
/*
**
*/
@Override
@SuppressWarnings("deprecation")
public boolean isFocusTraversable() {
return isFocusTraversable;
}
 
public void setFocusTraversable(boolean value) {
isFocusTraversable = value;
}
 
/*
** Override to add a mask as a background to the text field
*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
 
if (mask == null) {
return;
}
 
int length = getDocument().getLength();
 
// Paint mask character in remaining text field space
 
if (isDisplayPartialMask() && length < mask.length()) {
g.setColor(getMaskColor());
FontMetrics fm = getFontMetrics(getFont());
int y = getInsets().top + fm.getAscent();
int x = getInsets().left + fm.stringWidth(getText());
g.drawString(mask.substring(length), x, y);
return;
}
 
// Paint mask characters only when text field is empty
 
if (!isDisplayPartialMask() && length == 0) {
Dimension d = getSize();
g.setColor(getMaskColor());
FontMetrics fm = getFontMetrics(getFont());
int y = getInsets().top + fm.getAscent();
int x = getInsets().left + ((d.width - fm.stringWidth(mask)) / 2);
g.drawString(mask, x, y);
return;
}
 
}
 
/*
** Override so that the default document is always a SizeDocument
*/
@Override
protected Document createDefaultModel() {
return new SizeDocument(getColumns());
}
 
// Implement DocumentListener
// Automatically tab when the text field becomes full
 
public void insertUpdate(DocumentEvent e) {
Document doc = e.getDocument();
 
if (doc.getLength() == getColumns()) {
Object pa = getParent();
 
if (pa instanceof TextGroup) {
((TextGroup) pa).tab(this, TextGroup.TAB_NEXT);
} else {
FocusManager.getCurrentManager().focusNextComponent(this);
}
}
}
 
public void removeUpdate(DocumentEvent e) {
}
 
public void changedUpdate(DocumentEvent e) {
}
 
// Implement FocusListener
// Select all the text when a text field gains focus
 
public void focusGained(FocusEvent e) {
JTextField textField = (JTextField) e.getSource();
textField.selectAll();
textField.setToolTipText(null);
}
 
public void focusLost(FocusEvent e) {
}
 
// Implement KeyListener
 
@SuppressWarnings("deprecation")
public void keyTyped(KeyEvent e) {
// The following keys are always valid
 
switch ((int) e.getKeyChar()) {
case KeyEvent.VK_BACK_SPACE:
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_TAB:
return;
}
 
// Invoke the validator to make sure the key is valid
 
if (validator != null) {
setToolTipText(null);
String possibleText = getPossibleText(e);
 
if (!validator.isValid(possibleText)) {
// Toolkit.getDefaultToolkit().beep();
setToolTipText(validator.getMessage());
Component c = (Component) e.getSource();
c.dispatchEvent(new KeyEvent(c, KeyEvent.KEY_PRESSED, 0, KeyEvent.CTRL_MASK, KeyEvent.VK_F1));
e.consume();
}
}
}
 
public void keyPressed(KeyEvent e) {
}
 
public void keyReleased(KeyEvent e) {
}
 
/*
** Get the possible text by:
** 1) getting the current text,
** 2) delete any selected text,
** 3) insert typed character
*/
private String getPossibleText(KeyEvent e) {
JTextField textField = (JTextField) e.getSource();
 
textBuffer.setLength(0);
textBuffer.append(textField.getText());
 
int start = textField.getSelectionStart();
int end = textField.getSelectionEnd();
textBuffer.delete(start, end);
 
textBuffer.insert(start, e.getKeyChar());
 
return textBuffer.toString();
}
 
/*
** Control the number of characters that can be entered
*/
class SizeDocument extends PlainDocument {
 
private int maximumFieldLength = 0;
 
public SizeDocument(int maximumFieldLength) {
this.maximumFieldLength = maximumFieldLength;
}
 
@Override
public void insertString(int offset, String text, AttributeSet a)
throws BadLocationException {
if (text == null) {
return;
}
 
// Make sure text does not exceed its maximum length
 
if (getLength() + text.length() > maximumFieldLength) {
Toolkit.getDefaultToolkit().beep();
} else {
super.insertString(offset, text, a);
}
}
}
}
 
interface FieldValidator {
/*
** The text string will contain:
** the current contents of the document,
** less selected text about to be deleted,
** plus the character about to be added.
*/
 
public boolean isValid(String text);
 
/*
** Message explaining why last character typed was rejected
*/
public String getMessage();
}
 
/*
** Class that will only accept numeric digits.
** Optionally, the number enter can be validated within a range
*/
class IntegerValidator implements FieldValidator {
 
public final static int NEVER = 0;
public final static int OPTIONAL = 1;
public final static int ALWAYS = 2;
private int minimum;
private int maximum;
private int rangeCheckDigits;
private int signRequired;
protected String message;
 
/*
** Convenience constructor - effectively no range checking is done
*/
public IntegerValidator() {
this(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
}
 
/*
** Convenience constructor - specify range checking
**/
public IntegerValidator(int minimum, int maximum) {
this(minimum, maximum, 0);
 
int min = Integer.toString(Math.abs(minimum)).length();
int max = Integer.toString(Math.abs(maximum)).length();
startRangeCheckAt(Math.min(min, max));
}
 
/*
** Main contructor for creating this class.
** minimum - the minimum value allowed in this field
** maximum - the maximum value allowed in this field
** rangeCheckDigits - start range checking when this number of digits
** have been entered
*/
@SuppressWarnings("empty-statement")
public IntegerValidator(int minimum, int maximum, int rangeCheckDigits) {
this.minimum = minimum;
this.maximum = maximum;
startRangeCheckAt(rangeCheckDigits);
;
}
 
public boolean isValid(String text) {
// Set variables used in later tests
 
int multiplier = 1;
boolean signEntered = false;
char firstCharacter = text.charAt(0);
 
if (firstCharacter == '+' || firstCharacter == '-') {
signEntered = true;
text = text.substring(1); // remove sign from text to validate
 
if (firstCharacter == '-') {
multiplier = -1;
}
}
 
// Validate the sign
 
if (signRequired == NEVER && signEntered) {
message = "Field must not contain a sign (+ or -)";
return false;
}
 
if (signRequired == ALWAYS && !signEntered) {
message = "Field must contain a sign (+ or -)";
return false;
}
 
// Only a sign has been entered, no number to validate
 
if (text.length() == 0) {
return true;
}
 
// Check for a valid integer
 
int value;
 
try {
value = Integer.parseInt(text) * multiplier;
} catch (Exception e) {
message = "Field must contain a number";
return false;
}
 
// Do a range check on the value
// (when enough digits have been entered)
 
if (text.length() < rangeCheckDigits) {
return true;
} else {
if (value < minimum || value > maximum) {
message = "Field must contain a number in the range " + minimum + " : " + maximum;
return false;
} else {
return true;
}
}
}
 
public String getMessage() {
return message;
}
 
/*
** Start range checking only when the specified number of digits
** have been entered.
** (For example, range check a year after 4 digits have been entered)
*/
public void startRangeCheckAt(int rangeCheckDigits) {
this.rangeCheckDigits = rangeCheckDigits;
}
 
/*
** Indicates whether a leading +/- sign is required when entering integer
** NEVER - entering a sign will result in an error (default)
** OPTIONAL - sign is optional
** ALWAYS - a sign must be entered or an error will result
*/
public void setSignRequired(int signRequired) {
this.signRequired = signRequired;
}
}
QuestionHow to apply mask to whole column and decimal column error PinmemberAndrusM6-Jun-08 7:42 
AnswerRe: How to apply mask to whole column and decimal column error PinmemberJ.Thomas6-Jun-08 8:22 
GeneralRe: How to apply mask to whole column and decimal column error PinmemberAndrusM6-Jun-08 9:30 
GeneralRe: Can't apply mask for output PinmemberJ.Thomas8-Jun-08 2:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150301.1 | Last Updated 12 May 2008
Article Copyright 2008 by J.Thomas
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid