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
17Mar/060

Side-by-side VS2003 and VS2005: Unable to start debugging on the web server

Today I came across a big problem with an obvious easy solution. I had just installed a new machine with IIS, VS6, VS2003 and VS2005 side-by-side. To Microsoft's credit, this works flawlessly. However, after getting some of my old VS2003 (.NET 1.1) projects from SourceSafe, I found I couldn't run the project in debug mode. At first I thought this was due to the side-by-side installation or an error in the installation, so I ran a repair on the VS2003 installation but found the problem persisted even after that completed successfully.

Turns out it has to do with the version of the .NET runtime the virtual directory is setup for. To solve, open Internet Services Manager, and open the Properties dialog on the Virtual Folder of the application in question. On the far right, you'll find an "ASP.NET" tab. Check the ASP.NET dropdown is set to the version of the runtime the site was developed in.

There are several other reasons why debugging may not work on your machine, but those are all documented in the VS help.

Filed under: Tech No Comments
9Feb/060

Creating a DropDownList from an Enum using Attributes and Reflection

Often in software development, one has a need to map the values in a lookup table to an enumeration in code. In this way, the values of the lookup table can be readily accessed in a strongly typed way. The only drawback to this approach is that the enum and the table can often get out of sync if values in the table change. That is however an entirely different problem and will not be discussed here. It is also often the case, in web development anyway, that one needs to display the values in the table and hence the enumeration, in a DropDowList. There are two possibly approaches to this problem.

  1. Get the values from the database in a IDataReader (or DataTable or whatever) and bind it to the DropDownList
  2. Use the values in the enumeration to generate the ListItems for the DropDownList.

I have always favoured the second approach, but enums are quite limited in the data they can hold. Really, it's nothing more than a stringly typed key/value pair, usually of type int. Sometimes however, we want to store more information with the enumeration for display in the UI. We may for example, have an enum with the following values:

public enum Vendors {
Microsoft,
Oracle,
IBM
}

But, we may like to display something like "Microsoft Developmer Network" in a Drop Down. Enter Attributes! An Attribute can be applied to just about anything and can be easily consumed using Reflection. All the developer need do is create a class derived from the Attribute base class. It doesn't have to be particularly complex. The following will suffice:

public class EnumMetaDataAttribute : Attribute {
string _description;

public string Description {
get {
return _description;
}
}

public EnumMetaDataAttribute(string description) {
_description = description;
}
}

Then the attribute needs to be applied to the enum values:

public enum Vendors {
[EnumMetaData("Microsoft Developer Network")] Microsoft,
[EnumMetaData("Oracle Technology Network")] Oracle,
[EnumMetaData("IBM developerWorks")] IBM
}

So far, we have no way of consuming these attributes, but this is easily overcome using some basic reflection techniques. The following function should be fairly self explanatory. The .NET help should be sufficient reference for the reflection properties and methods:

public static void GetEnumDropDown(DropDownList dropDown, System.Type enumType) {
FieldInfo[] fields = enumType.GetFields();
foreach(FieldInfo field in fields) {
if(field.IsSpecialName) {
continue;
}

int value = Convert.ToInt32(field.GetValue(0));
EnumMetaDataAttribute attribute = field.GetCustomAttributes(typeof(EnumMetaDataAttribute), false)[0] as EnumMetaDataAttribute;

dropDown.Items.Add(new ListItem(attribute.Description, value.ToString()));
}

Which may then be called in the following way:

GetEnumDropDown(ddlDropDownName, typeof(Vendors));

Filed under: Tech No Comments
3Feb/060

Manage Recent Projects list in VS2005

It seems like an obvious thing to be able to do in Visual Studio 2005, but there is really no in-built way to manage items in the Recent Projects list on the Start Page. If you're like me, you end up with a bunch of project names from various things, with some duplicates even. It can rapidly become unwieldy to figure out what project you're actually trying to open. I did a little looking around and it seems the only way to actually manage items in that list is via the registry.

Go Start -> Run and type "regedit".

The keys you're looking for can be found in: HKCU\Software\Microsoft\VisualStudio\8.0\ProjectMRUList

Just delete the ones you no longer want and restart VS.

Filed under: Tech No Comments
20Jan/060

A generic C# function for casting from Object to any type

A very common scenario in software development is getting data from a database and then reading that data into strongly typed variables. Mostly I do this in an OR (Object Relational) layer, but really it could be done anywhere. Prior to .NET 2.0 and the introduction of generics, one had to cast the values from a DataRow or IDataReader column value, returned as an object, as follows:

string description = Convert.ToString(row["Description"]);

This works, but one needs to do a Convert operation for every data type. Furthermore, the object may be a DBNull, in which case you'll get an InvalidCastException. There are various strategies for getting around this. I offer my solution here.

public static T ToType<T>(object value) {
 if(Convert.IsDBNull(value)) {
  return default(T);
 }

 return (T)Convert.ChangeType(value, typeof(T));
}

Typically, this function would reside in a helper class of some kind and called in the following way:

string description = DbHelper.ToType<string>(reader["Description"]);

For any other data type, simply replace <string> with whatever cast you wish to perform.

Filed under: Tech No Comments
14Dec/050

DataList ItemDataBound e.Item.Visible = false doesn’t work

I came across a small problem today when trying to set items in a DataList to Visible = false in the ItemDataBound event. Quite simply, it just plain wasn't working. I did a bit of Googling and found several reported instances of this, but only one suggested any type of resolution. That forum thread can be found here: http://www.codecomments.com/archive320-2005-10-657712.html

Strangely, the problem only occurs when the RepeatLayout property of the DataList is set to Table. Change the RepeatLayout to Flow and the problem goes away.

Alternatively, Do something like this:

e.Item.CssClass = "hidden";

Then, add a CSS class "hidden" to your stylesheet:

.hidden {
display:none;
}

Filed under: Tech No Comments
22Nov/050

ASP.NET error when impersonating a user

Lately I have been developing a small application which is essentially the same as Windows Explorer. The idea is that business units within the company I work for, can publish documentation easily by giving a single user access to a given filepath, which I then impersonate when making calls to the file system in my application. Since I started developing the application, I have come up against numerous perplexing problems which have all had something to do with Windows or .NET security in one way or another. The latest one I came up against is as follows:

"Configuration information could not be read from the domain controller, either because the machine is unavailable, or access has been denied."

I blog it here because I was unable to find any useful information on this error anywhere and only managed to solve it by making a lucky (if educated) guess. The solution to this problem was to give the ASPNET account "Impersonate a client after authentication" rights on the machine the application is running on. For some reason, a reboot was not required in this case. You can find this setting by going to Control Panel -> Administrative Tools -> Local Security Policy, then expand Local Policies and select User Rights Assignment. Note that it will probably also be necessary to assign the "Act as part of the operating system" right to the ASPNET account, although you probably would not have gotten to this error before doing that in any case.

Filed under: Tech No Comments
11Nov/050

CommunityServer upgrade

For many months, I have been trying to upgrade gibbons.co.za to CommunityServer, the new .NET based blogging software from Telligent Systems. Each attempt has been met with blinding frustration and dismal failure, but at last, after trying and trying again, I have prevailed and managed a successful upgrade to CommunityServer v1.1 with the aid of two very key pieces of software. I write here as complete a description as possible of the procedure followed, tools used and problems encountered so as to provide assistance to any other would-be CommunityServer migrants. After running the new software for only a week, I can comprehensively say it is a vast improvement on .Text v0.95, although I intend no disrespect whatsoever to the original blog software, which was excellent for its time.

Migrating to CommunityServer is essentially a simple process, consisting of the following steps. Note that you must use CommunityServer v1.1 because the DotText-CS-Converter wizard (mentioned in point 5 below) only works with this version.

  1. Download the CommunityServer software from Telligent and follow the instructions in the README.
  2. Follow the setup instructions. Once complete you should have a functioning blog running in multi-blog (normal) mode.
  3. If you have an old .Text 0.95 blog, use Ken Harder’s DotText-CS-Converter tool to migrate the data. Full instructions on how to do this are included in the README accompanying the tool.
  4. To run your blog in single-blog mode, you need to replace the default SiteUrls.config in your site root, with one generated using Ken Robertson’s Single Blog/Gallery SiteUrls Generator. Note that you need to set the options ‘Single Blog’ and ‘Single Gallery’ to ‘Yes’. Your blog name will probably be 'blog_0'.
  5. You should then be able to open your site as per usual.

Troubleshooting

If you hit your site after following the above steps and the first page displays fine, but you get a 404 (Page not found) error when clicking any of the links, it’s probably because your site is not correctly configured in IIS to support URL rewriting. If you are hosting your site with a hosting company you may have a problem here. I had initially taken a hosting package with Fasthosts.co.uk but after much banging of head against the nearest wall, I finally contacted Ken Robertson and pleaded for help. He was gracious enough to advise me that the option “Check that file exists” must be unchecked. This option can be found by going into Internet Services Manager, then by choosing ‘Properties’ on the website, go to the ‘Directory’ tab and click ‘Configuration’, then select ‘.aspx’ from the ‘Application Mappings’ list on the ‘Mappings’ tab and click ‘Edit’. Fasthosts was unwilling to uncheck this box for me, so after a protracted discussion, I ended up getting flatly refused and am now hosting elsewhere.

I encountered one other problem which is worth mentioning. When running the DotText-CS-Converter wizard, I chose the option to migrate trackbacks. My site has been in operation for about a year now and I had close on 1000 trackbacks. Ordinarily this wouldn’t be a problem, but after migrating several hundred of those, I got an error saying the transaction log was full. I guess it’s obvious, but any hosting company will impose a size quote on your database and log to prevent disk space shortages. The number of transaction taking place during the migration quite simply used all that space.

Good luck.

Filed under: Tech No Comments
20Jul/050

My first article on CodeProject.com

For ages I’ve been meaning to share the odd pearl with the good readers of CodeProject.com but never seemed to find the time or perhaps more truthfully, code good enough, to post on the site. Finally though, I have posted my first article on CodeProject.com entitled "A useful UrlBuilder class". So far the readers are liking what they're seeing and that’s not only flattering but also quite encouraging. If I can find the time to write a whole article and make it look all pretty, there might be more articles to come. Hmmm, I have just the one in mind! You can find my article at the following Url: http://www.codeproject.com/aspnet/UrlBuilder.asp

UPDATE 06/12/2005: I wrote another article for CodeProject.com on August 24th, but didn't updated this post. You can find the new article at the following Url: http://www.codeproject.com/aspnet/EncodedUrlBuilder.asp

Filed under: Tech, Watch & Read No Comments