Introduction
Very often, we (UI designers) are in situations where we have to capture a large amount of information from the user in a single screen. In such cases, it seems impossible to add enough labels and textboxes in a single form due to space considerations and the dimensions of the form. Nowadays, we see many forms over the internet which solve this problem. The idea being "why not add the label to the textbox itself?" I.e., can't the textbox itself act as the label (caption) and capture User Input at the same time?
This aritcle tries to solve these kind of problems with the use of a custom made control which I have used in many of my commercial applications. Moreover, it is a straight out-of-the-box control with a custom UI Smart Tag Designer, which is very handy.
Background
In one of my applications, I was left with just enough space on the form to accommodate two text boxes. What I thought was why not use the ways similar to Internet web forms and add a textbox which can act as both a textbox as well as a label.
I quickly Googled and found some similar controls which solved my problem. Well, it seemed so,. The control which I used functioned like this:
- Embed a
Label
control in a TextBox
.
- Toggle the visibility of the
Label
when it get and loses focus.
This seemed to be a cool trick, but what pushed me to write my own control was, why in God's world should I use a Label
when only a string should ideally do the job?
I somehow completed the application and delivered it, but the quest to develop such a control was always rolling in the back of my mind. So there I was, left with an inquisitive mind and a need to develop such a control which I could use across all my applications, and that too with a Smart Tag Designer so as to quickly do the job of preparing the UI.
Control Code
Well, the control code is much simpler than it seems to be, though a little lengthy. You can browse through the source code in the Zip files above.
Idea
- Add a property of type
string
and use it to swap with the Text
property of base TextBox
on getting and losing focus.
- Use some swap variables to hold the values of the
Forecolor
and Text
properties.
SmartTag Designer
This was a bit tough to configure, but with some initial problems, I found out that the Smart Tags are dependent on Smart Tag UI Designers which are inherited from the System.Windows.Forms.Design.ControlDesigner
class present in the System.Design.dll assembly.
Every inherited class should have a DesignerActionListCollection
member variable which holds the reference to a class which holds the properties which should be displayed in the SmartTag.
The actual class which contains the properties to display is the one which is inherited from the DesignerActionList
class.
Anyways, this is a bit too tricky... here you go with the code:
[System.Security.Permissions.PermissionSet(
System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class ColorLabelDesigner : System.Windows.Forms.Design.ControlDesigner
{
private DesignerActionListCollection actionLists;
public override DesignerActionListCollection ActionLists
{
get
{
if (null == actionLists)
{
actionLists = new DesignerActionListCollection();
actionLists.Add(new HintTextBoxDesignerActionList (this.Component));
}
return actionLists;
}
}
}
internal class HintTextBoxDesignerActionList : DesignerActionList
{
HintTextBox _BaseControl;
private DesignerActionUIService designerActionUISvc = null;
public HintTextBoxDesignerActionList(IComponent component)
: base(component)
{
try
{
_BaseControl = (HintTextBox)component;
this.designerActionUISvc =
GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;
}
catch (Exception ex)
{
throw ex;
}
}
private PropertyDescriptor GetPropertyByName(String propName)
{
PropertyDescriptor prop;
prop = TypeDescriptor.GetProperties(_BaseControl)[propName];
if (null == prop)
throw new ArgumentException(
"Matching property not found!",
propName);
else
return prop;
}
public bool IsPassword
{
get { return _BaseControl.IsPassword; }
set
{
GetPropertyByName("IsPassword").SetValue(_BaseControl, value);
_BaseControl.Multiline = false;
this.designerActionUISvc.Refresh(this.Component);
}
}
public bool MultiLine
{
get { return _BaseControl.Multiline; }
set
{
GetPropertyByName("Multiline").SetValue(_BaseControl, value);
_BaseControl.IsPassword = false;
this.designerActionUISvc.Refresh(this.Component);
}
}
public string Hint
{
get { return _BaseControl.Hint; }
set
{
GetPropertyByName("Hint").SetValue(_BaseControl, value);
this.designerActionUISvc.Refresh(this.Component);
}
}
public string Name
{
get { return _BaseControl.Name; }
set
{
GetPropertyByName("Name").SetValue(_BaseControl, value);
this.designerActionUISvc.Refresh(this.Component);
}
}
public Color HintColor
{
get { return _BaseControl.HintColor; }
set
{
GetPropertyByName("HintColor").SetValue(_BaseControl, value);
this.designerActionUISvc.Refresh(this.Component);
}
}
public Color TextColor
{
get { return _BaseControl.TextColor; }
set
{
GetPropertyByName("TextColor").SetValue(_BaseControl, value);
this.designerActionUISvc.Refresh(this.Component);
}
}
public HorizontalAlignment HintAlignment
{
get { return _BaseControl.HintAlignment; }
set
{
GetPropertyByName("HintAlignment").SetValue(_BaseControl, value);
this.designerActionUISvc.Refresh(this.Component);
}
}
public override DesignerActionItemCollection GetSortedActionItems()
{
DesignerActionItemCollection items = new DesignerActionItemCollection();
items.Add(new DesignerActionHeaderItem("Design"));
items.Add(new DesignerActionHeaderItem("Behaviour"));
items.Add(new DesignerActionHeaderItem("Appearance"));
items.Add(new DesignerActionPropertyItem("IsPassword",
"Password Field", "Behaviour",
"Is it a password field ?"));
items.Add(new DesignerActionPropertyItem("MultiLine",
"MultiLine", "Behaviour",
"Multiline Text Field"));
items.Add(new DesignerActionPropertyItem("HintColor",
"Hint Color", "Appearance",
"Sets the Hint Text color."));
items.Add(new DesignerActionPropertyItem("TextColor",
"Text Color", "Appearance",
"Sets the Text Color."));
items.Add(new DesignerActionPropertyItem("HintAlignment",
"HintAlignment", "Appearance",
"Sets the Hint Alignment."));
items.Add(new DesignerActionPropertyItem("Name",
"Control Name", "Design",
"Sets the Control Name."));
items.Add(new DesignerActionPropertyItem("Hint",
"Hint", "Design",
"Sets the Hint text."));
items.Add(new DesignerActionMethodItem(this,
"DefaultColors", "Set Default Colors",
"Appearance",
"Sets default Hint and Text colors.",
true));
return items;
}
public void DefaultColors()
{
this._BaseControl.HintColor = Color.Gray;
this._BaseControl.TextColor = Color.Black;
this.designerActionUISvc.Refresh(this.Component);
}
All you now have to do is add the designer type attribute on top of the control class which we have prepared:
[Designer(typeof(ColorLabelDesigner))]
public class HintTextBox
Result
For those who are trying to modify the SmartTag UI, don't forget to add a reference to System.Design.dll found here.
Using the Control
To use the control, first drag HintTextBox.dll on the the toolbar (any toolbar) and you should be able to see a control named "HintTextBox
".
There you are.. ready to go.. drag and drop it on any form and use it as you like...
Just remember to use the InternalText
property to access user input text instead of using the traditional Text
property.
History
- Added the HintAlignment property.
- Rectified the source code to make the
ForeColor
property work.
N.B.: New property name: TextColor
.
- Modified the Smart Tag to add extra properties and categorical support for them.