Vishful thinking…

Simulating properties on a bindable list using ITypedList

Posted in .NET, C#, DataBinding by viswaug on August 29, 2007

In this blog post I am going to illustrate how to simulate properties on a bindable list using the ITypedList interface. The ITypedList interface is used by the .NET data-binding mechanism to discover the properties of the items in the list that is being bound. So in fact the ITypedList interface can be implemented by custom collections to customize the properties available for binding on the object to bind to. The .NET data-binding mechanisms use the GetItemProperties(…) method on the ITypedList interface to obtain a PropertyDescriptorCollection to obtain the list of properties available on the object in the list. So, the GetItemProperties(…) method can be overridden to return a custom collection of properties that will be used for data-binding.

Let us take for example, the ‘Distance’ class given below.

    public class Distance

    {

        double _tolerance = 0;

        esriUnits _units = esriUnits.esriFeet;

 

        public Distance(){}

 

        public Distance(double ptolerance, esriUnits punits)

        {

            _tolerance = ptolerance;

            _units = punits;

        }

 

        public double Tolerance

        {

            get { return _tolerance; }

            set { _tolerance = value; }

        }

 

        public esriUnits Units

        {

            get { return _units; }

            set { _units = value; }

        }

 

    }

 

The ‘Distance’ class above contains a tolerance measure and its units as it is public properties. Let’s say that we also have a typed collection ‘DistanceCollection’ of the ‘Distance’ objects.

    public class DistanceCollection

    {

 

        #region “CollectionBase Members”

 

        public Distance this[int index]

        {

            get

            {

                return ((Distance)List[index]);

            }

            set

            {

                List[index] = value;

            }

        }

 

        public int Add(Distance value)

        {

            return (List.Add(value));

        }

 

        public int IndexOf(Distance value)

        {

            return (List.IndexOf(value));

        }

 

        public void Insert(int index, Distance value)

        {

            List.Insert(index, value);

        }

 

        public void Remove(Distance value)

        {

            List.Remove(value);

        }

 

        public bool Contains(Distance value)

        {

            // If value is not of type Distance, this will return false.

            return (List.Contains(value));

        }

 

        protected override void OnInsert(int index, Object value)

        {

            // Insert additional code to be run only when inserting values.

            if (value.GetType() != typeof(Distance))

                throw new ArgumentException(“value must be of type Distance.”, “value”);

        }

 

        protected override void OnRemove(int index, Object value)

        {

            // Insert additional code to be run only when removing values.

            if (value.GetType() != typeof(Distance))

                throw new ArgumentException(“value must be of type Distance.”, “value”);

        }

 

        protected override void OnSet(int index, Object oldValue, Object newValue)

        {

            // Insert additional code to be run only when setting values.

            if (oldValue.GetType() != typeof(Distance))

                throw new ArgumentException(“oldValue must be of type Distance.”, “value”);

 

            if (newValue.GetType() != typeof(Distance))

                throw new ArgumentException(“newValue must be of type Distance.”, “value”);

        }

 

        protected override void OnValidate(Object value)

        {

            if (value.GetType() != typeof(Distance))

                throw new ArgumentException(“value must be of type Distance.”, “value”);

        }

 

        #endregion

 

    }

 

A new datasource could be added in Visual Studio for the ‘DistanceCollection’ type as shown below…

SimulateProperty1

SimulateProperty2

SimulateProperty3

SimulateProperty4

SimulateProperty5

SimulateProperty6

Our goal is to simulate a property called ‘ToleranceInMeters’ on the ‘DistanceCollection’ type so that we have an additional column on the DataGridView specifying the tolerance in meters. In order to achieve this we implement ITypedList on the ‘DistanceCollection’ type and override the GetItemProperties(…) method so that it returns a PropertyDescriptorCollection object with an added PropertyDescriptor object for the ‘ToleranceInMeters’ property.

To return an additional property in the PropertyDescriptorCollection that returns the tolerance in meters, we create a new class called ‘ToleranceInMetersPropertyDescriptor’. And in the GetValue(…) method of the new class we implement the logic to convert the tolerance value from the existing units into meters. In the below code, the ‘ToleranceInMetersPropertyDescriptor’ class has been implemented as an inner class inside ‘DistanceCollection’. We also build the new PropertyDescriptorCollection in the static constructor of ‘DistanceCollection’ and return it inside the GetItemProperties(…) method.

 

    public class DistanceCollection : CollectionBase, ITypedList

    {

 

        static DistanceCollection()

        {

            CalculatePropertyDescriptors();

        }

 

        #region “CollectionBase Members”

 

        public Distance this[int index]

        {

            get

            {

                return ((Distance)List[index]);

            }

            set

            {

                List[index] = value;

            }

        }

 

        public int Add(Distance value)

        {

            return (List.Add(value));

        }

 

        public int IndexOf(Distance value)

        {

            return (List.IndexOf(value));

        }

 

        public void Insert(int index, Distance value)

        {

            List.Insert(index, value);

        }

 

        public void Remove(Distance value)

        {

            List.Remove(value);

        }

 

        public bool Contains(Distance value)

        {

            // If value is not of type Distance, this will return false.

            return (List.Contains(value));

        }

 

        protected override void OnInsert(int index, Object value)

        {

            // Insert additional code to be run only when inserting values.

            if (value.GetType() != typeof(Distance))

                throw new ArgumentException(“value must be of type Distance.”, “value”);

        }

 

        protected override void OnRemove(int index, Object value)

        {

            // Insert additional code to be run only when removing values.

            if (value.GetType() != typeof(Distance))

                throw new ArgumentException(“value must be of type Distance.”, “value”);

        }

 

        protected override void OnSet(int index, Object oldValue, Object newValue)

        {

            // Insert additional code to be run only when setting values.

            if (oldValue.GetType() != typeof(Distance))

                throw new ArgumentException(“oldValue must be of type Distance.”, “value”);

 

            if (newValue.GetType() != typeof(Distance))

                throw new ArgumentException(“newValue must be of type Distance.”, “value”);

        }

 

        protected override void OnValidate(Object value)

        {

            if (value.GetType() != typeof(Distance))

                throw new ArgumentException(“value must be of type Distance.”, “value”);

        }

 

        #endregion

 

        #region “Simulated Property”

 

        private class ToleranceInMetersPropertyDescriptor : PropertyDescriptor

        {

            UnitConverterClass _converter = new UnitConverterClass();

 

            public ToleranceInMetersPropertyDescriptor(string name)

                : base(name, null)

            {

 

            }

 

            public override bool CanResetValue(object component)

            {

                return false;

            }

 

            public override System.Type ComponentType

            {

                get { return typeof(Distance); }

            }

 

            public override bool IsReadOnly

            {

                get { return true; }

            }

 

            public override System.Type PropertyType

            {

                get { return typeof(double); }

            }

 

            public override void ResetValue(object component)

            {

 

            }

 

            public override object GetValue(object component)

            {

 

                Distance d = (Distance)component;

                return _converter.ConvertUnits(d.Tolerance, d.Units, esriUnits.esriMeters);

            }

 

            public override void SetValue(object component, object value)

            {

 

            }

 

            public override bool ShouldSerializeValue(object component)

            {

                return false;

            }

        }

 

 

        #endregion

 

        #region ITypedList Members

 

 

        private static PropertyDescriptorCollection propertyDescriptors;

 

        PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)

        {

            return propertyDescriptors;

        }

 

        string ITypedList.GetListName(PropertyDescriptor[] listAccessors)

        {

            return “DistanceCollection”;

        }

 

        private static void CalculatePropertyDescriptors()

        {

            PropertyDescriptorCollection origProperties = TypeDescriptor.GetProperties(typeof(Distance));

            ArrayList properties = new ArrayList();

            foreach (PropertyDescriptor desc in origProperties)

            {

                properties.Add(desc);

            }

            properties.Add(new ToleranceInMetersPropertyDescriptor(“ToleranceInMeters”));

            propertyDescriptors = new PropertyDescriptorCollection((PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor)));

        }

 

        #endregion

 

    }

 

What do you achieve by all this? Well, we get a property on the ‘DistanceCollection’ type in the Data Sources window in Visual Studio 2005 and when it is dragged on to the form the DataGridView now appears with a an additional column called ‘ToleranceInMeters’.

SimulateProperty7

In the constructor of the form, initialize the binding source for the DistanceCollection and the result will be what you see below.

 

DistanceCollection _Coll = new DistanceCollection();

_Coll.Add(new Distance(1, ESRI.ArcGIS.esriSystem.esriUnits.esriFeet));

_Coll.Add(new Distance(1, ESRI.ArcGIS.esriSystem.esriUnits.esriInches));

_Coll.Add(new Distance(1, ESRI.ArcGIS.esriSystem.esriUnits.esriKilometers));

_Coll.Add(new Distance(1, ESRI.ArcGIS.esriSystem.esriUnits.esriMiles));

_Coll.Add(new Distance(1, ESRI.ArcGIS.esriSystem.esriUnits.esriNauticalMiles));

_Coll.Add(new Distance(1, ESRI.ArcGIS.esriSystem.esriUnits.esriDecimalDegrees));

distanceCollectionBindingSource.DataSource = _Coll;

SimulateProperty8

Advertisements

Comments on Microsoft Enterprise Library Exception Block

Posted in .NET by viswaug on August 27, 2007

We have been using the “Microsoft Enterprise Library Exception Block” for exception handling and logging in the current Enterprise Information management Systems that I am working on. Based on my experience with the “Microsoft Enterprise Library Exception Block“, I had a couple of comments that wanted to put out there. These observations of mine may just be complaints about the library that I wish could have been circumvented. But I do agree that the library does provide us with a powerful tool to standardize the way exceptions are handled throughout the application. For those who haven’t used the Exception block library before, here is a quick overview. I feel that this is a great one line summary of the Exception block. “The Exception block separates the definition how an exception should be processed from the application code itself“.

Exception Handling Application Block

It is a framework for handling exceptions (catching, presentation to the user, logging, etc.) in a recommended and standardized way. It allows developers and policy makers to create a consistent strategy for processing exceptions that occur throughout the architectural layers of enterprise applications. It does this in the following ways:

  • It supports exception handling throughout an application’s architectural layers and is not limited to service interface boundaries.
  • It enables exception-handling policies to be defined and maintained at the administrative level so that policy makers, who may be system administrators as well as developers, can define how to handle exceptions. They can maintain and modify the sets of rules that govern exception handling without changing the application block’s code.
  • It provides commonly used exception-handling functions, such as the ability to log exception information, the ability to hide sensitive information by replacing the original exception with another exception, and the ability to add contextual information to an exception by wrapping the original exception inside of another exception. These functions are encapsulated in .NET classes called exception handlers.
  • It can combine exception handlers to produce the desired response to an exception, such as logging exception information followed by replacing the original exception with another.
  • It lets developers create their own exception handlers.
  • It invokes exception handlers in a consistent manner. This means that the handlers can be used in multiple places within and across applications.“

(ms-help://MS.VSCC.2003/ms.EntLib.2005Jun/ms.EntLib.2005Jun.Ex/ExceptionHandling/Intro/intro01.htm)

image

Architecture

The Exception block separates the definition of how an exception should be processed from the application code itself and moves this configuration to a XML config file. The configuration of the Exception block uses following terms:

Policy
A Policy has a name and a set of exception types that should be processed. An application can have several policies, because it could be necessary to have different processing instructions for one exception type, depending in which layer of the application the exception arises.

Exception Type
For each policy one or more exception types can be defined. Exception types can be one of the standard .net exception classes or user defined ones. When an exception is thrown the exception block will determine the type of the exception against the definitions in the used policy and call the exception handlers of this type.

Exception handlers
Exception handlers process the exception. An exception type can have one or more handlers. They are called by the application block in the order like they were configurated.
There are following built-in handlers:

  • Wrap handler: Creates a new exception and sets the original exception as inner exception for the new one.
  • Replace handler: Replaces an exception with a new (custom) one.
  • Logging handler: Logs the exception to the Enterprise application logging block, which can be used to log events to different targets like event log, files, databases etc.
  • Custom handlers: Developers can write their own exception handlers which can be easily included in the project (see “Creating a custom exception handler”.

The following figure illustrates examples of cross-layer and single-application component exception handling:

(ms-help://ms.EntLib.2005Jun/ms.EntLib.2005Jun.Ex/exceptionHandling/Intro/whentouse03.htm)

The figure above shows how the “UI Layer (UI)”, “Business Components Layer (BCL)” and the “Data Access Layer (DAL)” normally handle exceptions. Any exceptions that occur in the DAL library should be logged and then wrapped with some meaningful contextual information on where and possibly why the exception happened and passed up to the calling BCL library. The BCL library handles the exception by logging it and if necessary replacing it with an another exception that makes more sense when finally handled by the UI layer and displayed to the user.

The policy that should handle the exception is specified by the developer in the ExceptionPolicy.HandleException(…) method as shown below.

try

{

        //…

}

catch (Exception e)

{

        bool rethrow = ExceptionPolicy.HandleException(e, “exception policy name”);

        if (rethrow)

        {

                throw;

        }

}

Ideally, I think that there should be some kind of restrictions on the type of policies that can be specified in the parameter for the ExceptionPolicy.HandleException(…) method. By that I mean, for example the “Notify User” policy should never be used in the DAL or the BCL.

My other comment about the above code is that, the developer is now responsible for handling the “rethrow” boolean value returned by the ExceptionPolicy.HandleException(…) method and explicitly throwing the exception again. The rule to throw exception again is not encapsulated in the policy itself. Also, the decision to throw the exception again is based on the exception that has occurred. Some exceptions may need to be thrown again but some others need not. Given that most programmers are lazy, there is a real good chance that the exceptions do not thrown again. This can lead to situations where exceptions get swallowed without proper user notification if required. The kind of errors can be hard to track down because there may not be a user notification that some thing has gone wrong and can cause problems in parts of the system that rely on it. Maybe some custom FxCop rules can be checked to catch these errors before they move into production.

Static constructors in generic types

Posted in .NET, C# by viswaug on August 22, 2007

Static constructors are a relatively little known feature .NET. It might probably be because they don’t have too many use cases where they fit in perfectly. But they did come in handy when I was looking for ways for make a list of all the properties in my class that were decorated in with a custom attribute. The static constructor was an ideal place to build the collection so that the collection can be exposed as a static property on my class.

But I did have my doubts on whether I will be able to use the static constructors effectively on generics types. Generics types are tricky when it comes to static constructors since the generic type can be customized with other type parameters. But turns out the static constructors do work effectively on generic types. The type parameters are available in the static constructor and the type parameter T in the static constructors for CustomType<int> and CustomType<string> refer to their corresponding type parameters even though they use the same CustomType<T> class.

The use of static constructors can be observed in the project attached to one of my previous blog post.

Persisting ESRI objects to the database

Posted in .NET, ArcGIS, ESRI by viswaug on August 20, 2007

A little while ago, I was trying to persist some objects implementing the IPersistStream to the database. The database was MS SQL Server 2005 and the field type where the object was going to be stored was VARBINARY(MAX). The field was exposed in the DAL as an Byte array like it is supposed to be. So, now I had to write my objects implementing IPersistStream into an byte array. This took me a little while to figure out but turned to be simple enough. When, I had to do this before, I was writing to a table in a personal geodatabase. This was pretty straightforward since I was accessing and updating the storage table using ESRI’s ITable interfaces. In that case, I simply serialized my objects to a MemoryBlobStream and the wrote the MemoryBlobStream object into the table using the “Value” property on IRow. But this wouldn’t work for the current MS SQL server tables since I wasn’t accessing in through the ITable interface. So, I HAD to convert my MemoryBlobStream into an byte array. This is where the IMemoryBlobStreamVariant interface comes in handy. The following is how I was able to convert the MemoryBlobStream object to an byte array.

        Dim s As MemoryBlobStream = New MemoryBlobStream()

        Dim os As IObjectStream = New ObjectStream()

        os.Stream = TryCast(s, ESRI.ArcGIS.esriSystem.IStream)

        pitem.Save(os, 0)

 

        Dim content As Object = Nothing

        Dim mvar As IMemoryBlobStreamVariant = TryCast(s, IMemoryBlobStreamVariant)

        mvar.ExportToVariant(content)

        Dim bContent As Byte() = CType(content, Byte())

Databinding Enum values to combo boxes

Posted in .NET, C#, DataBinding by viswaug on June 13, 2007

Even though I dislike the use of enumerations, there are cases where you can use them to make your life a lot easier. In those cases, there might be a requirement to have the enumeration property data bound to a combo box which lists all possible values of the enumeration in some meaningful text and also be able to select the right enumeration text when the object is bound to it. I have written a small little utility that will do just that. It returns a Read-Only collection with some meaningful text as the “Name” and the enum value as the “Value” member of the collection. In the sample, the generic class EnumCollection<T> has a static DataSource property that returns a ReadOnlyICollection that is data bindable to a combo box. The type T represents an enumeration type. The text to describe the enumeration values can be added as custom attribute decorators in the definition of the enumeration as below.

 

1 public enum EnumSample

2 {

3 [Text(“This is One.”)]

4 One,

5 [Text(“This is Two.”)]

6 Two,

7 [Text(“This is Three.”)]

8 Three,

9 [Text(“This is Four.”)]

10 Four

11 }

 

The EnumCollection<T> class creates its collection in a static constructor for the generic class and thus is able to return the required collection in a static “DataSource” property. The definition of the EnumCollection<T> class is below.

 

1 public class EnumCollection<T> : ReadOnlyCollection<EnumClass<T>>

2 {

3 static EnumCollection<T> _val;

4

5 static EnumCollection()

6 {

7 if (!typeof(T).IsEnum)

8 throw new InvalidCastException(“sbEnumCollection only supports ‘enum’ types.”);

9

10 List<EnumClass<T>> vals = new List<EnumClass<T>>();

11 Array values = Enum.GetValues(typeof(T));

12 foreach (Object item in values)

13 {

14 EnumClass<T> entry = new EnumClass<T>(GetText<T>(item), (T)item);

15 vals.Add(entry);

16 }

17 _val = new EnumCollection<T>(vals);

18 }

19

20 private static string GetText<U>(object item) where U : T

21 {

22 if (!typeof(U).IsEnum)

23 throw new InvalidCastException(“GetText only supports ‘enum’ types.”);

24

25 FieldInfo fi = typeof(U).GetField(item.ToString());

26 string result = null;

27 if (fi != null)

28 {

29 object[] attrs = fi.GetCustomAttributes(typeof(TextAttribute), true);

30 if (attrs != null && attrs.Length > 0)

31 result = ((TextAttribute)attrs[0]).Text;

32 }

33

34 if (result == null)

35 result = Enum.GetName(typeof(T), item);

36

37 return result;

38 }

39

40 internal EnumCollection(IList<EnumClass<T>> list) : base(list) { }

41

42 public static EnumCollection<T> DataSource

43 {

44 get

45 {

46 return _val;

47 }

48 }

49 }

 

Enumerations can be data bound to combo boxes easily as shown below.

 

 

1 comboBox1.DataSource = EnumCollection<EnumSample>.DataSource;

2 comboBox1.DisplayMember = “Name”;

3 comboBox1.ValueMember = “Value”;

 

The complete project can be downloaded here.