Introduction
I'm a huge fan of reusable code and for the last 10 years I have created a lot of libraries I have used in my company (mostly on Delphi and C/C++). Now that I'm migrating all my projects to C#/ASP.NET, I'm also rewriting those libraries, including some custom components I always loved.
The most important component for me is actually the TextBox
, since most of my work is based on database software that requires several forms to handle data and the idea of always knowing where you are and what you should do on a form is really nice.
Background
TextBoxEx
is an easy-to-use custom control and it tries to make the user aware about the current editing field and display tooltips inside all the other TextBox
es - no need to move the mouse over.
Features
- ToolTips in-place: The tooltip property is shown inside the control when it doesn't have the focus and unless the user types something else there.
- You can set the color for the ToolTip text to be used.
- You can set the colors for the font and for
TextBox
when the focus is on it. - You can link it to a
Label
, so when it gets the focus, the font-style of this label is set to bold.
Using the Code
You can either add the TextBoxEx.cs and TextBoxExEditor.cs to your project or to your Custom Control library that you're creating. Then you just place the TextBoxEx
control into your WebForm.
Warning: If you don't know how to use custom control, you must read something about it first. This article doesn't explain how you do it, but Your First ASP.NET Custom Control does.
How It Works
- A JavaScript code handles the
onblur
and onfocus
input control events. - Another JavaScript code is registered to the form
onsubmit
event so the tooltip text is removed from the unfulfilled TextBoxExes
. - When you select a
TextBoxEx
at design time and go to the Label
property, you can see a list of labels available for selection. You can set the link from more than one TextBoxEx
to the same Label
if it makes sense to you.
Linking to a Label
A special ListBox
If you use the DropDownControl
passing a list box as parameter, it will be shown OK, but it will close only when you press ENTER, so I made a ListBox
class that gets a IWindowsFormsEditorService
as parameter in the constructor and subscribe to the ListBox OnClick
event and close it.
public class ListBoxEditor : System.Windows.Forms.ListBox
{
IWindowsFormsEditorService SRV = null;
private ListBoxEditor() { }
internal ListBoxEditor(IWindowsFormsEditorService srv)
{
SRV = srv;
Sorted = true;
this.Click += new EventHandler(ListBoxEditor_Click);
}
void ListBoxEditor_Click(object sender, EventArgs e)
{
if (SRV != null)
SRV.CloseDropDown();
}
}
Listing the Labels Available at Design-Time
The IReferenceService
provides several methods that allow you to get instances of all controls available at design-time.
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
IWindowsFormsEditorService srv = null;
if (provider != null)
srv = (IWindowsFormsEditorService)provider.GetService(
typeof(IWindowsFormsEditorService));
if (srv != null)
{
ListBoxEditor lb = new ListBoxEditor(srv);
lb.Sorted = true;
IReferenceService irs =
(IReferenceService)provider.GetService(
typeof(IReferenceService));
if (irs != null)
{
object[] os = irs.GetReferences(typeof(Label));
foreach (object o in os)
{
Label wc = (Label)o;
lb.Items.Add(wc.ID);
}
}
lb.SelectedIndex = lb.Items.IndexOf((string)value);
srv.DropDownControl(lb);
return lb.Text;
}
return value;
}
Client-side Programming
The JavaScript part was by far the easiest part and the integration provided by ASP.NET is great!!!
I just had to create the JavaScript code, register it and then add the attributes to the control. But it's really important to remember that I had also to subscribe to the onsubmit form event to deal with unfulfilled TextBoxEx
controls, because I had to clear their values so the hints wouldn't be sent instead of the empty values.
private void RegisterScript()
{
#region Main Script Code
string script = @"
<script type='text/javascript'>
function JRTextBox_ChangeStyles_Enter(id, hint, backcolor, fontcolor)
{
if(id.value==hint)id.value='';
id.style.color = fontcolor;
id.style.backgroundColor = backcolor;
}
function JRTextBox_ChangeStyles_Enter_Label(label, id, hint, backcolor, fontcolor)
{
JRTextBox_ChangeStyles_Enter(id, hint, backcolor, fontcolor);
elem = document.getElementById(label);
if(elem!=null)
{
elem.style.fontWeight = 'bold';
}
}
function JRTextBox_ChangeStyles_Exit(id, hint, hintfontcolor, backcolor, fontcolor)
{
if(id.value=='')
{
id.value=hint;
id.style.color = hintfontcolor;
id.style.backgroundColor = backcolor;
}
else
{
id.style.color = fontcolor;
id.style.backgroundColor = backcolor;
}
}
function JRTextBox_ChangeStyles_Exit_Label(label, id, hint,
hintfontcolor, backcolor, fontcolor)
{
JRTextBox_ChangeStyles_Exit(id, hint, hintfontcolor, backcolor, fontcolor);
elem = document.getElementById(label);
if(elem!=null)
{
elem.style.fontWeight = 'normal';
}
}
function JRTextBox_ValidateHint(id, hint)
{
elem = document.getElementById(id);
if(elem!=null)
{
if(elem.value==hint)elem.value='';
}
}
</script>";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
ScriptName, script);
#endregion
#region OnSubmit
Page.ClientScript.RegisterOnSubmitStatement(this.GetType(),
string.Format("{0}_{1}_OnSubmit", ScriptName, ID),
string.Format("JRTextBox_ValidateHint('{0}','{1}');\n",
ClientID, ToolTip)
);
#endregion
} protected override void Render(HtmlTextWriter output)
{
if (!string.IsNullOrEmpty(Label))
{
LabelID = GetID(Page.Controls);
}
if (!string.IsNullOrEmpty(LabelID))
{
output.AddAttribute("onblur",
"JRTextBox_ChangeStyles_Exit_Label('" +
LabelID + "'," + ClientID + ",'" + ToolTip + "','" +
ColorTranslator.ToHtml(FontColorToolTip) + "','" +
ColorTranslator.ToHtml(BackColor) + "','" +
ColorTranslator.ToHtml(OldForeColor) +
"');"
);
output.AddAttribute("onfocus",
"JRTextBox_ChangeStyles_Enter_Label('" +
LabelID + "'," + ClientID + ",'" + ToolTip + "','" +
ColorTranslator.ToHtml(BackColorEnter) + "','" +
ColorTranslator.ToHtml(FontColorEnter) +
"');"
);
}
else
{
output.AddAttribute("onblur",
"JRTextBox_ChangeStyles_Exit(" +
ClientID + ",'" + ToolTip + "'," +
ColorTranslator.ToHtml(FontColorToolTip) + "," +
ColorTranslator.ToHtml(BackColor) + "," +
ColorTranslator.ToHtml(OldForeColor) +
");"
);
output.AddAttribute("onfocus",
"JRTextBox_ChangeStyles_Enter(" +
ClientID + ",'" + ToolTip + "'," +
ColorTranslator.ToHtml(BackColorEnter) + "," +
ColorTranslator.ToHtml(FontColorEnter) +
");"
);
}
base.Render(output);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
OldForeColor = ForeColor;
if (string.IsNullOrEmpty(Text))
{
Text = ToolTip;
ForeColor = FontColorToolTip;
}
if (!Page.ClientScript.IsClientScriptBlockRegistered(ScriptName))
RegisterScript();
}
Suggestions
- Create some kind of font-style property, so you can set most font attributes for the
onfocus
event (for the Label
and for the TextBoxEx
). The way it is now, it works for me, but someone may want to change the Label
style to italic and blue, instead of just making it bold.
History
- December 04, 2008 - Initial release
- December 05, 2008 - I pasted and explained come of the important code here as suggested
Working for about 10 years with Delphi/C/C++/Java technology and now migrating all Delphi products I have made to C# and ASP.NET.