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...
|
[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
{
if
( _dataSource !=
null
&& _dataSource.Equals (
value
) )
return
;
_dataSource =
value
;
_dataMember = String.Empty ;
}
}
[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 object _dataSource
;
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