Click here to Skip to main content
Click here to Skip to main content

Tagged as

Using PropertyGrid to Display and Edit Dynamic Objects

, 9 May 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
How to use PropertyGrid and dynamic objects to create a flexible and convenient business objects editor

Introduction

When developing business applications, you often encounter a small but annoying problem: it is necessary to create the business object's constructor so that the administrator of the application can add new properties to business objects (or modify old ones) directly in the user interface (without coding).

As an example, consider the application managing information about employees of an organization. The class Employee already contains properties FirstName, LastName, BirthDate, etc., but now the administrator needs to add a new property BirthPlace.

public class Employee
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime BirthDate { get; set; }
}
Source 1. Simple strongly typed business object.

What comes to mind first when you need to implement this functionality is to not use typed business objects, but dynamically configured objects. That is, the class, instead of clearly defined properties (such as FirstName, LastName, etc.), will contain a collection of pseudo-properties, and then we can add and delete new properties at any time, as well as, refer to the desired property by name. To refer to a property of the business object, you will need to access this collection, look for the right property by name, and write back the new values. It seems to be a good solution, even though you must ensure the persistence of such an object, which means that you possibly would not be able to use direct ORM-tools such as Entity Framework or nHibernate.

dynamic employee = new BusinessObject(); 
employee.FirstName = "John"; 
employee.LastName = "Doe"; 
Source 2. Using a dynamic business object.

The second task is to create a user interface to view and edit this object (editor). If the structure of the object, its contract and public properties are known in advance (as it often is in case of ordinary classes), that is not a big problem actually, because you can easily make any form for manual editing. But with the dynamic (configurable) object it would rather be a problem: you don’t know its structure during the development, therefore you don’t know how to make the editor. The next solution coming to mind is to use dynamically generated UI. Everyone knows the PropertyGrid control from Visual Studio – it uses reflection to examine unknown object, then displays the object’s structure and provides editors for its properties. Basically, this control is a convenient tool, it is configurable, and it is suitable for administration tasks very well. However, as the control uses reflection, it cannot find our dynamically defined properties in configurable object, because the structure of the object is different.

pic1.png

Figure 1. Using PropertyGrid for editing the business object

It is clear that we can replace the data extracted from the object by the reflection with our own data. For this purpose, we can use class PropertyDescriptor (or rather its inheritor), which is an abstraction of a property class, and is commonly used in designers within the Visual Studio. Not a bad idea, but it will cause a small inconvenience: you would not be able to work with business object as with ordinary strongly typed object and to refer its properties directly in the usual way, but you will have to find somehow the desired properties by their names and refer to them indirectly.

var employee = new BusinessObject(); 
employee["FirstName"] = "John"; 
employee["LastName"] = "Doe";
Source 3. Access to the dynamic properties of business object in the traditional way (in this case, through the indexer)

This code (source 3) looks unusual and rather awkward. It may even cause random errors and increase development time, since it violates the stereotypes of using objects can be really distracting and interfere with the developer’s work.

As we see, the solution above would work properly, but it has a number of disadvantages. Therefore, I would suggest using an even more interesting and convenient solution.

In NET 4.0, there is a newly appeared feature - dynamic types. One can get more information about dynamic types from MSDN, so I would not talk much about theory in this article. What I want to say is that the class DynamicObject allows the developer determine the operations, that can be performed on the dynamic properties and set how to perform these operations. For example, you can determine what should happen when you try to get or set an object property, call the method or to perform standard mathematical operations like addition and multiplication. This class can be useful if you want to create a user-friendly application interface for our business object, as it allows you to access properties that have not been defined previously. You cannot create an instance of DynamicObject directly. To implement the dynamic behavior, you can inherit from the DynamicObject class and override the necessary methods TryGetMember and TrySetMember.

public class BusinessObject : DynamicObject 
{
    private readonly IDictionary<string, /> dynamicProperties = 
        new Dictionary<string, />();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var memberName = binder.Name;
        return dynamicProperties.TryGetValue(memberName, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var memberName = binder.Name;
        dynamicProperties[memberName] = value;
        return true;
    }
}
Source 4. A trivial implementation of the class that supports dynamic properties.

This gives us a great advantage, since we can refer to an instance of the dynamic type in an arbitrary manner and thus simulate the missing property.

For example, in our class BusinessObject there is no property FirstName, however, we can refer it, and with proper implementation of this dynamic type, this property will be added automatically. Our dynamic type looks quite the same as usual classes and we refer to its properties through “dot” notation.

To ensure proper work of PropertyGrid, we must implement the inheritor of DynamicObject so that it could provide the control with description of its properties: names, data types, attributes that are specific to PropertyGrid (such as Category, or Description). The example of such implementation, given below, does not support attributes for simplicity's sake.

public class BusinessObject : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
{
    private readonly IDictionary<string, object> dynamicProperties = 
        new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var memberName = binder.Name;
        return dynamicProperties.TryGetValue(memberName, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var memberName = binder.Name;
        dynamicProperties[memberName] = value; 
        NotifyToRefreshAllProperties();
        return true;
    }


    #region Implementation of ICustomTypeDescriptor

    public PropertyDescriptorCollection GetProperties()
    {
        // of course, here must be the attributes associated
        // with each of the dynamic properties
        var attributes = new Attribute[0];
        var properties = dynamicProperties
            .Select(pair => new DynamicPropertyDescriptor(this, 
                pair.Key, pair.Value.GetType(), attributes));
        return new PropertyDescriptorCollection(properties.ToArray());
    }

    public string GetClassName()
    {
        return GetType().Name;
    }

    #region Hide not implemented members

    . . . . . 
    #endregion

	#region Implementation of INotifyPropertyChanged
	
	public event PropertyChangedEventHandler PropertyChanged;
	
	private void OnPropertyChanged(string propertyName)
	{
	  if (PropertyChanged == null)
	  {
	      return;
	  }
	
	  var eventArgs = new PropertyChangedEventArgs(propertyName);
	  PropertyChanged(this, eventArgs);
	}
	
	private void NotifyToRefreshAllProperties()
	{
	  OnPropertyChanged(String.Empty);
	}
	
	#endregion
	
	
	private class DynamicPropertyDescriptor : PropertyDescriptor
	{
	  . . . . . 
	}
Source 5. The class supporting dynamic properties and interaction with the PropertyGrid.

As you can see from source 5, our class implements the interface ICustomTypeDescriptor for the proper work of PropertyGrid.

ICustomTypeDescriptor interface allows an object to provide information about its inner structure, i.e., data type (unlike a TypeDescriptor it can retrieve information from the metadata). For example, ICustomTypeDescriptor is used in .NET to provide information about the types of COM objects, which do not support properties or attributes. To provide access to dynamic data about the type, dynamic class can implement ICustomTypeDescriptor or inherit CustomTypeDescriptor class, which provides a simple implementation of this interface.

In addition, our class contains a nested private class DynamicPropertyDescriptor, which describes the dynamic properties.

Most common solution is presented in the following UML-diagram:

pic2.png

Figure 2. Class diagram of an infrastructure supporting dynamic objects.
private class DynamicPropertyDescriptor : PropertyDescriptor
{
    private readonly BusinessObject businessObject;
    private readonly Type propertyType;

    public DynamicPropertyDescriptor(BusinessObject businessObject, 
        string propertyName, Type propertyType, Attribute[] propertyAttributes) 
        : base(propertyName, propertyAttributes)
    {
        this.businessObject = businessObject;
        this.propertyType = propertyType;
    }

    public override bool CanResetValue(object component)
    {
        return true;
    }

    public override object GetValue(object component)
    {
        return businessObject.dynamicProperties[Name];
    }

    public override void ResetValue(object component)
    {
    }

    public override void SetValue(object component, object value)
    {
        businessObject.dynamicProperties[Name] = value;
    }

    public override bool ShouldSerializeValue(object component)
    {
        return false;
    }

    public override Type ComponentType
    {
        get { return typeof(BusinessObject); }
    } 

    public override bool IsReadOnly
    {
        get { return false; }
    }

    public override Type PropertyType
    {
        set { return propertyType; }
    }
}
Source 6. The implementation of a derived class PropertyDescriptor, describing the dynamic properties.

Now we use any realization of PropertyGrid. In this example, we consider the open source WPF control library from www.codeplex.com, named ExtendedWPFToolkit.

If we replace the following line:

var properties = TypeDescriptor.GetProperties(instance.GetType(), 
  new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.All)}); 

with another:

var properties = TypeDescriptor.GetProperties(instance) 

the PropertyGrid will display our business object with all properties added on runtime (of course, as we remember, we have not implemented property attributes support).

In the same way, we can use the PropertyGrid to view and edit the object and to manage the object’s properties on runtime. Besides, if we expand the PropertyGrid a bit by adding commands to add and remove properties, we will have a universal editor for business objects with a minimum of efforts. All we have to do is to provide the mechanism of persistence to save all added dynamic properties.

As you can see, this solution is flexible and convenient for everybody – it would be easy to operate for the application user, and it would be fast and convenient to operate with such a class for the programmer.

<extToolkit:PropertyGrid Name="propertyGrid1" Height="249" Width="336" /> 

First, we should add in C# code the binding of the control to our business object:

propertyGrid1.SelectedObject = employee; 

Then we must find in PropertyGrid.cs file (in ExtendedWPFToolkit sources) the following method:

private List<PropertyItem> GetObjectProperties(object instance) 

The method receives data about the target object properties.

History

  • 9th May, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Andrey Karinskiy
Architect Smart Complexity
Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberiamsed25-Jul-12 1:07 
QuestionGood article, but no source/demo PinmemberFreddieDB26-Apr-12 9:14 
Questionstruggling PinmemberNickWarrington3-Nov-11 4:11 
GeneralGreat article, one addition Pinmemberkbussell18-May-11 16:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 9 May 2011
Article Copyright 2011 by Andrey Karinskiy
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid