Designing Inherited Controls For Winforms
Introduction
If you have worked on VB6 based thick client applications, one commonproblem that you as a developer would have countered are the waycontrols work. There were a handful of issues in terms of customizationof controls. .Net answers them through use of customizing existingcontrols using Inherited controls or creating a new control byinheriting User Control. This article uses Winforms TextBox control toexplain on how to design your controls.
There is difference between Inherited Controlsand User Controls in practical implementation even though conceptuallyboth can be grouped under Custom Controls category. Inherited controlsare those existing controls that user would inherit to add customfunctionalities. E.g. TextBox, DataGrid; Usually these are used forcommon validations, GUI features etc. User Controls are controls thatuser creates using either one or more controls, add some commonapplication logic. This inherits "System.Windows.Forms.UserControl"; soone can add a Combo Box, DataGrid, ImageList etc. Make the modifierspublic, protected; add some common application logic like on selectionof Combo value the DataGrid is revalidated.
Why?
Consider a very simple scenario inWinforms. I want to validate a user's entry to a TexBox control i.eeither numeric, text, URL etc. One way to do that would be just addkeypress event of textbox as follows:
public class frmTestControls : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox txtLogin;
private System.ComponentModel.IContainer components;
public frmTestControls()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.txtLogin = new System.Windows.Forms.TextBox();
this.KeyPress += new KeyPressEventHandler(OnKeyPress);
this.SuspendLayout();
//Some more code goes here �
}
private void OnKeyPress(object sender,KeyPressEventArgs e)
{
//Some code goes here �
}
}
Let us consider that you did the above for atextbox control that should allow only numerical entries and had aspecific format "0.00". This was implemented throughout the applicationthat had around 30-40 user input boxes of type numeric. One fine dayyou want the numeric format to be "0.000". Now to get this small changedone we may have to once again do a code change through out theapplication. Many might argue that I would use application config filesto get these values. I agree, but consider a situation that had eventslike MouseDown, Leave, Enter, LostFocus etc that may have been used foreach of these textboxes for whatever GUI features or common validationsas applicable. A better approach to do this would be to use theInherited Controls judiciously.
Sample Implementation
Objective: Design for common TextBox control for the application to manage changes with ease.
Step 1:
This textbox should be flexible tovalidate for all type of user entries. First design your constructorsfor controls. In this case nothing much to do in terms of overloading,hence we go with the default constructor. Create an Enum for validationtypes to implement the same. I use Enum because today I have onlyNumeric and Text as the types but in future I could add URL, SocialSecurity Number, some specifc patterns to identify inventory bins like'000-XXX' etc. Then decide on Events based on the type of features andvalidations that the textbox must provide. In the example I haveselected the Leave and Enter events to add some UI features. KeyPressand LostFocus is an event that I require for validating most events.
namespace Orbit-e.Controls
{
public enum ValidationType
{
Text=0,
Numeric,
URL,
SSN,
InventoryBin_Pattern
}
public class in4CommonTextBox : System.Windows.Forms.TextBox
{
public in4CommonTextBox():base()
{
InitializeComponent();
this.AutoSize =false;
this.Width =160;
this.Height =20;
this.ForeColor = Color.Blue;
this.BackColor = Color.White;
this.BorderStyle = BorderStyle.Fixed3D;
//Set for all the events and write the corresponding events
this.KeyPress += new KeyPressEventHandler(OnKeyPress);
this.Leave +=new EventHandler(onLeave);
this.Enter +=new EventHandler(onEntering);
this.MouseDown += new MouseEventHandler(onMouseDown);
this.LostFocus +=new EventHandler(OnLostFocus);
}
}
}
Step 2:
Decide on how to handle the GUI features like Height, Width, color andstyle. If personalization features like themes are to be provided thenusually one way to do this is to create a user configuration file forstoring theme schemes that include BackColor, ForeColor. A commontechnique to figure out the default color schemes would help fordesigning other controls as well. The height and Width are veryimportant aspects since this helps maintain uniformity in the UI looks.Also add accessor to set the type of validation and make sure thatthere is always a default value assigned.
private int intValidType=(int) ValidationType.Text
public ValidationType ValidateFor
{
set
{
intValidType = (int) value;
}
}
Step 3:
By now we know what all validationswe have to do. Decide upon which event handlers to use for what. Useevents like MouseLeave, Enter, LostFocus, MouseHover to do UI Jazz.
protected void OnLostFocus(object sender,EventArgs e)
{
this.BackColor =Color.White;
this.ForeColor =Color.Blue;
}
protected void onEntering(object sender,EventArgs e)
{
this.SelectAll();
this.BackColor = Color.Blue ;
this.ForeColor = Color.White;
}
protected void onMouseDown(object sender,MouseEventArgs e)
{
this.SelectAll();
}
As a sample I have used the KeyPress event tovalidate for numeric value. The following code can be optimized furtheras required.
private int intDecimalPlace=2;
protected void OnKeyPress(object sender,KeyPressEventArgs e)
{
try
{
switch (intValidType)
{
case (int) ValidationType.Numeric:
{
#region "Numeric Validation Rule"
int intActDecPlace= this.Text.IndexOf(".");
//Check & Reset boolean if the decimal place does not //exist
if(intActDecPlace==-1)
{
blndecimalClick=false;
}
//If Keypressed is of type numeric or "."
if(Char.IsDigit(e.KeyChar))
{
#region "Decimal place check"
int intTotDecPlace= this.Text.IndexOf(".");
//Decimalplace validation
if(intTotDecPlace > 0 && ((this.Text.Length – intTotDecPlace) > intDecimalPlace))
{
e.Handled =true;
return;
}
#endregion
e.Handled =false;
}
else if(e.KeyChar.ToString()==".")
{
#region "Decimal validation"
if(blndecimalClick!=true)
{
blndecimalClick=true;
e.Handled =false;
}
else{e.Handled =true;}
#endregion
}
else
{
e.Handled =true;
}
#endregion
break;
}
case (int) ValidationType.Text:
{
//Nothing here..
break;
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,"Validation",MessageBoxButto
ns.OK,MessageBoxIcon.Error);
}
}
Also I have used the Leave event to do somepersonalization. Here I am using the local Windows settings format toshow numeric value.
protected void onLeave(object sender,EventArgs e)
{
if(this.intValidType ==(int) ValidationType.Numeric)
{
CultureInfo ci = CultureInfo.CreateSpecificCulture("en-US");
NumberFormatInfo ni = ci.NumberFormat ;
string strnumformat= d.ToString("c",ni) ;
this.Text = return strnumformat.Remove(0 ,1);
}
this.BackColor =bgColor;
this.ForeColor =fColor;
}
Step 4
We have seen how to create theinherited controls. You may ask what great have we done, we used theOOPS concepts to inherit and extend a controls behavior. I would saythat's not the end; we can keep extending this control by adding ourown events.
For E.g There is a high possibility that once user entered value wasvalidated for numeric, you would want to receive an event notificationon leaving the control. For this I would add a simple event that isfired within "Leave" event.namespace Orbit-e.Controls
{
public delegate void DuringLostFocusArgs(int ValidationType);
public enum ValidationType
{
Text=0,
Numeric,
URL,
SSN,
InventoryBin_Pattern
}
public class in4CommonTextBox : System.Windows.Forms.TextBox
{
public event DuringLostFocusArgs DuringLostFocusHandler;
//Code continues�
protected void onLeave(object sender,EventArgs e)
{
if(this.intValidType ==(int) ValidationType.Numeric)
{
CultureInfo ci = CultureInfo.CreateSpecificCulture("en-US");
NumberFormatInfo ni = ci.NumberFormat ;
DuringLostFocusArgs(intValidType);
string strnumformat= d.ToString("c",ni);
this.Text = return strnumformat.Remove(0 ,1);
}
this.BackColor =bgColor;
this.ForeColor =fColor;
}
}
While using this control one can un/subscribe to this event as follows:
in4CommonTextBox txtLogin = new in4CommonTextBox();
this.txtLogin.DuringLostFocusHandler += new DuringLostFocusArgs(RunTest);
private void RunTest(int ValidationValue)
{
MessageBox.Show(intI.ToString());
}
Conclusion
Inherited controls is a verystrong implementation concept especially if you are working on aWindows application. They help maintain your applications better.Design controls that are flexible and customizable with a thought onvarious Datatypes, Patterns that are specific to your application. Becareful not to over-do the intense operations in controls, rather movethem for form level validations since there will be specificvalidations at this level.
Happy Programming!
About the Author:
I am working as AssociateConsultant (Technical), Orbit-e Consulting-Bangalore. Expertise in MSbased technologies : VB6, ASP, COM+, SQL 2K, C#, ADO. Net, ASP. Net.




13. Feb, 2007 by 







No comments yet... Be the first to leave a reply!