views:

268

answers:

1

Hello,

I've noticed that the SL3 Validators automatically uses the properties from DisplayAttribute when creating validation messages. I'm interested in any suggestions on how to extract this information from a control's binding using code. I've included an example:

ViewModel code:

[Display(Name="First Name")]
public string FirstName { get; set; }

I know can achieve this on a Control-by-Control basis doing something like the following (TextBox in this case):

BindingExpression dataExpression = _firstNameTextBox.GetBindingExpression(TextBox.TextProperty)
Type dataType = dataExpression.DataItem.GetType();
PropertyInfo propInfo = dataType.GetProperty(dataExpression.ParentBinding.Path.Path);
DisplayAttribute attr = propInfo.GetCustomAttributes(false).OfType<DisplayAttribute>().FirstOrDefault();
return (attr == null) ? dataExpression.ParentBinding.Path.Path : attr.Name;

I'm interested if there's any way to do this generically, without needing to know the specific type of Control.

Thanks in advance for any thoughts!!

+1  A: 

Good question. Unfortunately there's really no way to generically do it though you can hard code a few properties and be pretty safe. For example, ContentControl.ContentProperty, TextBlock.TextProperty, TextBox.TextProperty, etc.

DataForm in Silverlight does the same thing. I also re-implemented a simple helper method they use called GetPropertyByPath. It basically does what your code does except it can walk a multi-step property path. It can't access indexed properties but neither can DataForm so it's at least as good as that.

From that point on, getting at the DisplayAttribute is just as you've shown.

public static PropertyInfo GetPropertyByPath( object obj, string propertyPath )
{

    ParameterValidation.ThrowIfNullOrWhitespace( propertyPath, "propertyPath" );

    if ( obj == null ) {
        return null;
    }   // if

    Type type = obj.GetType( );

    PropertyInfo propertyInfo = null;
    foreach ( var part in propertyPath.Split( new char[] { '.' } ) ) {

        // On subsequent iterations use the type of the property
        if ( propertyInfo != null ) {
            type = propertyInfo.PropertyType;
        }   // if

        // Get the property at this part
        propertyInfo = type.GetProperty( part );

        // Not found
        if ( propertyInfo == null ) {
            return null;
        }   // if

        // Can't navigate into indexer
        if ( propertyInfo.GetIndexParameters( ).Length > 0 ) {
            return null;
        }   // if

    }   // foreach

    return propertyInfo;

}
Josh Einstein
+1 - I wrote something similar recently to do pretty much the same thing. One thing I might suggest is that if you need to handle attached properties, your code above will not work (you need to handle parenthesis, like (Validation.Errors), etc).
JerKimball
Yeah good point. I forgot to mention that in addition to indexed properties, it basically can't handle anything than normal dotted paths, which is all DataForm attempts too. This is fine most of the time since all you typically are doing is accessing a ViewModel property.
Josh Einstein
Ah, I forgot about multi-step properties. Thanks for the excerpt, it's very helpful.
Patrick