ASP.NET WebControl Critical Analysis: DropDownList



There is a lot of the common functionality when working with html controls that Microsoft simplified (some overcomplicated in my opinion) in order to fit into their WebForms framework.

This is my first post (of hopefully many to follow) where I am going to look at some of the common issues I've seen over the years with regards to webcontrols.

Note that I doubt that Microsoft see them as issues, since they didn't seem to endeavour to fix/improve any of this in the latest incarnation of the product, so all of these issues are likely by intended design.

The first control I am going to look at is the DropDownList webcontrol.

 
<asp:DropDownList runat="server" ID="ddlLetters">
	<asp:ListItem Text="A" Value="1"></asp:ListItem>
	<asp:ListItem Text="B" Value="2"></asp:ListItem>
	<asp:ListItem Text="C" Value="3" Selected="True"></asp:ListItem>
	<asp:ListItem Text="D" Value="4"></asp:ListItem>
</asp:DropDownList>
 


This control is an abstraction Microsoft created for the (X)HTML select list (the single selection part of it at least, they created a separate abstraction for the multiple selection - ListBox).

 
<select name="letters">
   <option value="1">A</option>
   <option value="2">B</option>
   <option selected="selected" value="3">C</option>
   <option value="4">D</option>
 </select>
 


  1. OPTGROUPS

    Probably the most common issue is the fact that the DropDownList doesnt support optgroups.

    Select tag optgroups
     
    <select name="letters">
    	<optgroup label="Microsoft">
    	   <option value="1">C#</option>
    	   <option value="2">VB.net</option>
    	</optgroup>
    	<optgroup label="">
    	   <option value="3">PHP</option>
    	   <option value="4" disabled="disabled">Java</option>
    	   <option value="5">Perl</option>
    	</optgroup>
    </select>
     



    The optgroup tag is by no means deprecated or obsolete or anything, so I am not quite sure why they excluded this functionality. Incidently, I wrote a post a while ago on how to include this functionality by means of WebControlAdapters.

  2. SELECTEDVALUE NOT BROWSABLE

    SelectedValue not Browsable

    Not really an issue (but confusing to some) that the SelectedValue is not visible in the designer, the reason being that whoever wrote the ListControl base class (the class from which the DropDownList inherits from) set the Browsable attribute above the property to false.

    I've seen devs coding around this (sometimes with good reason, like seen in point 3) even though it is still possible to bind/eval the SelectedValue when using it within a parent control e.g. ListView, FormView etc.

     
    <asp:DropDownList runat="server" ID="ddlLetters" Selectedvalue='<%# Eval("somevalue") %>'>		
     


    I do however believe that the developer(s) "hid" this property on purpose, I believe that the following issue might give us a clue to why (s)he did it.

  3. SELECTEDVALUE MUST EXIST

    Chances are that you've come across the following exception:

    'ddlSomething' has a SelectedValue which is invalid because it does not exist in the list of items

    The reason we get this exception is obvious, our selected value needs to be in the list we're setting it to and its not.

    When working with lists that constantly change (add/removed/edited), or in certain binding scenarios one can easily get this exception but equally as easy to manage this exception in our codebehind/codefile, but it becomes problematic to trap this exception as soon as we're working with something like ObjectDataSources and we attempt to bind the SelectedValue like seen in point 2.

    We get forced to bind our SelectedValue in our DataBound event like seen below (something you can perhaps move to a WebControlAdapter).

     
    protected void ddlLetters_DataBound(object sender, EventArgs e)
    {
    // Depends on the context in which this event fires
        ((DropDownList)sender).SelectedValue = Convert.ToString(Eval("somevalue"));
    }
     


    Which means its safer to bind our SelectedValue in the codebehind making it prudent to rather hide the SelectedValue property from the designer.

    It would have probably made more sense to allow developers to specify a default item to which our SelectedValue must revert if its not found and if no default item is specified we simply throw the does not exist exception like normally.

    Note: This workaround won't work for all scenarios, perhaps we can look at other scenarios in a future post.

  4. ITEM UNIQUENESS

    It makes sense to only list unique items in our DropDownList, but what happens if we list non-unique values? Meaning our Text/display is unique, but the value associated with it is not e.g:

     
    <asp:DropDownList runat="server" ID="ddlLetters">
        <asp:ListItem Text="A" Value="2"></asp:ListItem>
        <asp:ListItem Text="B" Value="1"></asp:ListItem>
        <asp:ListItem Text="C" Value="1"></asp:ListItem>
        <asp:ListItem Text="D" Value="4"></asp:ListItem>
    </asp:DropDownList>
     


    I remember helping someone with this very issue a few years ago (he wasted a good hour trying to figure this out, rather sad).

    Every time someone selected C it would highlight B after a postback since the control cant distinguish which value of "1" the user selected, so it assumed that the first value of "1" it finds is the selected one (he had a list containing 100 values)

    It would have saved that developer (who didnt realise that he's binding a non-unique valued list to the DropDownList) a whole hour of his time if the DropDownList simply threw an item uniqueness exception or something...


Feel free to comment on additional issues you've come across...




Post/View comments
 

Interesting Exceptions: C# - Cannot load dynamically generated serialization assembly



If you ever tried calling a webservice (among other things) from a SQLCLR procedure chances are pretty good that you've ran into the following exception:

 
Msg 6522, Level 16, State 1, Procedure Test, Line 0
A .NET Framework error occurred during execution of user-defined routine or aggregate "Test": 
System.InvalidOperationException: Cannot load dynamically generated serialization assembly. In some hosting environments assembly load functionality is restricted, consider using pre-generated serializer. Please see inner exception for more information. ---> System.IO.FileLoadException: LoadFrom(), LoadFile(), Load(byte[]) and LoadModule() have been disabled by the host.
System.IO.FileLoadException: 
   at System.Reflection.Assembly.nLoadImage(Byte[] rawAssembly, Byte[] rawSymbolStore, Evidence evidence, StackCrawlMark& stackMark, Boolean fIntrospection)
   at System.Reflection.Assembly.Load(Byte[] rawAssembly, Byte[] rawSymbolStore, Evidence securityEvidence)
   at Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch(CompilerParameters options, String[] fileNames)
   at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources)
   at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, String[] sources)
 

For all intents and purposes I am going to assume you've got an understanding on how to write and deploy CLR procedures (You can learn more about SQLCLR procedures over here and here)

CLR Procedures that make use of webservices require a strongly typed wrapper class (a nice little performance optimization when working with XML serialization/deserialization, needed for SOAP requests/responses)

Notice the following setting (Generate serialization assembly) within your project properties page under the build tab (assuming you're using Visual Studio 2008)

Project Build Properties - Serialization

When the serialization setting is set to auto/on and you're consuming a webservice in your project, a serialization assembly automatically gets generated in your output folder (e.g. SourceAssembly.XmlSerializers.dll) which you'll need to register in SQL like seen in the following snippet:

 
CREATE ASSEMBLY CLRProcedures FROM 'C:\demos\CSTruter.com\CLRSQL\bin\Release\SourceAssembly.dll' 
WITH PERMISSION_SET = UNSAFE
GO
CREATE ASSEMBLY CLRSerializer FROM 'C:\demos\CSTruter.com\CLRSQL\bin\Release\SourceAssembly.XmlSerializers.dll' 
WITH PERMISSION_SET = UNSAFE
GO
CREATE PROCEDURE Test
AS EXTERNAL NAME [CLRProcedures].[CSTruter.com.StoredProcedures].[Test]
 


All of this works fine when we're consuming webservices, there is however another scenario where we'll get this exception when working with SQLCLR - which comes into play as soon as we use the XmlSerializer class.

When working with XML Types for example, the XmlSerializer class becomes quite useful in that instead of working with raw xml, we can rather work with a .net object like seen in the following snippet where we map our xml to a .net class.

 
[XmlRoot("tester")]
public class tester
{
    [XmlAttribute("name")]
    public string name
    {
        get;
        set;
    }
}
 


Serialization will look something like this:

 
tester t = new tester();
t.name = "test";
using (StringWriter sw = new StringWriter())
{
    XmlSerializer xs = new XmlSerializer(typeof(tester));
    xs.Serialize(sw, t);
}
 


While deserialization will look something like this:

 
tester t = null;
using (StringReader sr = new StringReader("some xml string"))
{
    XmlSerializer xs = new XmlSerializer(typeof(tester));
    t = xs.Deserialize(sr) as tester;
}
 


Like with webservices we'll also need to supply SQL with a strongly type wrapper class, but unfortunately visual studio only seems to automatically generate the serialization assembly when consuming webservices.

In order to manually generate a serialization assembly Microsoft created a tool called sgen, which is available in the .net sdk (and a few other microsoft SDKs), by which we can generate this serialization assembly.

Firstly you need to add the XmlSerializerAssembly attribute above the class(es) that you're using for serialization e.g.

 
[XmlRoot("tester")]
[XmlSerializerAssembly("SourceAssembly.XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
public class tester
{	
 


Secondly run the sgen tool.

 
sgen C:\demos\CSTruter.com\CLRSQL\bin\Release\SourceAssembly.dll 
 

(generates SourceAssembly.XmlSerializers.dll)

Simply register the generated assembly in SQL and that should get rid of this exception.




Post/View comments
 
First 1 2 3 4 5 6 7 8 9 10 Last / 65 Pages (130 Entries)

Latest Posts

MS SQL: Parameter Sniffing


2012-05-21 22:38:48

Be the best stalker you can be


2011-12-13 22:33:54

Top 5 posts

Moving items between listboxes in ASP.net/PHP example


Move items between two listboxes in ASP.net(C#, VB.NET) and PHP
2008-06-12 17:07:43

Simple WYSIWYG Editor


Creating a WYSIWYG textbox for your website is actually quite simple.
2007-02-01 12:00:00

C# YouTube : Google API


Post on how to integrate with YouTube using the Google Data API
2011-03-12 08:37:51

Populate a TreeView Control C#


Populate a TreeView control in a windows application.
2009-08-27 16:01:03

Cross Browser Issues: Firefox Word Wrapping


Firefox word wrapping issues
2008-06-09 09:51:21