Craig Gibbons' Lifeblog lifeblog://tri.eat.net

23Aug/060

Adding META, CSS, JavaScript or any other kind of tag to a Page or MasterPage header

In .NET there is always a way. As far as I can tell, after working
extensively with the Framework for about 4 years, nothing has been
omitted. One might encounter the odd anomaly, but for the most part
there is always several workarounds to any problem which may arise.
Working with MasterPages poses some of its own problems. One such
problem is the adding of tags to the Page header. Such tags include
META, CSS and JavaScript, but this could be anything defined by the
standard. The tags can of course be entered straight into the page
code, but if you want to conditionally include something or use a
virtual path, you need to do this programmatically. The following three
code snippets show how to add META, CSS and any other tag to the Page
header.

META Tags

protected void Page_Load(object sender, System.EventArgs e) {
    HtmlMeta meta = new HtmlMeta();
    meta.HttpEquiv = "refresh";
    meta.Content = "5;http://www.gibbons.co.za";
    Page.Header.Controls.Add(meta);
}

CSS Tags

protected void Page_Load(object sender, System.EventArgs e) {
    HtmlLink css = new HtmlLink()
    css.Href = "http://www.gibbons.co.za/Style.css";
    css.Attributes["rel"] = "stylesheet";
    css.Attributes["type"] = "text/css";
    Page.Header.Controls.Add(css);
}

Any other tag (e.g. JavaScript include)

protected void Page_Load(object sender, System.EventArgs e) {
    HtmlGenericControl control = new HtmlGenericControl("script");
    control.Attributes["src"] = "common.js";
    control.Attributes["type"] = "text/javascript";
    control.Attributes["language"] = "javascript";
    Page.Header.Controls.Add(control);
}

Alternatively, you can always use a HtmlGenericControl and use the Attributes collection to write any attributes you desire.

Filed under: Tech No Comments
23Aug/060

ASP.NET DropDownList with OPTGROUP support

One of the new tags in the XHTML standard is OPTGROUP. This long
missing tag provides the ability to group elements into sub-sections
inside a SELECT element. Unfortunately, the ASP.NET DropDownList server
control lacks the ability to add OPTGROUP’s to its items collection, so
it falls to the developer to provide a custom solution for this. I did
some looking around and found a few solutions to the problem, but none
of them really looked like the real deal, until I discovered Control
Adapters in .NET 2.0! Essentially, Control Adapters allow you to
override any method of a control by providing your own implementation.
Even better still, you can use this technology to target different
browsers in different ways by using a browser file. From there, the
solution was quite simple. The steps and associated code are as follows:

  1. Create a .browser file. Paste in the following code:

    <browsers>

      <browser refID="IE6to9">

        <controlAdapters>

          <adapter controlType="System.Web.UI.WebControls.DropDownList"

          adapterType="DropDownListAdapter" />

        </controlAdapters>

      </browser>

    </browsers>

  2. Create a file called DropDownListAdapter.cs in App_Code or a sub
    directory thereof (I use a folder called Adapters), paste in the
    following code:
    public class DropDownListAdapter : System.Web.UI.WebControls.Adapters.WebControlAdapter {
        protected override void RenderContents(HtmlTextWriter writer) {
            DropDownList list = this.Control as DropDownList;

            string currentOptionGroup;
            List<string> renderedOptionGroups = new List<string>();

            foreach(ListItem item in list.Items) {
                if(item.Attributes["OptionGroup"] == null) {
                    RenderListItem(item, writer);
                } else {
                   
    currentOptionGroup = item.Attributes["OptionGroup"];

                   
    if(renderedOptionGroups.Contains(currentOptionGroup)) {
                       
    RenderListItem(item, writer);
                    } else {
                       
    if(renderedOptionGroups.Count > 0) {
                           
    RenderOptionGroupEndTag(writer);
                        }

                       
    RenderOptionGroupBeginTag(currentOptionGroup, writer);
                       
    renderedOptionGroups.Add(currentOptionGroup);

                       
    RenderListItem(item, writer);
                    }
                }
            }

            if(renderedOptionGroups.Count > 0) {
                RenderOptionGroupEndTag(writer);
            }
        }

        private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer) {
            writer.WriteBeginTag("optgroup");
            writer.WriteAttribute("label", name);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteLine();
        }

        private void RenderOptionGroupEndTag(HtmlTextWriter writer) {
            writer.WriteEndTag("optgroup");
            writer.WriteLine();
        }

        private void RenderListItem(ListItem item, HtmlTextWriter writer) {
            writer.WriteBeginTag("option");
            writer.WriteAttribute("value", item.Value, true);

            if(item.Selected) {
                writer.WriteAttribute("selected", "selected", false);
            }

            foreach(string key in item.Attributes.Keys) {
                writer.WriteAttribute(key, item.Attributes[key]);
            }

            writer.Write(HtmlTextWriter.TagRightChar);
            HttpUtility.HtmlEncode(item.Text, writer);
            writer.WriteEndTag("option");
            writer.WriteLine();
        }
    }

From there, usage of the DropDownList is exactly the same, except you
need to add an OptionGroup attribute to any items you wish to add to a
given group, sample code as follows:

ListItem item1 = new ListItem(“South Africa”, “SA”);
ListItem item2 = new ListItem(“United States”, “US”);
ListItem item3 = new ListItem(“United Kingdom”, “UK”);
item1.Attributes[“OptionGroup”] = “Countries”;
item1.Attributes[“OptionGroup”] = “Countries”;
item1.Attributes[“OptionGroup”] = “Countries”;

ddlCountries.Items.Add(item1);
ddlCountries.Items.Add(item2);
ddlCountries.Items.Add(item3);

Filed under: Tech No Comments
13Aug/060

Harlow 10 Mile

Last weekend I was up bright and early on Sunday morning, ready to run the Harlow 10, a 10 mile (surprisingly) race held in the town of Harlow (surprisingly), just outside of the M25 North of London. It's the first race I've done since the Edinburgh Marathon more than a year ago and I was quite excited to get back into competitive running. I've run 10
miles and more plenty of times in running but have never raced this distance and I had no idea what time to expect. Anything under 1:15 would have perfectly acceptable, but as soon as the gun went, I found myself running along at a fairly brisk pace which I was certain I could not maintain for the entire distance but I thought I'd see just how far I could push it. The course started quite tough with a few sharp hills and even some offroad before levelling out onto a nice flat tarred
section where I managed to regain some breath and keep the pace up, helped along by the everpresent sound of footsteps just behind. The race ended with a very tough section through a park with the sun now directly overhead, sapping every last bit of energy and making me very
grateful to be finished in 31st position, with an official time of 1:04:54.

Filed under: Travel, Tri & Run No Comments
9Aug/060

Marathons Aplenty

I signed up for the Chicago and then Berlin marathons some time ago but I blog them today for posterity. One day at work, while pondering my poor state of fitness and general lack of extra curricular activity, apart from furrowing a nightly path from the couch to the fridge and back, I received an email from the gym, inviting all interested parties to participate as part of a Merrill Lynch team in the Chicago Marathon. Having been in Chicago more than 10 years ago, I recall 2 things from my time there, cold and wind... yet I was keen to return. Chicago is afterall a beautiful and viby city in the summer and I thought seeing 26.2 miles of it from street level might provide a different perspective on the place. About a week later, stricken with compulsive single mindedness reminiscent only of marathon training days gone by, I discovered the Berlin marathon was taking place a month before Chicago and I figured it would be a good warm-up. I later found out both of these marathons form part of the World Marathon Majors, consisting of the Boston, London, Berlin, Chicago and New York marathons. I hope to
do London next year and maybe NY too, we'll have to see how high and how long these levels of enthusiasm will hold! The Berlin Marathon
takes place on September 24th and the Chicago Marathon on October 22nd.

Filed under: Travel, Tri & Run No Comments
8Aug/060

An afternoon at the US Embassy

There have been times when I have cursed my South African heritage. Usually it occurs while observing a rugby spectator, prone to dronk verdriet, sporting a new springbok jersey stretched over a monsterous boep brandishing a castle lager and a piece of droewors, but there is another equally distateful experience, that of waiting at an embassy of one country or another for some disinterested, tired, bored and generally disgruntled civil servant to process a visa application. To date, I have experienced only one pleasurable (mmm, that may be a slight overstatement) visa acquisition experience, strangely enough, at the Hungarian embassy. The entire process took about 10 minutes and cost pretty much nothing. Contrast this with the experience of being relieved of yet another £100 for a 3 day visa to France and one starts to appreciate just how tiresome and frustrating this exercise can become.

So it was, with very little enthusiasm, that I began the process of obtaining a visa to visit the USA in October to run the Chicago Marathon. The process is simple, call a premium rate number and make an interview booking, pay an extortionate fee for the interview. Then wait 6 weeks for said interview and expect to be there for 4 - 5 hours. Once your number is called, your 2 minute interview complete and your application approved, pay still more money to have your passport couriered back to you 5 days later. During those uncomfortably long and sardined 5 hours, I had begun writing a blog entry, slating the US and their embassy for the most inefficient, insensitive and costly visa application process, but I must regrettably take it all back now. Whereas France sees fit to grant me only sufficient time for my hotel and several restaurants to extract their necessary revenue, the US has made what can only be described as a leap of faith by granting me a 10 year visa based on only a reference letter from my employer, a bank statement, a utility bill and a declaration that I never have been, and am not currently, affiliated to any terrorist organisation. Yeeee-ha, I'm a goin' to Chicago.