Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

LinkTextBox - a quick solution for entering emails and hyperlinks

0.00/5 (No votes)
11 Apr 2005 1  
A guide to creating a cool looking LinkTextBox control that handles email and other hyperlinks just like MS Outlook.

Sample Image - LinkTextBox.png

Introduction

Do you want to impress your boss and create professional looking hyperlink entry forms for $0 in only 10 minutes? Who wouldn't? With regular .NET TextBox and HyperLink controls combined, that is really easy. Just follow a few simple steps in this article and you'll have your own full featured LinkTextBox control.

Background

Remember how Email and Web Page textbox in Microsoft Outlook work as a hyperlink when they are not focused, and as a regular textbox when they have focus? If you don't, just open your Outlook and then come back to this article...

Using the code

Let's figure out how it works first. The control is actually a combination of TextBox and LinkLabel controls. HyperLink control is positioned so that it overlaps the TextBox's text and intercepts the clicks. But if a user clicks in the TextBox and not on the LinkLabel, the TextBox gets the focus and LinkLabel hides. LinkTextBox acts and looks exactly as a regular TextBox until it loses focus. Then the LinkLabel is made visible again and the user can click on the link.

What you want to do is extend the regular TextBox. You can do that in your existing application, in any of your libraries, or create a new project and add a Class Object file and extend the System.Windows.Forms.TextBox. You could also use the Add Inherited Control... wizard, but I usually don't. I can type inheritance declaration faster than click through the wizard. In the constructor, let's create an instance of LinkLabel and position it so that it exactly overlaps the TextBox's text. We also need to show it and copy the TextBox.Text to the LinkLabel so it looks OK at design time. We also need to wire up a Clicked event in here.

namespace DavidVidmar.Windows.Forms {

    public class LinkTextBox : TextBox {

        private LinkLabel llLinkLabel;

        public LinkTextBox() {

            llLinkLabel = new LinkLabel();

            this.Controls.Add(llLinkLabel);                 

            llLinkLabel.AutoSize = true;
            llLinkLabel.Left = -1;
            llLinkLabel.Top = 1;
            llLinkLabel.Visible = true;
            llLinkLabel.Text = this.Text;

            llLinkLabel.LinkClicked += 
              new LinkLabelLinkClickedEventHandler(ll_LinkClicked);

        }
    }
}

Next, let's prepare an enum for various types of links. We need one that tells the user the link feature is disabled, one for email links, one for FTP links and one for web links or secure web links.

public enum LinkTypes {
    None,     // act as a regulator TextBox

    Http,     // act as a http:// or https:// hyperlink

    Ftp,      // act as a ftp:// hyperlink

    Email     // act as a mailto: hyperlink

}

Now, we can add a LinkType property to LinkTextBox. It tells what kind of link the Text property represents and if it is a link at all. By default, the LinkTextBox will work as a regular TextBox and we need to hide a LinkLabel. We'll call this mode edit mode and we'll use a SwitchToEditMode() method that we will create later. But if it is a real LinkTextBox, we need to switch to clickable mode, where LinkLabel is visible, again with SwitchToEditMode() just with a different parameter value. We also need to copy the information from TextBox to LinkLabel. Let's assume for a moment FillLinkData() method does just that.

private LinkTypes ltLinkType = LinkTypes.None;

[DefaultValue(LinkTypes.None)]
public LinkTypes LinkType {
    set { 
        this.ltLinkType = value; 
        if (value == LinkTypes.None) {
            SwitchToEditMode(true);
        } else {
            SwitchToEditMode(false);
            FillLinkData();
        }
    }
    get { return this.ltLinkType; }
}

The SwitchToEditMode() method only needs to show or hide the LinkLabel. We can easily do that in just one line of code:

protected void SwitchToEditMode(bool _bEditMode) {
    llLinkLabel.Visible = !_bEditMode;
}

The FillLinkData() method we used in the property setter is a little more complicated. We need to copy the value of Text property from TextBox to LinkLabel, try to figure out what protocol to use in our link, and update the Links array of LinkLabel. The easiest way to do that is to create a new entry each time around.

private void FillLinkData() {

    llLinkLabel.Text = this.Text;

    string sLinkType = "";
    switch (ltLinkType) {
        case LinkTypes.Http:            
            if (this.Text.ToLower().IndexOf(@"http://") < 0 && 
                this.Text.ToLower().IndexOf(@"https://") < 0) {            
                sLinkType = @"http://";
            }
            break;
        case LinkTypes.Ftp:
            if (this.Text.ToLower().IndexOf(@"ftp://") < 0) {
                sLinkType = @"ftp://";
            }
            break;            
        case LinkTypes.Email:
            if (this.Text.ToLower().IndexOf("mailto:") < 0) {
                sLinkType = "mailto:";
            }
            break;
    }

    llLinkLabel.Links.Clear();
    llLinkLabel.Links.Add(0, llLinkLabel.Text.Length, sLinkType + this.Text);

}

We could figure out when exactly to copy the text from TextBox to LinkLabel, but the easy way out is every time the Text property on TextBox changes. It's not optimal, but I don't think anybody will notice that.

protected override void OnTextChanged(EventArgs e) {
    base.OnTextChanged (e);
    if (ltLinkType != LinkTypes.None) {
        FillLinkData();
    }
}

Now, let's cover the switching from editable to clickable mode. If TextBox control gets the focus, user must have clicked to the right of the LinkLabel in TextBox, so we need to switch to edit mode. If control loses focus, we need to switch to clickable mode. We already created SwitchToEditMode() method that will do everything. All we need to do is override the OnGetFocus() and OnLostFocus() methods.

protected override void OnGotFocus(EventArgs e) {
    base.OnGotFocus(e);
    if (ltLinkType != LinkTypes.None) this.SwitchToEditMode(true);
}

protected override void OnLostFocus(System.EventArgs e) {
    base.OnLostFocus(e);
    if (ltLinkType != LinkTypes.None) this.SwitchToEditMode(false);
}

It's not very obvious but later on you will realize that some strange focus related issue will surface. If you give focus to a control through tab it will be different than if you shift-tab to the control. With some tricky focus handling, the issue is resolved.

    private void llLinkLabel_GotFocus(object sender, EventArgs e) {      
      // if control got focus with tab and not because user clicked a link

      // then transfer focus to TextBox and clear the flag

      if (!bLinkClicked) {
        this.Focus();
        bLinkClicked = false;
      }
    }

    private void llLinkLabel_MouseDown(object sender, MouseEventArgs e) {
      // remember that user clicked on the label,

      // so we can correct the focus of a label

      bLinkClicked = true;
    }

Now, let's make this thing clickable! In the constructor, we already wired the event, now let's code it. We need to check if we should use a link at all and then call a UserHyperlink() method that will actually open the link.

private void ll_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
    if (ltLinkType != LinkTypes.None) UseHyperlink();
}

Now, let's figure out how to open a link. Don't even start thinking about directly interacting with IE, Outlook, or any other browser or mail application. Let's use the OS to figure out what to do with the link. This way, we just execute the link, and Windows will open either a default mail application or default browser. This is the right way to do it. Just create a new Process class, call Start() method with the link, and keep your fingers crossed. The bad part is that you don't really know if it will work. Just catch an exception and throw a new one. In an application, just catch this exception, show a MessageBox, and give the user a chance to figure out what's wrong with the link.

private void UseHyperlink() {
    try {
        if (llLinkLabel.Links.Count > 0) {
            string sLink = llLinkLabel.Links[0].LinkData.ToString();
            System.Diagnostics.Process.Start(sLink);
        }
    } catch (Exception ex) {
        throw new ArgumentException("Link error!", ex);
    }
}

We can easily build in one more feature. If the control is in the edit mode (LinkLabel is hidden), we can help the user test the link. If the user clicks in the control while the Ctrl key is pressed, he can launch the link.

protected override void OnMouseDown(MouseEventArgs e) {
    if (ltLinkType != LinkTypes.None) {
        if (e.Button == MouseButtons.Left && 
           (Control.ModifierKeys & Keys.Control) == Keys.Control) {
            UseHyperlink();
        } else {
            base.OnMouseDown(e);
        }
    } else {
        base.OnMouseDown(e);
    }
}

And that's it. The control is ready for use. Just set the LinkType property and the control is ready for some serious clickin'! Or you could even let the user decide if the text in the LinkTextBox is a link with a ComboBox next to it!

I hope you realized how you can create quite powerful controls that could be easily a part of an expensive UI library, with a simple combination of two basic controls and some simple UI processing.

So, before you start Googlin' or even throwing couple of bucks out the window for a commercial control, think about writing your own control. It's the best in terms of control, and when it's this simple, it's really fun.

History

  • v1.0 - Initial release.
  • v1.1 - Added support for https:// and ftp:// links, issues related to different ways of giving focus to the control is resolved.

Keep checking this page out to see if anyone has found a bug or got an idea of how to extend the LinkTextBox.

You can also come over to my blog and see if there is anything new cooking.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here