- Part 1 – The Read-Only Hierarchical Field Template
- Part 2 – The Edit Hierarchical Field Template
- Part 3 – The Basic Hierarchical Filter
In the past I have done several articles on Cascading field templates and filters, these articles will cover a single cascading hierarchical field template that filters by it's parent fields without having to have those tables related directly related to the columns table.
Figure 1 – Old Cascading Relationships
So in the diagram above to have cascade from Builder->Developer->HouseType on the plot you must have an foreign key relationship with each of the Parent tables on the Plots tables. In the above Requires Plots table we have an FK relationship with Builder, Developer and HouseType to get the cascade to work.
There was however a Cascade filter in the old Dynamic Data Futures VS2008 SP1 RTM project on Codeplex. This did offered the hope of something better, which in this article we will build. First the field template then with a few changes a Filter.
So the aim of this sample if to have a single foreign key field on the table and have the selection cascade over each parent field.
The Attribute
We will need a simple attribute for this to pass in the list of parent columns
Figure 2 – New Cascading Relationships
So we will need to pass the field template a list of parent foreign key navigation properties like this:
Figure 3 – Foreign Key columns
In Figure 3 the normal foreign key column Product already in known to use but we will need to supply the name of the next foreign key navigation property which will be Category.
[UIHint("CascadeHierarchical")] [CascadeHierarchical("Category")] public Product Product { get; set; }
Listing 1 – Attribute applied
Note in Listing 1 we only need to supply the next navigation property and if there was another level up we could supply that also and so on.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] public class CascadeHierarchicalAttribute : Attribute { public String[] Parameters { get; private set; } /// <summary> /// Initializes a new instance of the <see cref="CascadeHierarchicalAttribute"/> class. /// </summary> /// <param name="parameters"> /// The parameters are the parent columns in /// order of hierarchy and must be Foreign Key /// navigation columns for the hierarchy for car manufacturers /// e.g. Manufacturer, VehicleType, Model and Style on the /// Styles table the parameters would be: /// [CascadeHierarchical("Manufacturer", "VehicleType", "Model")] /// as Style would already be known via foreign key navigation. /// </param> public CascadeHierarchicalAttribute(params String[] parameters) { Parameters = parameters; } }
Listing 2 – CascadeHierarchicalAttribute
The only parameter takes the param attribute and takes an array of type String.
Read Only Field Template
So first thing is to create the read only version of the field template, so what will be required, if we look at the standard foreign key field template the only methods we will need to replace is the GetDisplayString() method. In here we will return a string of the form:
Manufacturer > Vehicle Type > Model > Style
this would gives us with cars something like:
“Ford > Car > Mondeo > 5 Door Hatch”
// value to use to separate values in the Hierarchy private const string DISPLAY_SEPERATOR = " > "; protected String GetDisplayString() { // make sure this is a navigation property (ForeignKey column navigation property) if (!(Column is MetaForeignKeyColumn)) throw new InvalidOperationException(String.Format("Column {0} must be a foreign key column navigation property", Column.Name)); // get cascade hierarchical attribute var cascadeHierarchicalAttribute = Column.GetAttribute<CascadeHierarchicalAttribute>(); if (cascadeHierarchicalAttribute == null) throw new InvalidOperationException("Was expecting a CascadeFilterAttribute."); // check for null value if (FieldValue != null) { //get parent table var parentTable = ForeignKeyColumn.ParentTable; // add this foreign key navigation property to the soreted list var fieldValues = new SortedList<int, String>(); fieldValues.Add(0, ForeignKeyColumn.ParentTable.GetDisplayString(FieldValue)); // get current value as an object Object currentValue = FieldValue; // loop through each navigation property to build values for (int i = 0; i < cascadeHierarchicalAttribute.Parameters.Length; i++) { // set name of parent column var parentColumnName = cascadeHierarchicalAttribute.Parameters[i]; // get parent column from name var parentColumn = (MetaForeignKeyColumn)parentTable.GetColumn(parentColumnName); // check for valid column if (parentColumn == null && !(parentColumn is MetaForeignKeyColumn)) throw new InvalidOperationException(String.Format("Invalid parent column {0}", parentColumnName)); // get next value currentValue = currentValue.GetPropertyValue(parentColumnName); // extract current value as string using the parent table's display string var currentValueString = FormatFieldValue(parentColumn.ParentTable.GetDisplayString(currentValue)); // add current value as string to the sorted field values list fieldValues.Add(i + 1, currentValueString); // set next parent table parentTable = parentColumn.ParentTable; } // build display string var displayString = new StringBuilder(); for (int i = cascadeHierarchicalAttribute.Parameters.Length; i >= 0; i--) displayString.Append(fieldValues[i] + DISPLAY_SEPERATOR); // return the built string return FormatFieldValue(displayString.ToString().Substring(0, displayString.ToString().Length - 2)); } else // same as standard foreign key field return FormatFieldValue(ForeignKeyColumn.ParentTable.GetDisplayString(FieldValue)); }
Listing 2 – new GetDisplayString method
now in Details and List pages we will get output like this:
Figure 4 – new output from read only CascadeHierarchical field template.
The only clever piece of code in here is the GetPropValue() method which gets the actual value from the parent column using some basic reflection.
/// <summary> /// Gets the property value. /// </summary> /// <param name="sourceObject">The source object.</param> /// <param name="propertyName">Name of the property.</param> /// <returns>The named properties value</returns> public static Object GetPropertyValue(this Object sourceObject, string propertyName) { if (sourceObject != null) return sourceObject.GetType().GetProperty(propertyName).GetValue(sourceObject, null); else return null; }
Listing 3 - GetPropertyValue
So in the next part we will create the CascadeHierarchical_Edit field template that has multiple dropdown lists populated one for each parent with the furthest grandparent filtering the next and so on to the child.
3 comments:
It works also with LinqtoSql DynamicData?
Good to hear I thought it would but had'nt had the time to test.
Thanks Steve :)
Thanks a lot. This helped me a lot and saved me a lot of time.
One small bug, when there are special characters, they are being html-encoded twice, so their html entities are shown to the user.
Post a Comment