Click here to Skip to main content
15,885,366 members
Articles / Programming Languages / C#
Article

IP-Address TextBox

Rate me:
Please Sign up or sign in to vote.
3.92/5 (22 votes)
31 Aug 20033 min read 208.5K   5.1K   61   22
TextBox derived class to enter IP addresses

Introduction

Problem was, I didn't find a solution to edit an IP address like in Windows network environment, for C#. Although there are some controls for masked edit fields, I wanted to write my own, and if so I wanted it to behave like the controls from MFC library or Windows network environment and maybe a little more.

Problems to solve

The heaviest problem at writing the control was to catch the inputs of backspace and delete keys, to delete characters from the input field. I tried a lot with overridden event handlers, OnKeyDown and OnKeyUp but it didn't work like it should.

Then I remembered that another developer had overridden the PreProsessMessage method to catch keyboard inputs and handle it in own ways. So I implemented an override for PreProcessMessage to handle all the backspaces and delete key presses and used OnKeyUp, OnKeyPress and OnKeyDown to handle the inputs of dots and slashes and set the input cursor to the right position.

OnKeyDown event

C#
/// <summary>
/// Override standard KeyDownEventHandler
/// Catches Inputs of "." and "/" to jump to next positions
/// </summary>
/// <param name="e">KeyEventArgument</param>

protected override void OnKeyDown(KeyEventArgs e)
{
   //Zeichen an die richtige stelle schreiben
   int iPos = this.SelectionStart;
   char[] cText = this.Text.ToCharArray();
   if(e.Modifiers == Keys.None)
   {
       if((char.IsLetterOrDigit(Convert.ToChar(e.KeyValue)) || 
          e.KeyCode == Keys.NumPad0)//Numpad0=96 --> `
          && iPos < this.TextLength)
       {
           if(cText[iPos] == '.' || cText[iPos] == ':'
                            || cText[iPos] == '/')
               iPos+=1;
           this.SelectionStart = iPos;
           if(this.OverWriteMode)
           {
               if(cText[iPos] != ' ')
               this.SelectionLength = 1;
           }
           else
           {
                if(iPos < this.TextLength)
                   if(cText[iPos] == ' ')
                       this.SelectionLength = 1;
           }
       }
   }
   base.OnKeyDown (e);
}

OnKeyUp event

C#
/// <summary>
/// Override standard KeyUpEventHandler
/// Catches Inputs of "." and "/" to jump to next positions
/// </summary>
/// <param name="e">KeyEventArgument</param>

protected override void OnKeyUp(KeyEventArgs e)
{
     //Zeichen an die richtige stelle schreiben
     int iPos = this.SelectionStart;
     char[] cText = this.Text.ToCharArray();
   
     //Cursor hintern Punkt setzen
     if((char.IsLetterOrDigit(Convert.ToChar(e.KeyValue)) || 
             e.KeyCode == Keys.NumPad0)//Numpad0=96 --> `
               && iPos < this.TextLength)
     {
          if(cText[iPos] == '.' || cText[iPos] == ':' 
                               || cText[iPos] == '/')
            iPos+=1;
          this.SelectionStart = iPos;
     }
     base.OnKeyUp (e);
}

OnKeyPress event

C#
/// <summary>
/// Override standard KeyPressEventHandler
/// Catches Inputs of "." and "/" to jump to next positions
/// </summary>
/// <param name="e">KeyPressEventArgument</param>

protected override void OnKeyPress(KeyPressEventArgs e)
{
    //Zulassige Zeichen
    if(char.IsControl(e.KeyChar) ||
         m_regexValidNumbers.IsMatch(e.KeyChar.ToString()))
    {
         e.Handled = false;
    }
    else
    {
         switch(e.KeyChar)
         {
              case '/':
                 this.JumpToSlash();
                  break;
              case '.':
                  this.JumpToNextDot();
                  break;
              default:
                  break;
         }
         e.Handled = true;
    }
    base.OnKeyPress(e);
}

PreProcessMessage

C#
/// <summary>
/// Override standard PreProcessMessage
/// Catches Inputs of BackSpace and Deletes 
/// </summary>
/// <param name="msg">PreProcessMessage</param>

public override bool PreProcessMessage(ref Message msg)
{
    if (msg.Msg == WM_KEYDOWN)
    {
        Keys keyData = ((Keys) (int) msg.WParam) |ModifierKeys;
        Keys keyCode = ((Keys) (int) msg.WParam);

        int iPos = this.SelectionStart;
        char[] cText = this.Text.ToCharArray();
        switch(keyCode)
        {
            case Keys.Delete:
                if(iPos < this.TextLength)
                {
                    while(cText[iPos] == '.' || 
                       cText[iPos] == ':' || 
                       cText[iPos] == '/')
                    {
                        if((iPos+=1) >= cText.Length)
                            break;
                    }
                    if(iPos < this.TextLength)
                    {
                        base.Text = this.Text.Substring(0,iPos) + 
                                " " + this.Text.Substring(iPos+1);
                        this.SelectionStart = iPos+1;
                    }
                    else
                        this.SelectionStart = this.TextLength-1;
                }
                   return true;
            case Keys.Back:
                if(iPos > 0)
                {
                    while(cText[iPos-1] == '.' || 
                        cText[iPos-1] == ':' || 
                        cText[iPos-1] == '/')
                    {
                        if((iPos-=1)<=0)
                            break;
                    }
                    if(iPos>0)
                    {
                        base.Text = this.Text.Substring(0,iPos-1) 
                                    + " " + this.Text.Substring(iPos);
                        this.SelectionStart = iPos-1;
                    }
                    else
                        this.SelectionStart = 0;
                }
                return true;
            default:
                break;
        }    
    }
    return base.PreProcessMessage (ref msg);
}

Another problem was the input of numbers via the numpad. Especially the 0 key was not recognized, because it's char value is neither a letter nor a digit, so I had to ask for Keys.NumPad0 hard coded.

C#
if((char.IsLetterOrDigit(Convert.ToChar(e.KeyValue)) || 
    e.KeyCode == Keys.NumPad0)//Numpad0=96 --> `
    iPos < this.TextLength)
{[...]}

At least...

...I have a control that looks like a TextBox with dots, where I can input numbers, type dots to jump to next IP parts, and get its contents via the Text property.

Using the code

Include the IPAddressTextBox.cs in your project. Set a TextBox in your form or user control and clear its contents. Change the type of this TextBox from System.Windows.Forms.TextBox to rj2_cs.IPAddressTextBox in code editor. Then you can change the properties of the IP textbox like you want.

Changes/Modifications

  • 2003-08-05
    • Implemented some exception handling at IP-Validation
    • Compiled in an assembly it can be used via Visual Studio Designer
    • Text property overridden, so you can only enter valid IP addresses
    C#
    public override string Text
    {
        get
        {
            return base.Text;
        }
        set
        {
            try
            {
                if(IPAddressTextBox.ValidateIP(value, 
                      this.m_newIPNotation, this.m_arlDelimeter))
                    base.Text = IPAddressTextBox.MakeValidSpaces(value, 
                            this.m_newIPNotation, this.m_arlDelimeter);
            }
            catch
            {    }
        }
    }
  • 2003-08-06
    • Bug fix: Invalid IP addresses could not be changed by deleting characters, because of the validation of the Text property --workaround-> Delete/Backspaces change the base.Text property
    • Additional Method: GetPureIPAddress(), returns the IP address in the Text field without leading zeroes or leading/trailing spaces
    C#
    public string GetPureIPAddress()
    {
        string s = "";
        ArrayList arlIP = new ArrayList(this.Text.Replace(" ","").
            Split((char[])this.m_arlDelimeter.ToArray(typeof(char))));
        for(int i=0; i <arlIP.Count; i++)
        {
            while(arlIP[i].ToString().StartsWith("0"))
                arlIP[i] = arlIP[i].ToString().Substring(1);
        }
        s = IPAddressTextBox.MakeIP(
                  (string[])arlIP.ToArray(typeof(string)), 
                  this.m_ipNotation);
        /*in IPv6 Addresses replace 0000: by ::*/
        if(this.m_ipNotation == IPNotation.IPv6Hexadecimal || 
            this.m_ipNotation == IPNotation.IPv6HexadecimalCIDR || 
            this.m_ipNotation == IPNotation.IPv6Binary || 
            this.m_ipNotation == IPNotation.IPv6BinaryCIDR)
        {
            while(s.IndexOf(":::")>=0)
            {
                s = s.Remove(s.IndexOf(":::"),1);
            }
        }
        return s;
    }
  • 2003-08-11
    • Hide unneeded members from base class in Studio Designer
    • IPv6 implemented properly (I hope)
    • Properties, events and event handlers for "Binary", "IPv6", "Subnet" deleted
    • Notation-Property,-Event and -event handler added: Value is one of the IPNotation enumeration
      C#
      public enum IPNotation
      {
          IPv4Decimal,
          /*192.168.000.001*/
          IPv4Binary,
          /*11000000.10101000.00000000.00000001*/
          IPv4DecimalCIDR,
          /*192.168.000.001/16*/
          IPv4BinaryCIDR,
          /*11000000.10101000.00000000.00000001/16*/
          IPv6Hexadecimal,
          /*0000:0000:0000:0000:00c0:00a8:0000:0001*/
          IPv6Binary,
          /*0000000000000000:0000000000000000:
                  0000000000000000:0000000000000000:
          0000000011000000:0000000010101000:
                  0000000000000000:0000000000000001*/
          IPv6HexadecimalCIDR,
          /*0000:0000:0000:0000:00c0:00a8:0000:0001/16*/
          IPv6BinaryCIDR,
          /*0000000000000000:0000000000000000:
                  0000000000000000:0000000000000000:
          0000000011000000:0000000010101000:
                  0000000000000000:0000000000000001/16*/
          IPv6IPv4Decimal,
          /*::192.168.000.001*/
          IPv6IPv4Binary,
          /*::11000000.10101000.00000000.00000001*/
          IPv6IPv4DecimalCIDR,
          /*::192.168.000.001/16*/
          IPv6IPv4BinaryCIDR
          /*::11000000.10101000.00000000.00000001/16*/
      }
    • Change of the Notation Property causes call of a huge function that converts the given IP-Address to the new Notation
    • Default value of property OverWriteMode changed to true
    • Some changes in demo project to test the new properties and functions
  • 2003-09-01
    • Default value for property OverWriteMode set to true (else the VS-Designer couldn't change the property the code)
    • IPv4 addresses now have zeroes between dots (previously forgotten in MakeIP/GetPureIP methods)
    • Copy and paste via keyboard inputs enabled; check if modifier-key is pressed in OnKeyDown override (can just copy/paste whole IP addresses or part between dots, all the others I still have to implement)
    C#
    protected override void OnKeyDown(KeyEventArgs e)
    {
        int iPos = this.SelectionStart;
        char[] cText = this.Text.ToCharArray();
        if(e.Modifiers == Keys.None)
        {
            //[some code]
        }
        base.OnKeyDown (e);
    }

    Demo packet changed, so you don't have to download source packet.

TODO

  • Move digits to right, if there is an input and there are spaces left to next delimiter
  • Accept optimized IPv6 addresses (with :: instead of 0000: ) for set_Text property
  • Enable drag/drop

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
RJ2
Web Developer
Germany Germany
- Attended School from 1985-1998
- began programming in QBasic and Turbo-Pascal
- tried something with HTML, Perl, PHP
- Apprenticeship from 1999-2002 as Software Developer
- learned write programs with high level languages like C, C++, since 2002 C#
- wrote a lot of GUI applications
- since 2002 working as software developer for GUI Applications and Installation-Packages at Ferrari electronic AG (Germany, Teltow)
- continuing programming with high level languages to develop GUIs and helper libraries for installation packages

Comments and Discussions

 
QuestionCode License Pin
Member 1418843620-Mar-19 10:32
Member 1418843620-Mar-19 10:32 
GeneralMy vote of 5 Pin
unforgiven.179122-Aug-13 10:35
unforgiven.179122-Aug-13 10:35 
QuestionHow to use the component of RJ2_cs.IPAddressTextBox Pin
ryanandika18-May-11 17:51
ryanandika18-May-11 17:51 
AnswerRe: How to use the component of RJ2_cs.IPAddressTextBox Pin
rj2Skipper18-May-11 20:11
rj2Skipper18-May-11 20:11 
Generalgood article. Pin
G Haranadh31-Jul-10 5:17
G Haranadh31-Jul-10 5:17 
Generalquestion Pin
fradev13-Oct-06 5:48
fradev13-Oct-06 5:48 
GeneralZips Pin
Anonymous12-Oct-05 1:03
Anonymous12-Oct-05 1:03 
AnswerRe: Zips Pin
RJ212-Oct-05 1:36
RJ212-Oct-05 1:36 
GeneralpreventLeaveOnError Pin
Mustafa ÖZDEN11-May-05 19:20
Mustafa ÖZDEN11-May-05 19:20 
GeneralRe: preventLeaveOnError Pin
RJ218-May-05 22:26
RJ218-May-05 22:26 
GeneralRe: preventLeaveOnError Pin
Mustafa ÖZDEN22-May-05 20:49
Mustafa ÖZDEN22-May-05 20:49 
Generalthank you very much Pin
RanjithLogics10-May-05 22:55
RanjithLogics10-May-05 22:55 
GeneralRe: I can't use the source code Pin
RJ230-Sep-04 4:16
RJ230-Sep-04 4:16 
Generalnice Pin
Taha Elsayed24-Sep-04 15:35
Taha Elsayed24-Sep-04 15:35 
GeneralException in the designer Pin
Anthony_Yio4-Mar-04 18:54
Anthony_Yio4-Mar-04 18:54 
My VS.NET form designer complain about exception when i try to view it in the form designer.

Below is the exception message.

Exception from HRESULT: 0x80131019

I am using VS.NET 7.0.9466



Sonork 100.41263:Anthony_Yio
QuestionSource? Pin
Anonymous23-Aug-03 2:11
Anonymous23-Aug-03 2:11 
AnswerRe: Source? Pin
RJ224-Aug-03 21:47
RJ224-Aug-03 21:47 
GeneralDemo not working Pin
Sebastien Lorion16-Aug-03 20:02
Sebastien Lorion16-Aug-03 20:02 
GeneralRe: Demo not working Pin
RJ217-Aug-03 9:16
RJ217-Aug-03 9:16 
GeneralRe: Looks useful, but... Pin
Anonymous20-Aug-04 15:09
Anonymous20-Aug-04 15:09 
GeneralGood start but... Pin
Carl Mercier4-Aug-03 6:40
Carl Mercier4-Aug-03 6:40 
GeneralRe: Good start but... Pin
RJ24-Aug-03 20:11
RJ24-Aug-03 20:11 

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

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