Presenting Detail Values as Part of the Master – part 3

By Bill at September 04, 2009 15:39
Filed Under: .Net Programming

This is part three of a three part series on working with master-detail data.  In part one I showed how to present detail values on the master using a utility I wrote called the SubPropertyAccessor. In part two I showed how the SubPropertyAccessor works. In this part, I introduce a descendant of DataGridViewColumn that will allow us to show the indexed detail data in line with the master data as additional columns.  I also introduce a descendent of TypeDescriptionProvider that allows the DataGridView to see the SubProperty as a proper property so that the rest of the grid functionality will still work.

Note: The SubPropertyAccessor was called SubAttributeAccessor in parts one and two.

SubPropertyColumn

The whole point of having detail values appear as part of a master record is to show the data in a grid where the detail values would appear in columns next to the other properties in the record.  In order to do this I created a descendant of DataGridViewColumn called SubPropertyColumn.  Actually, SubPropertyColumn is a descendant of DataGridViewTextBoxColumn.  The DataGridViewTextBoxColumn has most of the functionality I want.  I only need to have the data marshaled in and out of the data bound item in a specific way.

The first thing we need is to know where the SubPropertyAccessor is on the data bound item.  This will be a property on the column that can be set at design time in the DataGridView designer.

  72:         /// <summary>
  73:         /// The name of the property that is the indexed property.
  74:         /// Note that for sorting to work this
  75:         /// should be set to 'Properties'
  76:         /// </summary>
  77:         [Category("Data"),
  78:         DefaultValue("Properties"),
  79:         Description("The name of the property that is the indexed property.")]
  80:         public string IndexedPropertyName {
  81:             get { return fIndexedPropertyName; }
  82:             set {
  83:                 fIndexedPropertyName = value;
  84:             }
  85:         }

Now that we know where to find the SubPropertyAccessor, we need to know which property we are looking for.  This is the index that we will pass into the SubPropertyAccessor.  We need another string property.

 114:         /// <summary>
 115:         /// The index to pass to the indexed property to get the value for this column.
 116:         /// </summary>
 117:         [Category("Data"),
 118:         DefaultValue(""),
 119:         Description("The index to pass to the indexed property to get the value for this column.")]
 120:         public string PropertyIndex {
 121:             get {
 122:                 return base.DataPropertyName;
 123:             }
 124:             set {
 125:                 base.DataPropertyName = value;
 126:             }
 127:         }

This property doesn’t have any backing data of its own.  It is using the DataPropertyName of the base DataGridViewTextBoxColumn.  We do this because the base DataGridColumn bases a lot of functionality on the DataPropertyName property.  For example, if this property is blank the base DataGridColumn doesn’t consider the column to be a data bound column.  If we are going to make use of the functionality provided by the base classes, we going to have to make them happy.  This may seem like a kludge, but it’s better than having to reinvent the DataGridColumn.

More...

kick it on DotNetKicks.com

Presenting Detail Values as part of the Master – part 2

By Bill at May 10, 2009 19:59
Filed Under: .Net Programming

This is part two of a three part series on working with master-detail data.  In part one I showed how to present detail values on the master using a utility I wrote called the SubAttributeAccessor.  In this article I will go into detail as to how the SubAttributeAccessor works. In part three, I’ll introduce a descendant of DataGridViewColumn that will allow us to show the indexed detail data in line with the master data as additional columns.

The SubAttributeAccessor is a class that when added to a parent class is able to present a list of children as an indexed property of the parent.  For example, let’s imagine that we had a class called song and that is has among other properties one that is a list of attribute objects.

public class Song {
public List<Attribute> Attributes {get; set;}
}

The attribute class on the other hand has at least two properties, a key and a value.  The key property is a string that will be used to identify the attribute and the value property would hold the actual value of the attribute.  Rather than a certain type or even an object, the value will hold a byte buffer where we can store anything using serialization.
 
public class Attribute {
public string Key {get; set;}
public byte[] Value {get; set;}
}

These two classes form the basis of the imaginary scenario that I used in part one of this article.  A song can have an unlimited number of attributes.  As things stand, we can add an attribute to a song like so:

Song mySong = new Song();
Attribute myAttribute = new Attribute();
myAttribute.Key = "Tempo";
myAttribute.Value = "slow";
mySong.Attributes.Add(myAttribute);

More...

kick it on DotNetKicks.com

Presenting Detail Values as part of the Master – part 1

By Bill at March 10, 2009 02:52
Filed Under: .Net Programming

This is part one of a three part series on working with master-detail data.  In part one I will show how to present detail values as indexed values on the master using a utility I wrote called the SubAttributeAccessor.  In part two I will show how the SubAttributeAccessor works.  In part three I will introduce a descendant class of DataGridViewColumn called the SubPropertyColumn that will allow showing the detail values as part of the master data.

On more than one occasion I’ve been asked to present master-detail data with the detail data being presented as if they were actually additional fields on the master record.

One time there were literally hundreds of detail records, but only one or two of them would be shown at a time. The master data were names and each of them had a never-ending list of scores.  The names were presented in a table and the last few scores were shown as additional columns with the date of the score as the header.

DataClasses Another time, it was a list of songs where each song had the usual attributes like artist, title, album, etc.  However, each song could also have an unlimited list of “user attributes”.  For example, one user might want to add a tempo and a category to his songs.  Another user might want to add a genre, a year and a rating to his songs.

I’m going to use a simpler version of this last example in this article.  The data will be stored in two tables: a songs table and an attributes table.  The songs table will have three fields: a unique code, an artist and a title.  The attributes table will have four fields: a unique code, a song code, a field name and a value.  The song code will directly relate each attribute to exactly one song.  The field name will be used to identify the the data (e.g.: tempo or score).  The value will store binary data of no particular type.  This is where each of the attributes actually will be stored. There is no limit to the number of additional attributes a song can have.

This master-detail setup is also reflected in the objects used to represent this data in code.  There will be a class named Song and a class named Attribute.  The Song class will own an unlimited list of Attribute.  In the attached solution, I used LINQ to SQL to generate the initial classes.

   1: using System.Collections.Generic;
   2: using System.IO;
   3: using System.Runtime.Serialization.Formatters.Binary;
   4: using System.Text;
   5: namespace SubPropertyColumns {
   6:     partial class Song {
   7:  
   8:         private SubAttributeAccessor<Attribute> fProperties = null;
   9:  
  10:         /// <summary>
  11:         /// Used to access the SongPropertiesAccessor class which
  12:         /// has an indexed property to access the additional
  13:         /// properties.
  14:         /// 
  15:         /// since the Song class already has a member called Attributes,
  16:         /// we'll call this Properties
  17:         /// </summary>
  18:         public SubAttributeAccessor<Attribute> Properties {
  19:             get {
  20:  
  21:                 //the the accessor hasn't been created yet,
  22:                 //create it passing a refrerence to this Song
  23:                 if (fProperties == null) {
  24:                     fProperties = new SubAttributeAccessor<Attribute>(this, "Attributes", "FieldName", "Value");
  25:                 }
  26:  
  27:                 return fProperties;
  28:             }
  29:         }
  30:  
  31:         /// <summary>
  32:         /// This is the default indexed property
  33:         /// It is just a wrapper around the Properties property
  34:         /// that allows us to access the functionality without having
  35:         /// to use the Properties name.
  36:         /// </summary>
  37:         /// <param name="index"></param>
  38:         /// <returns></returns>
  39:         public object this[string index] {
  40:             get {
  41:                 return Properties[index];
  42:             }
  43:             set {
  44:                 Properties[index] = value;
  45:             }
  46:         }
  47:  
  48:     }
  49: }

LINQ to SQL generates the classes as partial classes which allows us to add a few properties of our own.

The first thing we add, on line 8, is a private field of type SubAttributeAccessor<T>.  The SubAttributeAccessor does all of the work accessing the list of attributes and presenting them as an indexed property.

Next, on line 18,  we add a public property to access the SubAttributeAccessor.  The property checks to see if SubAttributeAccessor is created and if it isn’t it creates it.  When creating the SubAttributeAccessor, on line 24, we need to add a few important parameters.  First, we need to include the type parameter <Attribute>.  This defines what the type of each of the attributes will be.  In our case, the type of the attribute is Attribute. We also need to add a reference to the object itself and the names of several fields.  We need the reference to the object itself so that the SubAttributeAccessor can have access to the list of attributes.  Then, we need to tell the SubAttributeAccessor the name of the property on the object that is the list of attributes.  In this case it is “Attributes”.  We also need to tell the SubAttributeAccessor the name of the property on each of the attributes that will be used as the index.  In this case it is “FieldName”.  Finally, we need to tell the SubAttributeAccessor the name of the property on each of the attributes that will be the actual value of the attribute.  In this case it is “Value”.  It is important to note that the first property name is the name of a property on the master object – it is the property that returns a list of detail objects.  In this case it is the Attributes property on the Song class itself.  The next two property names are names of properties on the detail object.  In this case, they are the FieldName and Value properties of the Attribute class.  It is also important to note that we named the SubAttributeAccessor property “Properties” because the Song class already has a property named Attributes.

Now that we have a SubAttributeAccessor hooked up to our Song class, we can already write code like:

   1: Song MySong = new Song();
   2: MySong.Properties["Tempo"] = "Medium";
   3: MySong.Properties["Rating"] = 5;

However, we can take this one step further.  On line 39, we add a default indexed property that is just a wrapper around the Properties property.  This allows us to drop the poorly named .Properties and just use the index directly on Song like so:

   1: Song MySong = new Song();
   2: MySong["Tempo"] = "Medium";
   3: MySong["Rating"] = 5;

At this point we’re able to access the detail data as indexed values of the master.  In the next part I’ll show how the SubAttributeAccessor works.  Finally, in the third part, I’ll introduce a descendant of DataGridViewColumn that will allow us to show the indexed detail data in line with the master data as additional columns.

Source Code : SubPropertyColumns.zip (485 KB)

kick it on DotNetKicks.com

Authors

  RSS Feed Bill Fugina

Bill is Director of Technology for Coleman Insights. He enjoys programming, software design, walking, reading, dining out and watching movies, most of which he enjoys even more when he doing them with his wife, Deb, and or his son, Isaac.  Bill and Isaac are working on a video game, but they haven't made very much progress yet.

  RSS Feed Debra Hill

Deb dabbled in Project Management in the Advertising industry for (too) many years. She has happily ditched that and is taking some time to decide what is next career-wise. She enjoys gardening, knitting, sewing and various other crafty things. She also enjoys vegetating on the weekends with the family.

RSS Feed Isaac Hill-Fugina

Isaac has his own blog called Isaac's Place.

Recent Comments

Comment RSS

Bill's Run.GPS Stats

Training Sessions 16
Total Distance 50.17 mi
Total Time 0.11:50:02
Calories 6961 kcal
Average Speed 4.24 mph
Min Altitude -157 ft
Max Altitude 590 ft
Total Ascent 226 ft
Total Descent 236 ft