Exposing DataSource and DataMember properties

The DataGrid and other controls contains two properties, DataSource and DataMember, which are used at design time to define the data binding. This short article shows how you can add these properties to your own class, and have them behave in an equivalent manner to those on 'standard' controls.

The first necessity is Reflector. If you don't have it already, download it now. I use it a great deal to find out how to integrate properly with the framework - the MSDN documentation is good, but source code seldom lies, and IL is as near as most of us will get to the Framework source code. Anyhow, install Reflector and then click on the View -> Customize menu item. This will bring up a dialog as shown below, and you should alter your settings to match.

The most important setting for this example is the 'Custom Attributes' check box. Now find the DataGrid class (hit F3 and type in DataGrid), and scroll down until you see the DataSource and DataMember properties - these are a fair way down the class structure. Once there you can then look at the attributes which define how these two properties are displayed and edited on screen.

DataSource Property

This property, of type object, is attributed as shown in the following screenshot.

The attributes of interest are [RefreshProperties] and [TypeConverter] - the others are used to define the category and description in the GUI and the default value.

The [RefreshProperties] attribute is defined as [RefreshProperties(RefreshProperties.Repaint)], which indicates that the VS.NET UI should repaint all properties displayed when the user changes this property. This flag is used here as there is a relationship between the data source and data member properties - when the data source changes, the data member is reset.

The [TypeConverter] object is much more interesting - it is used by the UI to determine how to display (and edit) the value of the DataSource property. If you look at the value of this attribute on the DataSource property, it is set to a private class 'System.Windows.Forms.Design.DataSourceConverter'. To replicate the behaviour of the UI we need to be able to attribute our property with the same type information.

Looking at the type converter attribute, it has a couple of constructors. One takes a System.Type object, but as the DataSourceConverter is a private class we can't just do a typeof(...DataSourceConverter) as that won't compile. Luckily (well it was probably judgement rather than luck), the designers of the TypeConverter class also saw fit to provide us with a constructor which takes a fully qualified type name. This means that any type converter can be added to a class simply by looking up the complete type name (i.e. including the assembly etc).

DataMember Property

This one's fairly simple to implement too. Again I've looked up the attributes used to find that the DataMember property is attributed with an [Editor] attribute. Once again this takes System.Type values, and once again the designers of the framework have permitted a string representation of the type to be used. The actual editor is System.Windows.Forms.Design.DataMemberListEditor, and the type string is a copy of that used above, except that DataMemberListEditor is substituted for DataSourceConverter.

Other than the attribute, the property has nothing special at all.

The Code

With that explained, the DataSource and DataMember properties are trivial to implement...

///
/// Get/Set the datasource
///

[Category("Data")]
[RefreshProperties(RefreshProperties.Repaint)]
[TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public object DataSource
{
    get { return _dataSource ; }
    set
    {
        // Ensure that the screen is only updated when the datasource changes
        if ( _dataSource != null && _dataSource.Equals ( value ) )
            return ;

        _dataSource = value ;

        // Reset the datamember
        _dataMember = String.Empty ;
    }
}

///
/// Get/Set the DataMember
///

[Category("Data")]
[Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",typeof(System.Drawing.Design.UITypeEditor))]
public string DataMember
{
    get { return _dataMember ; }
    set { _dataMember = value ; }
}

///
/// Private storage for the data source object
///

private object    _dataSource ;

///
/// Private storage for the data member
///

private string    _dataMember ;

The Results...

If you add these properties to your control and then display them within the property grid, you'll see something like the images below...

I have omitted the [Description] attribute on these two properties but you could add it in, or better yet check out my Localized Property Grid article which will show how you can add description attributes that are localized.

Summary

Integrating with the Visual Studio design time environment doesn't have to be hard - especially with the wealth of classes already there for you. A little investigation with Reflector and some knowledge of how Visual Studio does what it does can help you create components that have the same behaviour as the standard controls.

The attached file is a simple control which exposes the DataSource and DataMember properties. Note: This is a VS.NET 1.1 project.

DataSourceDataMember.zip