Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

How to list class properties that are classes too?

Posted on 2008-11-13
4
Medium Priority
?
392 Views
Last Modified: 2013-11-08
I'm trying to list a class's properties in a drop down menu using reflection. It works great but if the class has another class as a property, that's where I'm having problems.

I'm binding to a drop down like this:
            foreach (PropertyInfo property in typeof(Product).GetProperties())
                ddlClassColumns.Items.Add(property.Name + " (" +
                        property.PropertyType.Name.ToLower() + ")", property.Name));

Which results in this using the classes below. Dropdown:
text: ProductID (string),    value: ProductID
text: Name (string),           value: Name
text: MetaData (MetaData),    value: MetaData

The last one is where I'm having trouble. What I'm trying to do is flatten it out to dot notation like this:

text: ProductID (string),    value: ProductID
text: Name (string),           value: Name
text: MetaData.Title (string),    value: MetaData.Title
text: MetaData.Keywords (string),    value: MetaData.Keywords
text: MetaData.Description (string),    value: MetaData.Description

How can I do this?
public class Product
    {
        private string _productID = String.Empty;
        public string ProductID
        {
            get { return _productID; }
            set { _productID = value; }
        }
 
        private string _name = String.Empty;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
 
        private MetaData _metaData = new MetaData();
        public MetaData MetaData
        {
            get { return _metaData; }
            set { _metaData = value; }
        }
    }
 
    public class MetaData
    {
        private string _title = String.Empty;
        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }
 
        private string _keywords = String.Empty;
        public string Keywords
        {
            get { return _keywords; }
            set { _keywords = value; }
        }
 
        private string _description = String.Empty;
        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }
    }

Open in new window

0
Comment
Question by:bemara57
  • 2
  • 2
4 Comments
 
LVL 19

Accepted Solution

by:
drichards earned 2000 total points
ID: 22957232
If you need it to work generically for any properties of any class, then you are pretty much out of luck because you'll have a hard time determining which things to expand.  In the example you give, the string properties would get expanded also since string is a class with properties.  You'd end up with tons of code trying to decide what to expand.

If you just need it to work specifically for MetaData, then you just put a case in the loop and if the property name is MetaData, start another loop to add its properties to the list in the form you want.
0
 

Author Comment

by:bemara57
ID: 22976306
Thanks, but I'm having a hard time assigning a value to a property of a sub class. Say I'm trying to dynamically assign a value to Product.MetaData.Keywords, how do I do this? I tried the code below but I get an exception:

string propertyToAssign = "MetaData.Keywords";
Product oProduct = new Product();

PropertyInfo propertyParent = oProduct .GetType().GetProperty(propertyToAssign.Split('.')[0]); //MetaData
PropertyInfo propertyInfo = propertyParent.PropertyType.GetProperty(propertyToAssign.Split('.')[1]); //Keywords

propertyInfo.SetValue(propertyParent, 13.54, null);

And the exception is:
System.Reflection.TargetException: Object does not match target type. at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
0
 

Author Comment

by:bemara57
ID: 22984703
I got it working. Just wanted to share my end result, which is an extension to convert a DataTable to a collection of a classes (strong-typed). It does this by accepting a hashtable that maps the columns of the DataTable to the property names of the class. Pls share any comments on this if any:
        public static List<T> ToClassCollection<T>(this DataTable param, Hashtable mappings) where T : new()
        {
            List<T> col = new List<T>();
 
            foreach (DataRow row in param.Rows)
            {
                if (row.HasErrors)
                    break;
 
                T objInstance = new T();
 
                foreach (DictionaryEntry map in mappings)
                {
                    if (row[map.Value.ToString()] != DBNull.Value)
                    { 
                        object tableCell = row[map.Value.ToString()];
                        
                        try
                        {
                            if (map.Key.ToString().Contains('.'))
                            {
                                string[] propertySplit = map.Key.ToString().Split('.');
 
                                // Get instance of subclass
                                PropertyInfo subClassInfo = objInstance.GetType().GetProperty(propertySplit[0]);
                                object subClassInstance = Activator.CreateInstance(subClassInfo.PropertyType);
 
                                // Get property of subclass
                                PropertyInfo propertyInfo = subClassInstance.GetType().
                                    GetProperty(propertySplit[1]);
 
                                if (propertyInfo != null)
                                {
                                    if (propertyInfo.PropertyType == typeof(string))
                                        propertyInfo.SetValue(subClassInstance, tableCell.ToString(), null);
                                    else if (propertyInfo.PropertyType == typeof(bool))
                                        propertyInfo.SetValue(subClassInstance, Convert.ToBoolean(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(int))
                                        propertyInfo.SetValue(subClassInstance, Convert.ToInt32(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(decimal))
                                        propertyInfo.SetValue(subClassInstance, Convert.ToDecimal(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(double))
                                        propertyInfo.SetValue(subClassInstance, Convert.ToDouble(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(DateTime))
                                        propertyInfo.SetValue(subClassInstance, Convert.ToDateTime(tableCell), null);
                                }
 
                                subClassInfo.SetValue(objInstance, subClassInstance, null);
                            }
                            else
                            {
                                PropertyInfo propertyInfo = objInstance.GetType().GetProperty(map.Key.ToString());
                    
                                if (propertyInfo != null)
                                {
                                    if (propertyInfo.PropertyType == typeof(string))
                                        propertyInfo.SetValue(objInstance, tableCell.ToString(), null);
                                    else if (propertyInfo.PropertyType == typeof(bool))
                                        propertyInfo.SetValue(objInstance, Convert.ToBoolean(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(int))
                                        propertyInfo.SetValue(objInstance, Convert.ToInt32(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(decimal))
                                        propertyInfo.SetValue(objInstance, Convert.ToDecimal(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(double))
                                        propertyInfo.SetValue(objInstance, Convert.ToDouble(tableCell), null);
                                    else if (propertyInfo.PropertyType == typeof(DateTime))
                                        propertyInfo.SetValue(objInstance, Convert.ToDateTime(tableCell), null);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Helpers.LogException(ex);
                        }                       
                    }
                }
 
                col.Add(objInstance);
            }
 
            return col;
        }
    }

Open in new window

0
 
LVL 19

Expert Comment

by:drichards
ID: 22991034
I am not sure what your goal is, so I can't comment too much on the code.  I can say, however, that the reason your first code didn't work is that you did not ever retrieve the value of the Metadata property from the Product instance, so you can't set its value.  I've included a fix for that below.  Note that propertyParent is now the result of a GetValue call to retrieve the Metadata value from the Product instance.  I renamed the PropertyInfo to propertyParentInfo so it's clear what I did.

Additionally, you can't set the Keywords property of metadata to a double because it's a string property.  You could use the PropertyInfo.PropertyType value to figure out how to cast the value you want to set to the correct type.
            string propertyToAssign = "MetaData.Keywords";
            Product oProduct = new Product();
 
            PropertyInfo propertyParentInfo = oProduct.GetType().GetProperty(propertyToAssign.Split('.')[0]); //MetaData
            // Need to get the value of the Metadata property so we can set sub-parts
            object propertyParent = propertyParentInfo.GetValue(oProduct, null);
            PropertyInfo propertyInfo = propertyParentInfo.PropertyType.GetProperty(propertyToAssign.Split('.')[1]); //Keywords
 
            propertyInfo.SetValue(propertyParent, "Key1, Key2", null);

Open in new window

0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project
High user turnover can cause old/redundant user data to consume valuable space. UserResourceCleanup was developed to address this by automatically deleting user folders when the user account is deleted.
Integration Management Part 2
Despite its rising prevalence in the business world, "the cloud" is still misunderstood. Some companies still believe common misconceptions about lack of security in cloud solutions and many misuses of cloud storage options still occur every day. …
Suggested Courses

581 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question