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
29 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
Hi Steve,
Where and how to call the extension methods. My requirement is like I want to show a column in Insert.aspx but not in other pages.
Please provide in detail.
Hi there, that is what this sample will do, there is no extension methods to call you just replce the metsmodel with this one and then add you attribute and it all just works.
Steve
But the problem is I am not able to pass getVisiblecolumns in CustomMetaModel. In Global.asax I am doing like this -
private static MetaModel s_defaultModel = new CustomMetaModel(); //MetaModel();
and in MetaData class -
public class EmployeeMetadata
{
[HideColumnIn(PageTemplate.List)] [UIHint("TextPassword")]
public object Pass{get; set;}
}
And how to pass one more PageTemplate.Edit in HideColumnIn
Please provide in detail.
Thanks
You don't need to pass anything in in Global.asax you just add the attribute to you buddy/metadata classes.
Try downloading and looking at the sameple.
Steve
Steve,
It is not working.
See my req. is I have to show a column in Insert and Edit Page
but not in List and Listdetails.
I am doing same as what you told but it's not working. May be I am wrong somewhere.
If you have any sample for same then please send to viv_bit at yahoo
Can you send me a sample project that uses Northwind and I will test here?
Steve
P.S. my e-mail address is in the top right of the page.
Hi,
I have implemented the solution you provided to hide the columns in specific template. I am getting an error at global.asax
Error "Could not find an implementation of the query pattern for source type 'System.Collections.Generic.IEnumerable'. 'Where' not found. Are you missing a reference to 'System.Core.dll' or a using directive for 'System.Linq'?
I am facing this error at method
_____________*****________________
public static IEnumerable GetVisibleColumns(IEnumerable columns)
{
var visibleColumns = from c in columns
where IsShown(c)
select c;
return visibleColumns;
}
_________******_____________
I have written this method in global.asax.cs file.
please help me.
Hi Navneet, not sure where you are getting this error, have you downloaded the sample and tested that?
Steve
Hi Steve,
I don't have northwind DB so that's why not able to run the project completely. I am getting an error at line:-
var visibleColumns = from c in columns. columns variable is providing the issue.
you can get it from here
Northwind
Steve
Hi Steve,
This is the post I pointed to from your blog on the older method of hiding columns.
This code is converting to vb.net well, with one unfortunate exception.
In the global.asax file you have this line of code:
Private Shared s_defaultModel As New CustomMetaModel(GetVisibleColumns()
which is not passing any parameters, but the function in global.asax is expecting a collection of columns:
Public Shared Function GetVisibleColumns(columns As IEnumerable(Of MetaColumn)) As IEnumerable(Of MetaColumn)
Dim visibleColumns = From c In columns Where IsHidden(c) Select c
Return visibleColumns
End Function
- so it won't compile and I don't know what to pass into it. I'm thinking the problem has to do with the delegate, but I don't know how to solve it.
Thanks alot!
John (Don't push yourself to answer this in a hurry, I know you are on the mend!)
Hay, did you ever solved that, i have the same problem?
Hi there I have not solved the issue as there has been no one to do the VB conversion for me I'm a C# coder. It need someone doing a lot of VB to do it and I don't know any experienced VB programmers sorry :(
Steve
Hi there, awesome post here BTW.
I stumbled on it after searching for random things to solve my problem here:
http://stackoverflow.com/questions/20497450/asp-net-dynamic-data-full-scaffolding-without-datacontext-objectcontext
I wonder if you could take a look and suggest a workaround there, seeing as you are very knowledgeable on Dynamic Data :)
Cheers,
Juliano
I posted a reply to the thread on StackOverflow
Steve
Post a Comment