Click here to Skip to main content
Click here to Skip to main content

Why Develop Custom Controls? Just Customize Generic Controls. Part 2: InTextboxLabel

, 24 Jan 2007
Rate this:
Please Sign up or sign in to vote.
Embed a label inside the text box for compact UI. No need to develop a derived class of TextBox

Introduction

Sometimes, you might need to provide a small window for the user to input a small amount of data with a few text boxes. For example, you want to provide an edit box for defining keyword of search, and the edit box will be sitting at the Windows task bar. It might be more elegant that a label indicating the purpose of the text box will not occupy more space of the task bar. An easy approach is just to use a hint property of the text box. However, you will need to have the mouse pointer over the text box to show hint. It will be handy to show the hint all the time. A good existing instance of this example is Google Desktop's Deskbar.

When you don't have keywords defined, "Google" inside the text box tells you to Google(search). After you focus on the text box, or the text box has some content, the "Google" hint inside the text box will disappear.

From a certain point to view, within a small window with a few text boxes, you will always know the purposes of the boxes after a few instances of running. In some scenario, it is good to keep the window small. To save space for data input, the UI design like Google Deskbar will be handy and economic.

While there can be various approaches of implementing this feature, I am not going to develop a new derived class from class TextBox, rather, I would just plug these features to a text box. The in-box label is shown when you are not using the box.

If the box has content or is being focused, the in-box label will disappear.

Using the Code

The code was constructed with Visual Studio 2005. This project in the above download link contains two example classes: Flashing and InTextBoxLabel. The second one will be discussed in the next article.

   public class InTextboxLabel
   {
       protected TextBoxBase box;
       protected string hint;
       protected Label label;

       public InTextboxLabel(TextBoxBase box, string hint)
       {
           this.box = box;
           this.hint = hint;

           box.Enter += new EventHandler(box_Enter);
           box.Leave += new EventHandler(box_Leave);
           box.TextChanged += new EventHandler(box_TextChanged);

           label = new Label();
           label.Text = hint;
           label.ForeColor = SystemColors.ActiveBorder;
           box.Controls.Add(label);
           label.Dock = DockStyle.Fill;
           label.Click += new EventHandler(panel_Click);

           if (String.IsNullOrEmpty(box.Text))
               label.Show();
       }

       void box_TextChanged(object sender, EventArgs e)
       {
           if (!String.IsNullOrEmpty(box.Text))
               label.Hide();
           else if (! box.Focused)
               label.Show();
       }

       void panel_Click(object sender, EventArgs e)
       {
           label.Hide();
           box.Select();
       }

       void box_Leave(object sender, EventArgs e)
       {
           if (String.IsNullOrEmpty(box.Text))
               label.Show();
       }

       void box_Enter(object sender, EventArgs e)
       {
           label.Hide();
       }
   }

In the client code of a Form, you may plug class InTextboxLabel to TextBox objects. When the form is loaded, we have labels embedded in text boxes.

private void Form1_Load(object sender, EventArgs e)
{
    new InTextboxLabel(textBox1, "User Name");
    new InTextboxLabel(maskedTextBox1, "Password");
}

Will those InTextboxLabel objects be disposed after Form1_Load is finished? No.

InTextboxLabel provides visual effects to the TextBox object through subscribing to some events of the TextBox object, thus the TextBox object has references to the InTextboxLabel object.

In the attached source code, you may also find a class named InTextBoxGraphic, which can give some visual effects like the one in the Google Taskbar.

What's This Design Pattern?

Rather than developing something like the TextboxWithEmbeddedLabel class derived from the Textbox class, we have InTextboxLable being plugged into a TextBox object. This approach is light-weight and flexible. It can work on derived classes of Textbox like class MaskedTextBox, and in addition, you may merge features introduced by the same design pattern, for example, if you have codes like this:

new InTextboxLabel(textBox1, "User Name");
new Flashing(textBox1);

Now we have a flashing text box with an embedded label.

As you can see, this design pattern is based on the builder pattern with extension. The director subscribes to some events of the builder, thus the builder now has a reference to the director. Shall we continue to call this builder pattern? or helper? Just looks similar.

Somehow I came up with names like Agent and Parasite.

As we knew, a pattern is named after a logical structure, call sequences and purposes. I haven't yet known the "official" name that might exist in the programming world. Maybe you can tell me.

Anyway, for the time being, I would call it the Agent pattern, and the class being developed is called the Agent class.

Points of Interest

You might be thinking of introducing an in-box label to other Windows controls like ComboBox or DatetimePicker. Moreover, you might consider evolving this InTextboxLabel into something like InBoxLabel to work on any derived class of the Control class. While it is easy to change the above code, you need to be aware that in different derived classes of the Control class, the Text property has different meanings, for example, property Text in class DateTimePicker and class Panel is inappropriate.

Where Is the IDisposable Interface?

You might be wondering why class InTextboxLabel does not implement the Idisposable interface, as the class contains a Label object which implements the IDisposable interface. Microsoft FxCop will cry out for this.

When the form or the text box with InTextboxLable is disposed, will the Label object created inside InTextboxLabel be safely disposed? The answer is yes. Though the Label object is created inside InTextboxLabel, it is then assigned to the Controls property of the TextBox object which implement IDisposable. When the TextBox object is disposed, the Controls of the TextBox will be disposed, and the Label object attached will go, then the sequence reaches InTextboxLabel. As the Textbox object is the only one having the reference to InTextboxLabel, GAC reached the end and will dispose InTextboxLabel.  I have tested with Spy+ to monitor the Windows resources allocated. Spy+ showed that the window handle to the Label was gone when the TextBox was disposed.

Yes, it is more robust to have IDisposable implemented if you are going to evolve this class for flexibility of adapting different use cases. The current implementation works for these scenarios:

  1. The agent (parasite) always lives with the host TextBox. It is a long live object with the host.
  2. Nobody else but the host has reference to the agent.

I had some very thoughtful discussions with a colleague who has in-depth knowledge and experience of .NET. He pointed out many weaknesses of the implementation regarding a greater vision. The following works will make the class more healthy and more robust for different scenarios:

  1. Implement IDisposable interface

  2. Hide the constructors, and provide some static functions delivering the instantiated class. So, we will have something like:

    public static InTextboxLabel AttachLabelToTextBox(TextBox textBox, string hint) 
    	{return new InTextboxLabel(textbox, hint); } 
    

I agree this will make the interfaces of the class more meaningful and more friendly. Actually in .NET 2, there are a lot of classes delivering objects this way.

After all, I will just remind you that the discussion around IDisposable in this section is not part of this design pattern, as this pattern does not necessarily create disposable objects inside.

Agile Approach vs. Component Suits

I have been using this design pattern for years, with Delphi coding, when I felt that developing a derived class or using a component suit is overkill or too expensive.

Generally I don't like a very fancy UI, skinny things, strange shape buttons, etc. which I consider are too disturbing and confusing.  I generally just need a little bit extra from existing generic Windows controls provided by the development tools like Delphi and Visual Studio. There are many high quality 3rd party components delivering powerful and consistent UI experiences, and I do use them, such as Jedi Libraries and Turbo Power suits. They are open sources, and it is quite safe to stay with them. I did sometimes use some commercial packages like Info Power, mostly only when I needed some powerful UI features urgently. Though I could purchase the source code, I did not have the interest or resources to maintain these heavy codes.

As a matter of fact, I gradually had removed visual components of Info Power suit from my legacy projects when I had time to do so, replaced the codes with the agent pattern described above. The codes are short and easy to maintain. Just let me outline all benefits of this agile approach.

  1. To implement a similar set of visual effects, the code of the agent pattern is shorter than that in component suits.
  2. Because the agent class talks to an existing class, when the existing classes get upgraded, the agent class can be compiled with these upgraded classes seamlessly. If I used component suits, it could be common that I had to get XX suit for Delphi 5, and then XX suit for Delphi 6, 10, ...because the source codes of XX suit for Delphi 5 might mostly not be compiled with Delphi 7.
  3. Comparing with the approach of deriving from an existing class, the agent class can work with any derived classes of the existing class. If you use the approach of deriving, you will then have to have a base class with the special features in order to deliver the features to derived classes. However, if the derived classes are from different vendors, you will have trouble.
  4. You may merge features of agent classes into the same client object, without changing the class structure. You just need to plug the agent object into the client object in client code.

Of course, the Agent pattern is not a silver bullet. It has its own use cases and limitations.

  1. You need spend some time to plan, design and implement. You won't do it when you have a closing deadline and there exist free or commercial packages around.
  2. These agent classes might be hard for those junior programmers to use, who only get used to drag-dropping components from tool palettes to design time forms.

Although previous and latter examples are with visual controls, this design pattern is not limited to visual controls. Later, I will provide some links to some examples codes which were for TDataset in Delphi.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Zijian
Software Developer
Australia Australia
I started my IT career in programming on different embedded devices since 1992, such as credit card readers, smart card readers and Palm Pilot. Programming on the hardware was really fun, feeling like driving the hardware directly.
 
Beside technical works, I enjoy reading literatures, playing balls, cooking and gardening.

Comments and Discussions

 
GeneralIsnt it like .. PinmemberMr.Prakash7-Feb-07 22:44 
GeneralAttached files [modified] Pinmembergxdata22-Jan-07 22:54 
AnswerRe: Attached files PinmemberZijian23-Jan-07 19:13 
GeneralRe: Attached files Pinmembergxdata23-Jan-07 19:49 
GeneralDecorator Pattern PinmemberSteve Hansen18-Jan-07 3:34 
GeneralRe: Decorator Pattern PinmemberZijian18-Jan-07 13:41 
GeneralSomehow I came up names like Agent and Parasite. PinmemberIlíon18-Jan-07 3:18 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 24 Jan 2007
Article Copyright 2007 by Zijian
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid