[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,
Http,
Ftp,
Email
}
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 (!bLinkClicked) {
this.Focus();
bLinkClicked = false;
}
}
private void llLinkLabel_MouseDown(object sender, MouseEventArgs e) {
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.