There have been several questions on the Dynamic Data Forum saying things like IAutoFieldGenerator does not work with Details, Edit and Insert pages. This is because these page template have now moved to FormView which allows for us to have the nice new Entity Templates and this is cool; but leaves us with the issue of having to do custom column generation in two ways one for form view and one for GridView in List and ListDetails pages. So harking back to this post A Great Buried Sample in Dynamic Data Preview 4 – Dynamic Data Futures long ago in a year far far away![]()
So what I am planning to do is add our own MetaModel that we can pass in a delegate to produce a custom list of columns. I am going to implement the Hide column based on page template (HideColumnInAttribute) for now.
The Custom MetaModel
So first things first lets build out custom MetaModel, the only two classes we will need to implement for our custom MetaModel are:
MetaModel
MetaTable
We need MetaModel because it goes away and get the other classes.
public class CustomMetaModel : MetaModel
{
/// <summary>
/// Delegate to allow custom column generator to be passed in.
/// </summary>
public delegate IEnumerable<MetaColumn> GetVisibleColumns(IEnumerable<MetaColumn> columns);
private GetVisibleColumns _getVisdibleColumns;
public CustomMetaModel() { }
public CustomMetaModel(GetVisibleColumns getVisdibleColumns)
{
_getVisdibleColumns = getVisdibleColumns;
}
protected override MetaTable CreateTable(TableProvider provider)
{
if (_getVisdibleColumns == null)
return new CustomMetaTable(this, provider);
else
return new CustomMetaTable(this, provider, _getVisdibleColumns);
}
}Listing 1 – Custom MetaModel class
So what are we doing here, firstly we have a delegate so we can pass in a methods to do the column generation and we are passing this in through our custom constructor. Then in the only method we are overriding we are returning the CustomMetaTable class, and passing in the delegate if it has been set.
public class CustomMetaTable : MetaTable
{
private CustomMetaModel.GetVisibleColumns _getVisdibleColumns;
/// <summary>
/// Initializes a new instance of the <see cref="CustomMetaTable"/> class.
/// </summary>
/// <param name="metaModel">The entity meta model.</param>
/// <param name="tableProvider">The entity model provider.</param>
public CustomMetaTable(MetaModel metaModel, TableProvider tableProvider) :
base(metaModel, tableProvider) { }
/// <summary>
/// Initializes a new instance of the <see cref="CustomMetaTable"/> class.
/// </summary>
/// <param name="metaModel">The meta model.</param>
/// <param name="tableProvider">The table provider.</param>
/// <param name="getVisibleColumns">Delegate to get the visible columns.</param>
public CustomMetaTable(
MetaModel metaModel,
TableProvider tableProvider,
CustomMetaModel.GetVisibleColumns getVisibleColumns) :
base(metaModel, tableProvider)
{
_getVisdibleColumns = getVisibleColumns;
}
protected override void Initialize()
{
base.Initialize();
}
public override IEnumerable<MetaColumn> GetScaffoldColumns(
DataBoundControlMode mode,
ContainerType containerType)
{
if (_getVisdibleColumns == null)
return base.GetScaffoldColumns(mode, containerType);
else
return _getVisdibleColumns(base.GetScaffoldColumns(mode, containerType));
}
}Listing 2 – Custom MetaTable class
In CustomMetaTable we have the default constructor and a custom constructor again we passing the delegate into the custom constructor. Now in the only method we are overriding we either call the base GetScaffoldColumns or our delegate if is has been set. And that’s it as far as the Meta Classes are concerned.
The Attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HideColumnInAttribute : Attribute
{
public PageTemplate PageTemplate { get; private set; }
public HideColumnInAttribute() { }
public HideColumnInAttribute(PageTemplate lookupTable)
{
PageTemplate = lookupTable;
}
}Listing 3 – HideColumnInAttribute
Listing 3 is the HideColumnIn attribute see Dynamic Data - Hiding Columns in selected PageTemplates for details on this attribute.
public static class ControlExtensionMethods
{
// "~/DynamicData/PageTemplates/List.aspx"
private const String extension = ".aspx";
/// <summary>
/// Gets the page template from the page.
/// </summary>
/// <param name="page">The page.</param>
/// <returns></returns>
public static PageTemplate GetPageTemplate(this Page page)
{
try
{
return (PageTemplate)Enum.Parse(typeof(PageTemplate),
page.RouteData.Values["action"].ToString());
}
catch (ArgumentException)
{
return PageTemplate.Unknown;
}
}
}Listing 4 – GetPageTemplate extension method.
[Flags]
public enum PageTemplate
{
// standard page templates
Details = 0x01,
Edit = 0x02,
Insert = 0x04,
List = 0x08,
ListDetails = 0x10,
// unknown page templates
Unknown = 0xff,
}Listing 5 – PageTemplate enum.
In Listing 4 we have the new improved GetPageTemplate extension method now you don’t have to change each page to inherit DynamicPage you can just call the Page.GetPageTemplate() to find out which page you are on. it required the PageTemplate enum in listing 5.
The Delegate Methods
public static IEnumerable<MetaColumn> GetVisibleColumns(IEnumerable<MetaColumn> columns)
{
var visibleColumns = from c in columns
where IsShown(c)
select c;
return visibleColumns;
}
public static Boolean IsShown(MetaColumn column)
{
// need to get the current page template
var page = (System.Web.UI.Page)System.Web.HttpContext.Current.CurrentHandler;
var pageTemplate = page.GetPageTemplate();
var hideIn = column.GetAttribute<HideColumnInAttribute>();
if (hideIn != null)
return !((hideIn.PageTemplate & pageTemplate) == pageTemplate);
return true;
} Listing 6 – Column generator methods
Now we need to supply our own column generator methods, in Listing 6 we have two methods the first GetVisibleColumns (and the name does not need to be the same as the Delegate) is the one we pass into the MetaModel, and the second IsHidden is where we test to see if the column should be hidden or not.
Adding To Web Application
Now we need to put these into our sample web application.
public class Global : System.Web.HttpApplication
{
private static MetaModel s_defaultModel = new CustomMetaModel(GetVisibleColumns);
public static MetaModel DefaultModel
{
get { return s_defaultModel; }
}
// other code ...
}Listing 7 – Adding to Global.asax
So all we have to do is change the default value in Global.asax from
private static MetaModel s_defaultModel = new MetaModel();
to
private static MetaModel s_defaultModel = new CustomMetaModel(GetVisibleColumns);
Now all column generation throughout the site is handled by the GetVisibleColumns method from Listing 6.
[MetadataType(typeof(OrderMetadata))]
public partial class Order
{
internal partial class OrderMetadata
{
public Object OrderID { get; set; }
public Object CustomerID { get; set; }
public Object EmployeeID { get; set; }
public Object OrderDate { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object RequiredDate { get; set; }
public Object ShippedDate { get; set; }
public Object ShipVia { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object Freight { get; set; }
public Object ShipName { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object ShipAddress { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object ShipCity { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object ShipRegion { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object ShipPostalCode { get; set; }
[HideColumnIn(PageTemplate.List)]
public Object ShipCountry { get; set; }
// Entity Ref
public Object Customer { get; set; }
// Entity Ref
public Object Employee { get; set; }
// Entity Set
public Object Order_Details { get; set; }
// Entity Ref
public Object Shipper { get; set; }
}
}Listing 8 – sample metadata.
Download
Happy Coding![]()
MetaModel 



13 comments:
Hello Steve,
in this solution we lost following:
[HideColumnIn(PageTemplate.Edit, PageTemplate.Insert)]
So, no more multiple restrictions. How can we add more template pages in HideColumIn attribute?!
Functionality we need with Dynamic Data is Hide/Show columns dynamically based on metadata (works perfect with list view but not with edit or insert with your older solution (http://csharpbits.notaclue.net/2008/10/dynamic-data-hiding-columns-in-selected.html)!)
Thanx for help,
Denis
Hi Denis, not sure exactly what you want, just drop me an e-mail and I'll see what I can do.
Steve :D
Is Microsoft trying to make this easier for people, or a lot more complex? Is there going to be any end to abandoning old methods and going in for new, unnecessary ones?
Hi,
I am trying to use your sample with my DD web application , but for some reason he is not using my
MetaDatType.cs...
Do you have an idea why?
Hi there yes the usual reason that metadata is not recognised is namespace issues, my e-mail is at the top of the site e-mail me and I'll see what I can do.
Steve :)
Hey Steve,
Thank you for the great post.
I had the same problem as Dennis Brulic (2 June). I wanted to hide columns from two or more actions (e.g. List and Insert) at the same time. Here is my solution but would appreciate any feedback on improving it:
1. Created a 2nd HideColumn Attribute (e.g. for Insert) from Listing 3
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HideColumnInsertAttribute : Attribute
{
public PageTemplate PageTemplate { get; private set; }
public HideColumnInsertAttribute() { }
public HideColumnInsertAttribute(PageTemplate lookupTable)
{
PageTemplate = lookupTable;
}
}
2. Edited the code from Listing 6. This example handles two actions (List and Insert) but could be edited to handle more:
public static Boolean IsShown(MetaColumn column)
{
// need to get the current page template
var page = (System.Web.UI.Page)System.Web.HttpContext.Current.CurrentHandler;
var pageTemplate = page.GetPageTemplate();
var hideIn = column.GetAttribute<HideColumnInAttribute>();
var hideInsert = column.GetAttribute<HideColumnInsertAttribute>();
if (pageTemplate == PageTemplate.Insert)
{
if (hideInsert != null) return !((hideInsert.PageTemplate & pageTemplate) == pageTemplate);
}
if (pageTemplate == PageTemplate.List)
{
if (hideIn != null)
return !((hideIn.PageTemplate & pageTemplate) == pageTemplate);
}
return true;
}
3. Use in metadata as appropriate (see listing 8):
[DisplayName("Field 1")]
[HideColumnIn (PageTemplate.List)]
[HideColumnInsert (PageTemplate.Insert)]
public string field1 { get; set; }
Hope this helps.
Cheers,
Grant
I'll redo this with some ideas I've had since.
Steve :)
How can this be implemented in ListDetails.aspx template which has got both edit and insert forms in it?
this just works ford any page template, as it works at the meta model level
Steve
In the ListDetails.aspx page, I have both Insert FormView and Edit GridView. I want set of columns to be hidden only in the FormView but must be shown in the gridview. Since the code sample given here works against a page template, I'm not sure how this can be implemented in this case.
I'll send you that sample I have if you email me direct
Steve
Hi Steve,
Thank you so much for sharing this with us, it's great!
Like Sachin, I also need to hide certain columns in the FormView but show them in the GridView inside of the ListDetails.aspx page.
Can you please show us how to implement such functionality?
Thank you in advance.
Hi Christian, send me an e-mail and I will explain a method you could use.
Steve
Post a Comment