views:

239

answers:

2

I have a MaskedTextBox control that, in our case, is collecting social insurance (tax) numbers (without a ValidatingType though since the string representation including the mask literals). A social insurance number is 3 groups of 3 digits separated by dashes. Sometimes spaces may be typed or entered instead of the dashes.

The configuration of the textbox is:

  • Mask: 999-999-999
  • ValidationType: null / not required
  • SkipLiterals: true
  • CutCopyMaskFormat: IncludeLiterals (only relevant when cut/copy FROM textbox)
  • TextMaskFormat: IncludeLiterals

-- Let me know if there other properties that you think could be important!

Problem

When pasting the following tax number "450 622 097" because of the spaces it doesn't match the mask. So I end up with "450- 62-2 9" in the text box. Pasting "450-622-097" will successfully paste into the box.

I want to be able to intercept the paste event in order to possibly fix it up to replace the spaces with dashes.

Alternatively, could we make the mask accept dashes OR spaces (but always output dashes)?

Non-solutions

MaskInputRejected event - I can't seem to get a handle on what was originally input (i.e. what's being rejected) so as to compare it with what's sitting at the top of the Clipboard. It merely returns how it was rejected

Validating event - Already occurs after the mask has been applied. I.e. the value of "450- 62-2 9" is in the textbox now.

Use custom ValidatingType with static Parse function - Again, occurs after the mask has been applied.

Detecting Key-Down event - Then if key series is Ctrl-V then manually handle and pass in a cleaned up version of the clipboard text. Could work, but then what about paste via the right click context menu?

Any other ideas?

A: 

While this is a hammer solution, there are limitations to the mask string and i don't see another way around it. What you need is to capture the paste event and process the text before it gets in the textbox. See below a simplistic example

   class MyMaskedTextbox : MaskedTextBox
        {
            const int WM_PASTE = 0x0302;

            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_PASTE:
                        if (Clipboard.ContainsText())
                        {
                            string text = Clipboard.GetText();
                            text = text.Replace(' ', '-');
//put your processing here
                            Clipboard.SetText(text);
                        }
                        break;
                }
                base.WndProc(ref m);
            }
        }
anchandra
Great! Yes, I had thought of this as a possibility over night... I had wanted to avoid extension via inheritance but I think that's the only way at the control's message pump if I'm not mistaken?
Reddog
Perhaps in my inherited TextBox I'll actually raise a "Pasting" event with event args to alter what will be pasted (raised where you have "put your processing here" comment is)... Will post it when I finish her!
Reddog
Yes, you can handle the messages only by inheritance. I don't think you need to raise any event. The code above is already doing some processing (see text.Replace), and with Clipboard.SetText you modify the content to be pasted
anchandra
+1  A: 

As per @anchandra's response and subsequent comments here is the class to enable processing of the text on a per-control basis.

public class MyMaskedTextBox : MaskedTextBox
{
    private const int WM_PASTE = 0x0302;

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_PASTE:
                if (Clipboard.ContainsText())
                {
                    string text = Clipboard.GetText();
                    var args = OnPasting(text);
                    if (args.Cancel)
                    {
                        // Swallow it up!
                        return;
                    }

                    // If value changed, then change what we'll paste from the top of the clipboard
                    if (!args.Value.Equals(text, StringComparison.CurrentCulture))
                    {
                        Clipboard.SetText(args.Value);
                    }
                }
                break;
        }
        base.WndProc(ref m);
    }

    public event EventHandler<PastingEventArgs> Pasting;

    protected virtual PastingEventArgs OnPasting(string value)
    {
        var handler = Pasting;
        var args = new PastingEventArgs(value);
        if (handler != null)
        {
            handler(this, args);
        }
        return args;
    }
}

public class PastingEventArgs : CancelEventArgs
{
    public string Value { get; set; }

    public PastingEventArgs(string value)
    {
        Value = value;
    }
}

And simple usage of the Pasting event to strip out spaces as per:

private void sinTextBox_Pasting(object sender, PastingEventArgs e)
{
    e.Value = e.Value.Replace(" ", String.Empty);
}
Reddog