1. Original Entry + Comments2. Write a Comment3. Preview Comment
New comments for this entry are disabled.

April 30, 2006  |  Custom email control  |  8624 hit(s)

For whatever reason, creating custom ASP.NET controls is something that is ... uh ... underrepresented in my skill set. As with certain other corners of the product, my theoretical knowledge outstrips my practical knowledge. So when I was messing around with a couple of things recently, I thought that they might be good practice scenarios for creating some custom controls.

The thing about custom controls is that the concept is really quite easy to grasp. You inherit from, say, WebControl and you override Render, and you're in business. And if you're working in VS2005, the thing practically writes itself. (haha) The base control class does an amazing amount of heavy lifting and -- again, in outline -- you only need to add your control-specific logic.

As happens, though, the difficulties lie in the details. I have found that to be true even with the very simple controls I've done -- there's always a gotcha or three that makes (at least for the less-experienced, like me) for a certain two-steps back type of development. Which I guess is where all the fun is.

The first control I fooled around with was a "dynamic" email control. (I'm pretty sure that an email control is the first thing most people fool around with, the "Hello, World" of control development.) In my case, I wanted a control that implemented the supposed anti-email-spambot feature that I learned about some time ago. The idea is that instead of setting the href attribute in an <a> tag to a mailto: value, you set it to a JavaScript function that dynamically constructs the mailto: value on the fly (on onclick and onmouseover) and assigns it to the href attribute. Look, ma, nothing for the spambot to find. (At least, not for dumb spambots.) I've done this procedurally in a page, so now it was time to encapsulate the behavior into a control.

I thought about listing the source here, but it's slightly too long, so I put it into an .htm page. These are the points to call out, I guess.

The control requires that JavaScript is enabled on the browser. There is no self-contained test for this (that I know of), so oh, well. We just assume.

When the user clicks the control, it does whatever mailto: does on their machine.

The control exposes these properties:
  • EmailLinkText -- what the user sees. If no value is specified, the control constructs the link text out of the target email name and displays it as EmailName AT Domain DOT Extension. (The strings " AT " and " DOT " are configurable.) Note that the user doesn't have to translate this back into an email address, coz the link is clickable.

  • FullEmailAddress -- the email address for that link. Data-bindable, thanks to the Power of Attributes. You can specify the recipient using a complete email address, or you can specify ...

  • EmailName, Domain, and Extension separately. (Also bindable.) Your choice.

  • DomainDelimiterText and ExtensionDelimiterText. In case the strings " AT " and " DOT " are not to your liking.
The control has to emit a JavaScript function, of course. Per Nikhil's and Vandana's book (I think it was), I did this by overriding OnPreRender and calling RegisterClientScriptBlock.

You can play with the control on this page: DynamicEmailTest.aspx, which enables you to see how the JavaScript is set and how setting different properties affects the control.

One of the things that I had to investigate was how to render only an <a> element. By default, the base control class wants to emit a <span> element. A most excellent article on MSDN explained that I would have to override the base TagKey property and return HtmlTextWriterTag.A. I also had to override AddAttributesToRender in order to set the href attribute (to an empty string) and the onclick and onmouseover attributes to call the JavaScript function. These two requirements took me afield from the basic strategy I mentioned earlier of simply overriding Render. In fact, in the end, the only thing I had to explicitly render was the link text.

I had initially tried to implement a feature that ended up costing me about 90% of the time I spent screwing around with this. I had thought that I could (should) make the FullEmailAddress and EmailName/Domain/Extension properties reciprocal -- setting one would populate the other(s). Boy, as far as I can tell, there's no clean way to do this within the control itself. For starters, you don't want to overwrite any values that the user sets, so you don't want to just barge in there and set the properties willy-nilly. Instead, I had a notion that if and only if FullEmailAddress was not set, it would return the combination of EmailName/Domain/Extension (and vice versa), without necessarily setting those properties explicitly. This is codable inasmuch as you can look for If ViewState("FullEmailAddress") Is Nothing, but the problem is that it doesn't stay Nothing for long. In my test page, once the text boxes had been populated with one of these based-on-the-other property values, it was as if the user had entered those property values, and on postback, ASP.NET happily set those properties, so they were no longer null. (Make sense?) In the end, I gave up on the idea of reciprocal properties, although there might be a way to do these. As a compromise, and because this seemed like it might be useful in some circumstances, I implemented the read-only properties FullEmailAddressEmailName, FullEmailAddressDomain, and FullEmailAddressExtension, which return the parsed components of FullEmailAddress.

There is one 2.0-specific feature in this code. The RegisterClientScriptBlock call, which is exposed via the ClientScript page property, has a new 2.0-specific overload. But you can use uncomment the corresponding 1.1 version and it seems to work fine -- I tested it on Web Matrix, yahoo!

So that's the first control. Another one another time.