Chris Donnan : Programming - Brooklyn Style
software, trading, family, fun
Posted Xml, c#, databinding on Monday, January 9th, 2006.
Lately - I have been up to some Presentation Layer programming. I have been making a mechanism to create bind-able things from our entity objects. This will enable us to bind to grids and other UI elements. We need to be able to create N number of views for our Entity objects - and not put code in odd places in our layered architecture. For example; if we have a FooObject entity class (and/ or several derived FooObject types), - we need to make many different views of this same entity - really entity collections - FooObjectCollection(s).
That being said - in the past I have done this a few ways -
#1) Implement the interfaces for binding (needs to be done over and over for all objects). Since our objects are sometimes quite deep aggregates of other objects, this amounts to lots of work in this case. It is not very DRY (Dont Repeat Yourself) - you need to do this again and again for each object that you want to bind.
#2) Attributes on the entity classes to define attributes at binding- the limitation here is that you get 1 view definition and it is embedded in the entity object. This also puts code for the Presentation Layer in the Domain Layer. This involves reflection and there are more ‘reflectiony variations’ on this approach - but they are basically the same.
#3) Manually create datasets, datatables, dataviews, etc. from your entities. This means manually plucking info out of your classes and sticking them into the datasets. This approach results in you loosing your entities and programming to datasets etc. It becomes a mess. You could use many dataset variants - strongly typed datasets, non-strongly typed datasets, yadda yadda….
#4) Our approach in the end was to use metadata to create a bindable something. This metadata is XML that looks something like:
The XML Meta-data for binding looks like this (this is a flat view):
2 <View>
3 <Band DisplayName=”FooObject” Type=”Some.Name.Space.FooObject, Some.Name.Space”>
4 <Column DisplayName=”FooObjectDetails Id” Path=”FooObjectDetails.Id” Type=”System.String” />
5 <Column DisplayName=”FooObjectDetails Version” Path=”FooObjectDetails.Version” Type=”System.Int32″ />
6 <Column DisplayName=”FooObjectDetails Type” Path=”FooObjectDetails.Type” Type=”System.String” />
7 <Column DisplayName=”FooObjectDetails Name” Path=”FooObjectDetails.Name” Type=”System.String” />
8 <Column DisplayName=”FooObjectDetails FlahFlah” Path=”FooObjectDetails.FlahFlah” Type=”System.String” />
9 <Column DisplayName=”FooObjectDetails IsSomething” Path=”FooObjectDetails.IsSomething” Type=”System.Boolean” />
10 <Column DisplayName=”FooObjectDetails State” Path=”FooObjectDetails.State” Type=”Some.Name.Space.State, Some.Name.Space” />
11 <Column DisplayName= “Location” Path=”FooObjectDetails.Location” Type=”Some.Name.Space.Location, Some.Name.Space” />
12 <Column DisplayName= “SizeOfMe” Path=”FooObjectDetails.SizeOfMe” Type=”Some.Name.Space.SizeOfMe, Some.Name.Space” />
13 <Column DisplayName=”FooObjectDetails FooObjectId” Path=”FooObjectDetails.FooObjectId” Type=”System.String” />
14 <Column DisplayName=”FooObjectDetails Approval” Path=”FooObjectDetails.Approval” Type=”System.String” />
15 <Column DisplayName=”FooObjectDetails SuperHat” Path=”FooObjectDetails.SuperHat” Type=”System.Int32″ />
16 <Column DisplayName=”FooObjectDetails SomeScenario” Path=”FooObjectDetails.SomeScenario” Type=”System.String” />
17 <Column DisplayName= “UptraProperty” Path=”FooObjectDetails.UptraProperty” Type=”System.String” />
18 <Column DisplayName=”FooObjectDetails ChildClothing” Path=”FooObjectDetails.ChildClothing” Type=”System.Double” />
19 <Column DisplayName=”FooObjectDetails Gap” Path=”FooObjectDetails.Gap” Type=”System.String” />
20 <Column DisplayName=”FooObjectDetails ProductGroup” Path=”FooObjectDetails.ProductGroup” Type=”System.String” />
21 <Column DisplayName=”FooObjectDetails Blargh Ref” Path=”FooObjectDetails.Blargh.Ref” Type=”System.String” />
22 <Column DisplayName=”FooObjectDetails Blargh Id” Path=”FooObjectDetails.Blargh.Id” Type=”System.String” />
23 <Column DisplayName= “RationaleOfLife” Path=”FooObjectDetails.RationaleOfLife” Type=”Some.Name.Space.RationaleOfLife, Some.Name.Space” />
24 <Column DisplayName= “FeeType” Path=”FooObjectDetails.AProperty.FeeType” Type=”Some.Name.Space.FeeType2, Some.Name.Space” />
25 <Column DisplayName= “SalesPerson” Path=”FooObjectDetails.AProperty.SalesPerson” Type=”System.String” />
26 <Column DisplayName= “KiddieKooKi Farfehn” Path=”FooObjectDetails.AProperty.KiddieKooKi.Farfehn” Type=”System.String” />
27 <Column DisplayName= “AProperty KiddieKooKi Amount” Path=”FooObjectDetails.AProperty.KiddieKooKi.Amount” Type=”System.Double” />
28 <Column DisplayName=”FooObjectDetails AProperty Farfehn” Path=”FooObjectDetails.AProperty.Farfehn” Type=”System.String” />
That Xml is then loaded as part of a IViewDescriptor in IObjectView :
3 public interface IObjectView
4 {
5 ///
6 /// Gets or sets the source object.
7 ///
8 /// The source object.
9 object SourceObject { get; set; }
10
11 ///
12 /// Gets or sets the view descriptor.
13 ///
14 /// The view descriptor.
15 IViewDescriptor ViewDescriptor { get; set; }
16
17 ///
18 /// Gets the bindable object.
19 ///
20 ///
21 object GetBindableObject();
22 }
Then at runtime you get the IViewDefintion, the source object - and call GetBindableObject() to get something to bind to.
Like so:
257 private void BindDemoForm_Load(object sender, EventArgs e)
258 {
262 // Get a view from the view repository
263 IObjectView objectView = appContext.ObjectViewRepository.GetByKey( “BindDemoForm.ModelView3″ );
264
265 //give the view some data to bind to (the fooObjects in the FooObjectRepository in this case)
266 objectView.SourceObject = appContext.FooObjectRepository.Values;
267
268 // bind to the grid
269 gridBinder.SetObjectToGrid( grid, objectView );
270
271 // get underlying objects when grid is clicked
272 gridBinder.RowDoubleClick += new StuckRowClickHandler(gridBinder_RowDoubleClick);
273 }
After that - we hook up some events so that when we select or click on a row in a grid - we get the underlying Entity object in stead of a row. This means that all of your business operations can happen on your business object - not on a dataset, etc.
This looks something like:
345 private void gridBinder_RowDoubleClick(object sender, StuckRowClickEventArgs e)
346 {
347 // get the underlying item in 1 line of code ![]()
348 FooObject fooObject = e.BoundObject as FooObject;
349 Debug.Assert( fooObject != null );
350 string formatString = “Your underlying object \nFooObject Id = {0}, FooObject Version = {1}, Type = [{2}]“;
351 string fooObjectDescription = string.Format(
352 formatString,
353 fooObject.FooObjectDetails.Id,
354 fooObject.FooObjectDetails.Version,
355 fooObject.GetType().ToString()
356 );
357 fooObject.FooObjectDetails.FooObjectId = new Random().Next().ToString();
358 gridBinder.UpdateRowFromObject( e.GridRow, fooObject );
359 MessageBox.Show( this, fooObjectDescription );
360 }
There are several advantages to this approach: You can flatten your view of a hierarchical object, you can change the look in the hierarchy, or of course - you can use the existing hierarchy. You can define an unlimited number of views. You can do business operations on your objects directly - not on DataSets/ DataTables/ DataRows, etc.
Behind the scenes - in essence - we are inferring a schema from the root object using reflection. From there - we create a series of datatables in a dataset - complete with relations for the hierarchy etc.
It was quite easy to derive classes from DataTable, DataRow and DataColumn. Doing this allowed me to attach the ColumnDescriptor class and the bound object to the DataRow. The ColumnDescriptors keep the translated meta-data at run-time. The underlying object that the row (and child rows) are bound to are in the BoundObject of the DataRow. The DataTable keeps a reference to the translated meta-data - the BandDescriptor and creates new derived DataRows as required.
9 public class StuckDataTable : DataTable
10 {
11 private BandDescriptor bandDescriptor;
12
13 public BandDescriptor BandDescriptor
14 {
15 get { return bandDescriptor; }
16 set { bandDescriptor = value; }
17 }
18
19 public StuckDataTable()
20 : base()
21 {
22 }
23
24 public StuckDataTable( SerializationInfo info, StreamingContext context )
25 : base( info, context )
26 {
27 }
28
29 public StuckDataTable( string tableName )
30 : base( tableName )
31 {
32 }
33
34 protected override DataRow NewRowFromBuilder( DataRowBuilder builder )
35 {
36 return new StuckDataRow( builder );
37 }
38 }
9 public class StuckDataColumn : DataColumn
10 {
11 private ColumnDescriptor columnDescriptor;
12
13 public StuckDataColumn()
14 : base()
15 {
16 }
17
18 public StuckDataColumn( string columnName, Type dataType, string expr, MappingType type )
19 : base( columnName, dataType, expr, type)
20 {
21 }
22
23 public StuckDataColumn( string columnName, Type dataType, string expr )
24 : base( columnName, dataType, expr )
25 {
26 }
27
28 public StuckDataColumn( string columnName, Type dataType )
29 : base( columnName, dataType )
30 {
31 }
32
33 public StuckDataColumn( string columnName )
34 : base( columnName )
35 {
36 }
37
38 public ColumnDescriptor ColumnDescriptor
39 {
40 get { return columnDescriptor; }
41 set { columnDescriptor = value; }
42 }
43
44 }
8 public class StuckDataRow : DataRow
9 {
10 object boundObject;
11
12 public StuckDataRow( DataRowBuilder builder )
13 : base( builder )
14 {
15 }
16
17 public object BoundObject
18 {
19 get { return boundObject; }
20 set { boundObject = value; }
21 }
22 }
To generate the Xml to do the binding required I whipped up a simple little UI. This UI allows you to load an assembly, list it’s types and generate the basic Xml Meta-data - either flattened or to match the full object hierarchy. From there - you can re-arrange the Xml, delete some etc. This gets us far along the path of binding very complex objects to grids and other things.More to come on ObjectBinding - I have flapped enough for tonight. We can chat some more about how Enum binding was handled, the getting and setting of properties using the nifty Object.SubObject.SubObject.Property syntax….. more to come.
-Chris-
Comment on this post below
You must be logged in to post a comment.
You can leave a response, or trackback from your own site.















