Jason Coyne

October 2005 Entries

Accessing columns collection via the private autoGenColumnsArray property in a DataGrid

 I have seen several people looking for a way to access the Columns
collection when using the AutoGenerate = true option. Some
people have gotten so far as to find the private autoGenColumnsArray
that has the information, but we as developers have no way to access
this information.

I have come up with a solution for the problem, (as I am sure many
others have) using reflection. Here is some sample code that will
print out the auto generated column names from a datagrid.

As stated, this code uses reflection. Reflection is slow. It might not
also be allowed depending on your system's security settings. Also, if
MS changes the internal structure of the DataGrid, this might break.
(We are breaking encapsulation)

Provided, is a function that will generically allow you to get the
value of any private member of a class using reflection.

Sorry about the formatting, I dont know a good way to format code for
usenet. Visual Studio should clean up the code just find for you tho.


DataGrid dg = new DataGrid();
dg.DataSource =new DataTable();// (Run some query or whatnot here)
dg.DataBind

ArrayList AutoGeneratedColumns
= (ArrayList) GetPrivateField(dg,"autoGenColumnsArray") ;

if (AutoGeneratedColumns!= null)
foreach (DataGridColumn CurrentColumn in AutoGeneratedColumns)
{
Response.Write(CurrentColumn.HeaderText);
}

public static object GetPrivateField(object PassedObject, string
FieldName)
{

object Field=null;

if (PassedObject == null)
throw new ArgumentNullException("PassedObject"
,"PassedObject must be an instantiated object.");

if (FieldName == null || FieldName.Trim() == "")
throw new ArgumentOutOfRangeException("FieldName"
,"Fieldname must be a non empty string.");

Type ObjectType = PassedObject.GetType();
System.Reflection.FieldInfo PrivateField =
ObjectType.GetField(FieldName
,System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.IgnoreCase);

if (PrivateField == null)
throw new ArgumentOutOfRangeException("FieldName"
, ObjectType.FullName + " does not have a field : " + FieldName +
".");

Field = PrivateField.GetValue(PassedObject);
return Field;
}

Getting human readable values out of an enum.

I found a post on Abhinaba's Blog talking about a cool way to override ToString on an enum to get human readable values out of it. I like the atributes, but have my own solution below.

 In the past I have used a function that converts the enum strings to human strings.  This code relies on the developer using good naming standards for the enum values. But if you can't rely on that, you couldn't rely on them to put in the attribute values either, so I call it a wash.

I have another function that you can pass an enum type, and it will populate a drop down list from the enum, using the int value for the list values, and the text description (passed through this function) for the text value.

I may try to unify the two solutions, so that the attributes are provided, but the strings are generated automatically.

public static string FixEnumString(string s)

{

string output = "";

StringBuilder sb = new StringBuilder(s.Length);

foreach (char c in s)

{

if (sb.Length != 0)

{

if (c.ToString()!=c.ToString().ToLower())

sb.Append(" ");

}

sb.Append(c);

}

output = sb.ToString();

output = output.Replace("I D", "ID");

output = output.Replace("_"," ");

output = output.Replace(" ", " ");

 

return output;

}

 

 

public static void PopulateDropDownListBoxFromEnum(DropDownList list, System.Type enumType)

{

list.Items.Clear();

foreach (object currentEnumValueObject in System.Enum.GetValues(enumType))

{

int currentEnumValue = (int) currentEnumValueObject;

string currentEnumText= currentEnumValueObject.ToString();

currentEnumText = FixEnumString(currentEnumText);

//currentEnumText = Translator.TranslateText(currentEnumText);

list.Items.Add(new ListItem(currentEnumText, currentEnumValue.ToString()));

}

}

 

Solution : How to handle null values (especially date time) in a c# web service

Here is another programming blog entry. Sorry for the normal friends :)

I recently ran into a problem with a web service I was trying to call from c# where the web service returned null dates (and other null values on elements that end up de-serialized as value types)

I didn't find any good solutions online (other than wait for nullable types in c# 2.0 or change the service, neither of which was applicable in my situation)

I ended up working out a solution with microsoft, and here it is. The following class will loop through a returned soap call, and set any datetime fields that are null to be DateTime.MinDate

This can easily be extended to enable any other fields that need default values (enums, etc)

To use this class, edit the reference.cs file that is generated by .net, and add this decoration to each webMethod call that you want to apply the fix to.

[SoapFixer.SoapFixer()]

for example, one of my functions looks like this :

[System.Web.Services.Protocols.SoapHeaderAttribute("AuthenticationInfoValue")]
 [System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:HPD_Help_Desk/OpGetList", RequestNamespace="urn:HPD_Help_Desk", ResponseNamespace="urn:HPD_Help_Desk", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("getListValues")]
[SoapFixer.SoapFixer()]
        public GetListOutputMapGetListValues[] OpGetList(string Qualification) {
            object[] results = this.Invoke("OpGetList", new object[] {
                        Qualification});
            return ((GetListOutputMapGetListValues[])(results[0]));
        }


The SoapFixer class decends from SoapExtensionAttribute, which makes all the magic happen. The class loops through each node in the returned XML, and uses reflection to find the appropriate field in the destination class. If the destination field is a datetime, and the value is null, the value is converted to MinDate (1/1/0001)

The field lookup starts out by assuming that the node name in the xml is identical to the class field name. However, this is not always true.  In some cases (notably where the XML element name is an invalid field name, like it has a - in it)   they are not the same. In this case, the field is decorated with a XmlElementAttribute attribute that specifies the real name. The GetFieldInfo function(s) try to grab the field name assuming an exact match, and if that fails tries to grab the attributes and get a match that way. Any fields that are not found are written out to the console.

using System;
using System.IO;
using System.Web;
using System.Web.Services.Protocols;
using System.Web.Services.Description;
using System.Xml;
using Rockwell.Library.RemedyWebService;
using System.Reflection;
using System.Xml.Serialization;

namespace Rockwell.Library.DataAccessor.Reusable
{
    ///


    /// Summary description for CaptureSoap.
    ///

    [AttributeUsage(AttributeTargets.Method)]
    public class RemedySoapFixer : SoapExtensionAttribute
    {
        private int m_Priority = 0;

        public override Type ExtensionType
        {
            get
            {
                return typeof(DeleteDate);
            }
        }

        public override int Priority
        {
            get
            {
                return m_Priority;
            }
            set
            {
                m_Priority = value;
            }
        }

    }

    public class DeleteDate : SoapExtension
    {
        private Stream soapStream;
        private Stream customStream;
//Switch this to be your destination field type. (IE, the return value of your web method)
        static GetListOutputMapGetListValues exampleItem = new GetListOutputMapGetListValues();

        static FieldInfo[] fields = ((object)exampleItem).GetType().GetFields();

        public override object GetInitializer(Type serviceType)
        {  
            return GetType();
        }

        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
        {
            return null;
        }

        public override void Initialize(object initializer)
        {

        }

        public override Stream ChainStream(Stream stream)
        {
            soapStream = stream;
            customStream = new MemoryStream();
            return customStream;
        }
      
        public override void ProcessMessage(SoapMessage message)
        {
            switch(message.Stage)
            {
                case SoapMessageStage.BeforeSerialize:                  
                    break;
                case SoapMessageStage.AfterSerialize:
                    ChangeOutgoingSoap(message);
                    break;
                case SoapMessageStage.BeforeDeserialize:
                    ChangeIncomingSoap(message);
                    break;
                case SoapMessageStage.AfterDeserialize:
                    break;
                default:
                    break;
            }
        }
          
  
        public void ChangeOutgoingSoap(SoapMessage message)
        {
            //reads the content of the customStream
            //modify it, log it, whatever
            //and write it to soapStream

            Stream tempstream = new MemoryStream();
            customStream.Position = 0;
            Copy (customStream,tempstream);
            customStream.Position = 0;
            tempstream.Position = 0;
            XmlDocument doc = new XmlDocument();
            doc.Load(tempstream);
            //modify outgoing XML here
            //doc.GetElementsByTagName("elementname").Item(0).InnerText = "whatever";
            doc.Save(customStream);  
  
            customStream.Position = 0;
            Copy(customStream, soapStream);
        }

        public void ChangeIncomingSoap(SoapMessage message)
        {
            //reads the content of the soapStream
            //modify it, log it, whatever
            //and write it to customStream
  
            Copy(soapStream, customStream);
            customStream.Position = 0;

            Stream tempstream = new MemoryStream();
            customStream.Position = 0;
            Copy (customStream,tempstream);
            customStream.Position = 0;
            tempstream.Position = 0;
            XmlDocument doc = new XmlDocument();
            doc.Load(tempstream);
 
            FixNodes(doc);
          
        
            doc.Save(customStream);
            customStream.Position = 0;
        }

 

        public FieldInfo GetFieldInfo(object target, string fieldName)
        {
            if (target == null)
                throw new ArgumentNullException("target", "target must be an instantiated object.");

            if (fieldName == null || fieldName.Trim().Length==0)
                throw new ArgumentOutOfRangeException("fieldName", "Fieldname must be a non empty string.");

          
      
            Type objectType = target.GetType();

          
          

            FieldInfo privateField = objectType.GetField(fieldName,
                BindingFlags.Instance
                | BindingFlags.NonPublic
                | BindingFlags.Public
                | BindingFlags.IgnoreCase
                | BindingFlags.FlattenHierarchy
              
                );

          
            if (privateField == null)
                throw new ArgumentOutOfRangeException("FieldName", objectType.FullName + " does not have a field : " + fieldName + ".");

            return privateField;
        }

        private FieldInfo GetFieldInfo(XmlNode node)
        {
            if (node.Name =="xml"
                || node.Name =="#text")
                return null;

            FieldInfo fieldInfo = null;
            try
            {
                fieldInfo = GetFieldInfo(exampleItem, node.Name);
            }
            catch(ArgumentOutOfRangeException)
            {
                foreach (FieldInfo currentFieldInfo in fields)
                {
                    if (currentFieldInfo.Name == node.Name)
                        fieldInfo = currentFieldInfo;
                  
                    if (fieldInfo==null)
                    {
                       object[] attributes = currentFieldInfo.GetCustomAttributes(false);
                        foreach (object currentAttribute in attributes)
                        {
                         
                            XmlElementAttribute currentElementAttribute;
                            currentElementAttribute = currentAttribute as XmlElementAttribute;
                            if (currentElementAttribute!=null)
                            {
                                if (currentElementAttribute.ElementName == node.Name)
                                   fieldInfo = currentFieldInfo;
                            }
                        }
                    }

                }
      
            }
            return fieldInfo;
        }

        private void FixNodes (XmlNode node)
        {
            foreach (XmlNode childNode in node.ChildNodes)
            {
                FixNodes (childNode);
            }

            FieldInfo fieldInfo = GetFieldInfo(node);

            if (fieldInfo!=null)
            {
                if (fieldInfo.FieldType ==typeof(DateTime))
                  
                {
                    if (node.InnerText.Length==0)
                        node.InnerText = "0001-01-01T00:00:00-06:00";
                    else
                    {
                        DateTime dt;
                        try
                        {
                            dt = DateTime.Parse(node.InnerText);
                        }

                        catch (FormatException)
                        {
                            Console.WriteLine ("{0}, {1}", node.Name, node.InnerText);
                            node.InnerText = "0001-01-01T00:00:00-06:00";

                        }
                    }
                }

            
      
      
          
            }
            else
            {
            if (node.Name!="#text")
                Console.WriteLine("field not found for {0}", node.Name);
            }
        }

        private void Copy(Stream from, Stream to)
        {
            TextReader reader = new StreamReader(from);
            TextWriter writer = new StreamWriter(to);
            writer.WriteLine(reader.ReadToEnd());
            writer.Flush();
        }
      
    }
}