Generic Access to ASP.NET Dynamic Data UIHint Attribute Values
Yesterday I published a post titled, Passing Arguments to a Dynamic Data Field Template from a UIHint Attribute. The purpose of that post is to show you how you can access the UIHint argument values in a dynamic data field template. Today I want to show you how to take the next step.
The Wrap Up
After spending a little more time with my site I realized there is a better way to access the UIHint argument values. I don’t like writing code over and over again that checks for the existence of items. A simple method call that takes care of all the implementation details was in order.
First I started with the following method that looks for string-based UIHint arguments:
using System.ComponentModel.DataAnnotations;
…
protected string GetUIHintArg(string key)
{
UIHintAttribute hint = null;
string returnValue = string.Empty;
hint = (UIHintAttribute)this.Column.Attributes[typeof(UIHintAttribute)];
if (hint != null)
{
if (hint.ControlParameters.ContainsKey(key))
{
returnValue = hint.ControlParameters[key].ToString();
}
}
return returnValue;
}
This is basically the same code as described in my last post, but the key difference here is that I consolidated two “if” statements down to one. I was able to tighten the code up by using the ContainsKey method to test for the existence for the desired key/value pair. This approach is not only more succinct, but also will not throw an exception if there are no arguments defined in the dictionary. Having this method is convenient because you may only define hints as string in the attribute, so you will probably use this method a lot.
Going Beyond Strings
Strings are great, but I also want to pass in other types like booleans and enumerations. In my control I am using a boolean to decide if I should overwrite existing data. The control can also emit the user name or the date and time – so I wanted an enumeration to help the control know how to format itself.
To handle native types and enumerations, I implemented the same method while capitalizing on the use of generics.
As an example, consider the following UIHint attribute:
[UIHint("AuditField", null, "Overwrite", "true")]
Knowing that the “Overwrite” argument is a boolean type, there should have an easy way to get the value back.
Here’s how:
this.overwrite = this.GetUIHintArg<bool>("Overwrite");
By passing in the expected type of the object, the returned value is pre-converted to the appropriate data type.
Here’s the new generic method:
Update: Scott Hanselman suggested in the comments a way to get rid of a very ugly switch statement I was previously using. The code is now updated per his suggestion. Thanks, Scott!
protected T
GetUIHintArg<T>(string key)
{
UIHintAttribute
hint = null;
T returnValue = default(T);
string
value = string.Empty;
hint = (UIHintAttribute)this.Column.Attributes[typeof(UIHintAttribute)];
if (hint !=
null)
{
if
(hint.ControlParameters.ContainsKey(key))
{
value =
hint.ControlParameters[key].ToString();
var
converter = TypeDescriptor.GetConverter(typeof(T));
returnValue =
(T)converter.ConvertFromInvariantString(value);
}
}
return
returnValue;
}
The only real change here is that the method is asking what
type to expect to find the argument and then casting the value to that type.
This method will work with any custom enumeration as well. Consider the following enumeration and UIHint:
public enum DisplayTypes
{
Undefined,
UserName,
DateTime
}
…
[UIHint("AuditField", null, "DisplayType","UserName")]
The above arguments tell the template to display the user name when the control is rendered. Here is the code used to access this data:
this.displayType = this.GetUIHintArg<DisplayTypes>("DisplayType");
Placement is Everything
To get the most out of this method, the best place to keep it is in a base class that wraps up System.Web.DynamicData.FieldTemplateUserControl. Once all your field templates inherit from your new base class then this feature is available at all times!
Thanks to George Durzi who gave some help thinking though some of the code on this post.