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>
-
OPTGROUPS
Probably the most common issue is the fact that the DropDownList doesnt support optgroups.
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.
-
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.
-
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) its easy to 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.
-
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...
Posted by - Christoff Truter
Date - 2011-06-27 14:18:00
Comments - 3
Date - 2011-06-27 14:18:00
Comments - 3
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)
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.
Posted by - Christoff Truter
Date - 2011-06-19 22:13:34
Comments - 0
Date - 2011-06-19 22:13:34
Comments - 0
First 1 2 3 4 5 6 7 8 9 10 Last / 62 Pages (124 Entries)