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.