
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
protected override void OnKeyDown(KeyEventArgs e)
{
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) && 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
protected override void OnKeyUp(KeyEventArgs e)
{
int iPos = this.SelectionStart;
char[] cText = this.Text.ToCharArray();
if((char.IsLetterOrDigit(Convert.ToChar(e.KeyValue)) ||
e.KeyCode == Keys.NumPad0) && iPos < this.TextLength)
{
if(cText[iPos] == '.' || cText[iPos] == ':'
|| cText[iPos] == '/')
iPos+=1;
this.SelectionStart = iPos;
}
base.OnKeyUp (e);
}
OnKeyPress event
protected override void OnKeyPress(KeyPressEventArgs e)
{
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
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.
if((char.IsLetterOrDigit(Convert.ToChar(e.KeyValue)) ||
e.KeyCode == Keys.NumPad0) 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
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
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);
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 public enum IPNotation
{
IPv4Decimal,
IPv4Binary,
IPv4DecimalCIDR,
IPv4BinaryCIDR,
IPv6Hexadecimal,
IPv6Binary,
IPv6HexadecimalCIDR,
IPv6BinaryCIDR,
IPv6IPv4Decimal,
IPv6IPv4Binary,
IPv6IPv4DecimalCIDR,
IPv6IPv4BinaryCIDR
}
- 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)
protected override void OnKeyDown(KeyEventArgs e)
{
int iPos = this.SelectionStart;
char[] cText = this.Text.ToCharArray();
if(e.Modifiers == Keys.None)
{
}
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