Team LiB
Previous Section Next Section

The Masked Edit Control

The Masked Edit control is not native to .NET. Normally, I am reluctant to discuss third-party controls because most programmers will not have access to them. This one is different, however.

The Masked Edit control comes with Visual Studio 6.0. I would imagine that nearly everyone who is using .NET and is reading this book has Visual Studio 6.0 or 5.0 on his or her machine.

The fact that this control comes from Visual Studio 6.0, specifically from VB 6.0, should tell you something. It is an OCX; therefore, it is a COM control. Because it is not native to .NET, you will not see it on your Toolbox. You can get it easily enough, though, by following these steps:

  1. Open up a Windows Forms project.

  2. Bring the Toolbox into view.

  3. Right-click the Toolbox and choose Customize Toolbox.

  4. Choose the COM Components tab.

  5. Select Microsoft Masked Edit Control, version 6.0.

Figure 8-1 shows the Customize Toolbox window.

Click To expand
Figure 8-1: Choosing the Masked Edit control

Click OK and you should now have the Masked Edit control in your Toolbox. Figure 8-2 shows what it looks like.


Figure 8-2: The Masked Edit control in the Toolbox

You should also notice that two references were also added: AxInterop.MSMask and Interop.MSMask. Now, you already know that .NET talks to VB programs and OCXs via the Interop services, right? If not, you should. You can easily go along and code a perfectly good program without knowing how .NET is talking to this control, but to truly excel at this game you should know what is going on. Coverage of Interop is beyond the scope of this book, but I do urge you to spend some time learning at least the basics of how the COM Interop services work. OK, enough of that.

This control looks like a normal TextBox when dropped on a form. It has some enhancements, though. You can set a masked property to validate input and also to format output. You can even add some visual cues in the form of literal characters. For instance, you can make the box display the dashes for a normal phone number. I could add a mask in this form: ##/##/##. This is a typical date mask. The front slashes appear on the control as visual cues. Figure 8-3 shows this.

Click To expand
Figure 8-3: The Masked Edit control with a date mask

As you type in numbers, the cursor will skip over the slashes and put the numbers where they belong. Isn't this so much easier than hooking into the Paint and KeyPress events? The mask is easy to change as well.

You can mask just about any set of characters you like. Be aware, though, that the masking you can do here is not even close to the validation you can do using regular expressions. Table 8-5 shows the masking characters available for this control.

Table 8-5: Masking Characters for the Masked Edit Control

Character

Meaning

#

Digit placeholder

.

Decimal placeholder based on culture settings

,

Thousand separator based on culture settings

:

Time separator based on culture settings

/

Date separator based on culture settings

\

Escapes the next character as a literal

&

Character placeholder

<

Converts all characters to lowercase

>

Converts all characters to uppercase

A

Required alphanumeric character

a

Optional alphanumeric character

9

Optional digit placeholder

C

Works like the & character

?

Letter placeholder (upper- or lowercase)

If you enter any character that is not valid according to the mask, it rejects that character. This is just like setting the Handled property of a TextBox to true during the KeyPress event. If you want to trap this rejection, you can hook into the ValidationError event.

All this is really nice, don't you think? Well, guess what?

It does not work properly in .NET!

Now, I know that quite a few of you VB programmers are probably sitting there in disbelief, so I will prove it to you. First, I will show you the sequence of events (literally) and the resulting Text property when you use this control in a VB 6.0 program. You can try this out if you want to see how it works, but suffice it to say that this is exceedingly easy VB 6.0 code.

VB 6.0

Private Sub me1_GotFocus()
  L.AddItem "m1 got focus event"
End Sub

Private Sub me1_LostFocus()
  L.AddItem "m1 lost focus event"
End Sub

Private Sub me1_ValidationError(InvalidText As String, StartPosition As Integer)
  L.AddItem "m1 validation error even " & me1.Text
End Sub
'========================================================
Private Sub me2_GotFocus()
  L.AddItem "m2 got focus event"
End Sub

Private Sub me2_LostFocus()
  L.AddItem "m2 lost focus event"
End Sub

Private Sub me2_ValidationError(InvalidText As String, StartPosition As Integer)
  L.AddItem "m2 validation error event " & me2.Text
End Sub

Figure 8-4 shows the result of running this code.

Click To expand
Figure 8-4: VB 6.0 using Masked Edit control

You can see that the events are in the following correct order:

  1. The control got focus.

  2. A validation error event fired because I left the control too early.

  3. The second control got focus.

  4. A validation error event fired because I left the control too early.

  5. The first control got focus.

This is what you would expect, and it is what you get. Notice that when I display the Text property of the control, I get the text entered as well as the visual cues. I went from control to control by using the Tab key.

Now let's do the same thing in C# and in VB. Believe it or not, this control behaves slightly differently in the two languages.

Start a new project in either C# or in VB. Mine is called "MaskedEdit." Add the following controls and properties:

  1. Add a Label whose text reads Enter Date.

  2. Below this Label add a Masked Edit control called meDate. Change its TabIndex to 0. Change its mask to 9#/9#/####.

  3. Add a Label whose text reads Enter Military Time.

  4. Below this Label add a Masked Edit control called meTime. Change its TabIndex to 1. Change its mask to ##:##.

  5. Add a ListBox called L. Change its TabStop property to false.

Figure 8-5 shows what this looks like.

Click To expand
Figure 8-5: .NET Masked Edit control test

The code for this project is similar to the code for the VB 6.0 project. Listings 8-1a and 8-1b show the code for the Form_Load event handler and also for the delegates that handle the enter, leave, and validation error events.

Listing 8-1a: C# Code for the Masked Edit Control Test Program
Start example

    private void Form1_Load(object sender, System.EventArgs e)
    {
      meDate.ValidationError +=
                  new MaskEdBoxEvents_ValidationErrorEventHandler(DateErr);
      meDate.Enter += new EventHandler(DateEnter);
      meDate.Leave += new EventHandler(DateLeave);

      meTime.ValidationError +=
                  new MaskEdBoxEvents_ValidationErrorEventHandler(TimeErr);
      meTime.Enter += new EventHandler(TimeEnter);
      meTime.Leave += new EventHandler(TimeLeave);
    }

    #region Masked Edit events

    private void DateEnter(object sender, EventArgs e)
    {
      L.Items.Add("Date got focus");
    }

    private void DateLeave(object sender, EventArgs e)
    {
      L.Items.Add("Date left");
      L.Items.Add("Date Text = " + meDate.Text);
    }

    private void DateErr(object sender, MaskEdBoxEvents_ValidationErrorEvent e)
    {
      L.Items.Add("Date validation error");
    }

    private void TimeEnter(object sender, EventArgs e)
    {
      L.Items.Add("Time got focus");
    }

    private void TimeLeave(object sender, EventArgs e)
    {
      L.Items.Add("Time left");
      L.Items.Add("Time Text = " + meTime.Text);
    }

    private void TimeErr(object sender, MaskEdBoxEvents_ValidationErrorEvent e)
    {
      L.Items.Add("Time validation error");
    }

    #endregion
End example
Listing 8-1b: VB Code for the Masked Edit Control Test Program
Start example
  Private Sub Form1_Load(ByVal sender As System.Object, _
                         ByVal e As System.EventArgs) Handles MyBase.Load

    AddHandler meDate.ValidationError, _
              New MaskEdBoxEvents_ValidationErrorEventHandler(AddressOf DateErr)
    AddHandler meDate.Enter, New EventHandler(AddressOf DateEnter)
    AddHandler meDate.Leave, New EventHandler(AddressOf DateLeave)

    AddHandler meTime.ValidationError, _
              New MaskEdBoxEvents_ValidationErrorEventHandler(AddressOf TimeErr)
    AddHandler meTime.Enter, New EventHandler(AddressOf TimeEnter)
    AddHandler meTime.Leave, New EventHandler(AddressOf TimeLeave)

 End Sub

#Region "Masked Edit events"

  Private Sub DateEnter(ByVal sender As Object, ByVal e As EventArgs)
    L.Items.Add("Date got focus")
  End Sub

  Private Sub DateLeave(ByVal sender As Object, ByVal e As EventArgs)
    L.Items.Add("Date left")
    L.Items.Add("Date Text = " + meDate.Text)
  End Sub

  Private Sub DateErr(ByVal sender As Object, _
                      ByVal e As MaskEdBoxEvents_ValidationErrorEvent)
    L.Items.Add("Date validation error")
  End Sub

  Private Sub TimeEnter(ByVal sender As Object, ByVal e As EventArgs)
    L.Items.Add("Time got focus")
  End Sub

  Private Sub TimeLeave(ByVal sender As Object, ByVal e As EventArgs)
    L.Items.Add("Time left")
    L.Items.Add("Time Text = " + meTime.Text)
  End Sub

  Private Sub TimeErr(ByVal sender As Object, _
                      ByVal e As MaskEdBoxEvents_ValidationErrorEvent)
    L.Items.Add("Time validation error")
  End Sub

  #End Region
End example

Compile and run the program. Type in a few valid characters in the date field and tab over to the time field. Type a few valid characters in the time field and tab back over to the date field. Figure 8-6 shows the result of this.

Click To expand
Figure 8-6: .NET rendition of the Masked Edit control test

As you can see, the events are in the following order:

  1. The date field got focus.

  2. The date field lost focus.

  3. The time field got focus.

  4. The date field fired a validation error.

  5. The time field lost focus.

  6. The date field got focus.

  7. The time field fired a validation error.

Talk about a time lag! The validation error event is not fired until the next control already has focus. The reason I would use this event is to bring focus back to the offending control if not enough characters were entered. The control itself takes care of any error while you are still in the control, but this event is the way for you to know if the control has enough characters.

Imagine this. What do you think would happen if I had the following code for these two controls?

C#

    private void DateErr(object sender, MaskEdBoxEvents_ValidationErrorEvent e)
    {
      L.Items.Add("Date validation error");
      meDate.Focus();
    }
    private void TimeErr(object sender, MaskEdBoxEvents_ValidationErrorEvent e)
    {
      L.Items.Add("Time validation error");
      meTime.Focus();
    }

VB

  Private Sub DateEnter(ByVal sender As Object, ByVal e As EventArgs)
    L.Items.Add("Date got focus")
    meDate.Focus()
  End Sub
  Private Sub TimeErr(ByVal sender As Object, _
                      ByVal e As MaskEdBoxEvents_ValidationErrorEvent)
    L.Items.Add("Time validation error")
    meTime.Focus()
  End Sub

All I did was set the focus back to the offending control. Let me tell you what happens here: complete lockup. Because the events come out of order, the validation code for the date control sets focus back to the date control when focus is already in the time control. This makes the time control validation error code return focus back to the time control. You have set up a game of high-speed ping-pong. You can spend days trying to overcome this, but it can't be done without lots of code. Eliminating lots of code is the whole point of using this control.

Notice in Figure 8-6 that when I printed out the Text property of each control, all I got was an empty string. There is no way to get at the Text property of this control in .NET.

Now for the difference in using this control between C# and VB. The Text property does not even show up in the C# IntelliSense. It does in VB .NET.

So, why didn't I just tell you that this control does not work? Because you needed to feel the pain I went through trying to get it to work. Really, I show you this because I don't want you to think that this control can save you hours of programming and that it's the answer to most of your data entry and validation problems. It isn't.

Does it end here? Is there any hope? No and yes. How about making your own Masked Edit control?


Team LiB
Previous Section Next Section