Bite my bytes

What I learn by day I blog at night - A blog from Microsoft Consultant working from Ljubljana, Slovenia

  Home :: Contact :: Syndication  
  999 Posts :: 7691 Comments :: 235 Trackbacks

Search

Most popular posts

Categories

My Projects

Archives

Stuff


Copyright © by David Vidmar
 
Contact me!
 
LinkedIn Profile
 
 
 

[This is a copy of the article as submitted to CodeProject. Any changes to it will be published here and on CodeProject. ]

  • Download source files - 14.7 Kb
  • Download demo project - 6.89 Kb
  • 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 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 it 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 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 a Add Inherited Control... wizard, but I usually don't. I can type inheritance declaration faster than click through the wizard. In constructor, lets 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 a enum for various types of links. We need one that tells user the link feature is disabled, one for email links and one for 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 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 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 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 arround.

    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 everytime 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();
    }
    }

    It's not very obvious but later on you will realize that some strange focus realted issue will surface. If you give control a focus through tab it will work different than if you shift-tab to control. With some tricky focus handling the issue is easily 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 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 looses 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);
    }

    Now, let's make this thing clickable! In 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 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 user test the link. If 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 as 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 simple combining of two basic controls and some simple UI processing.

    So, before you start googlin' or even throw couple of bucks out the window for a commercial control, think about writting 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:// link, issue related to different ways of giving this control focus is resolved.

    If you find a bug or have an idea how to extend the LinkTextBox, please let me know.

    Posted on Sunday, December 26, 2004 11:15 PM | |

    Feedback

    # LinkTextBox on CodeProject 12/26/2004 11:39 PM vidmar.net/weblog


    # LinkTextBox on CodeProject 12/26/2004 11:46 PM vidmar.net/weblog


    Comments have been closed on this topic.