Wednesday, 9 July 2008

Part 1 - Standard Custom Page Dynamic Data and Custom Pages

As far as I can see there are three (oops! four then) types of Custom Page:

  1. Custom Pages Part 1 - Standard Custom Page based on an existing PageTemplate and customised in the DynamicData\CustomPages folder.
  2. Custom Pages Part 2 - A completely Custom Page again in the DynamicData\CustomPages folder.
  3. Custom Pages Part 3 - Standard ASP.Net Page with Dynamic Data features added to take advantage of the FieldTemplates.
  4. Custom Pages Part 4 - A DetailsView and a GridView using Validation Groups
  5. Custom Pages Part 5 - I18N? Internationalisation Custom Page  

Creating a Standard Custom Page

To customise a standard PageTemplate all you do is create a folder with the name of the entity collection (e.g. Order entity would have the folder Orders).

 Copy PageTemplate to CustomPages folder

Figure 1 – Copy PageTemplate to CustomPages folder

All you do then is copy the PageTemplate you want to modify to the CustomPages sub-folder.

 Copying Details.aspx to the CustomPages <TableName>folder

Figure 2 - Copying Details.aspx to the CustomPages <TableName>folder

Customising the Standard Custom Page

For out first step we will wire up the LinqDataSource to the Orders Table/Entity set, this will cause the column of the DetailsView to be updated see below.

<Fields>
    <asp:BoundField DataField="OrderID" HeaderText="OrderID" InsertVisible="False" ReadOnly="True" SortExpression="OrderID" />
    <asp:BoundField DataField="CustomerID" HeaderText="CustomerID" SortExpression="CustomerID" />
    <asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID" SortExpression="EmployeeID" />
    ...
<asp:BoundField DataField="ShipPostalCode" HeaderText="ShipPostalCode" SortExpression="ShipPostalCode" /> <asp:BoundField DataField="ShipCountry" HeaderText="ShipCountry" SortExpression="ShipCountry" /> </Fields>

Listing 1 – Columns collection from the Details.aspx

Now to edit his to take advantage of DynamicData:

<Fields>
    <asp:DynamicField DataField="OrderID" />
    <asp:DynamicField DataField="Customer" />
    <asp:DynamicField DataField="Employee" />
    ...
<asp:DynamicField DataField="ShipPostalCode" /> <asp:DynamicField DataField="ShipCountry" /> </Fields>

Listing 2 – Updated columns collection from the Details.aspx

Here what I did was search and replace BoundField with DynamicField and remove all other unneeded properties (I used regular expressions to remove the extra fields here is the expression HeaderText=\".*\" SortExpression=\".*\" and replaced with an empty string), (these should come from the metadata from the column) then rearrange the order and replace the columns I didn’t want displaying with columns that I did (i.e. ShipVia with Shipper, CustomerID with Customer, etc) you can find the names of the EntitySet and EntityRef in the designer.cs/designer.vb file under the dbml file of your LinqToSql classes just drop the ‘_’ underscore at the beginning (you will see the actual property for each later on but this bit at the beginning always gets me what I want).

private EntitySet<Order_Detail> _Order_Details;
private EntityRef<Customer> _Customer;
private EntityRef<Employee> _Employee;
private EntityRef<Shipper> _Shipper;

Listing 3 – from Northwind.designer.cs file

If we run the DynamicData website now we will get what seems similar to the default Details.aspx page, but missing the Edit and Delete links.

Now whart we need is to add Edit and Delete functionality again.

<asp:TemplateField>
    <ItemTemplate>
        <asp:HyperLink ID="EditHyperLink" runat="server"
            NavigateUrl='<%# table.GetActionPath(PageAction.Edit, GetDataItem()) %>'
            Text="Edit" />
        <asp:LinkButton ID="DeleteLinkButton" runat="server" CommandName="Delete" CausesValidation="false"
            OnClientClick='return confirm("Are you sure you want to delete this item?");'
            Text="Delete" />
    </ItemTemplate>
</asp:TemplateField>

Listing 4 – copied from the original List.aspx

Here I’ve copied and pasted the TemplateField from the original List.aspx page to get the same functionality for Edit, Details and Delete.

Adding Master Details page funtionality

Now we add a GridView and LinqDataSource to the page for the Order_Details, (which all I did was copy the one from the List.aspx PageTemplate and then edit it).

<asp:GridView ID="GridView1" runat="server" DataSourceID="GridDataSource"
    AllowPaging="True" AllowSorting="True" CssClass="gridview">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:HyperLink ID="EditHyperLink" runat="server"
                    NavigateUrl='<%# table.GetActionPath(PageAction.Edit, GetDataItem()) %>'
                Text="Edit" />&nbsp;<asp:LinkButton ID="DeleteLinkButton" runat="server" CommandName="Delete"
                    CausesValidation="false" Text="Delete"
                    OnClientClick='return confirm("Are you sure you want to delete this item?");'
                />&nbsp;<asp:HyperLink ID="DetailsHyperLink" runat="server"
                    NavigateUrl='<%# table.GetActionPath(PageAction.Details, GetDataItem()) %>'
                    Text="Details" />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>

    <PagerStyle CssClass="footer"/>        
    <PagerTemplate>
        <asp:GridViewPager runat="server" />
    </PagerTemplate>
    <EmptyDataTemplate>
        There are currently no items in this table.
    </EmptyDataTemplate>
</asp:GridView>

<asp:LinqDataSource ID="GridDataSource" runat="server" EnableDelete="true">
    <WhereParameters>
        <asp:DynamicControlParameter ControlID="FilterRepeater" />
    </WhereParameters>
</asp:LinqDataSource>
<br />

Listing 5 – Extra GridView and LinqDataSource which I copied from the List.aspx PageTemplate

Linking the two GridViews together is done in the customising of the LinqDataSource switch to design view of the Details.aspx page and find the new LinqDataSource and click on the tasks button:

LinqDataSource Tasks

Figure 3 – Configuring the LinqDataSource

Follow the wizard through and select Order_Details as the source table and then click on the Where button. Then configure the where expression to limit the list to the record in the DetailsView.

Configure the where expression

Figure 4 - Configure the where expression

Say yes to updating the GridViews column when asked. Then edit the GridView’s columns collection the same way we did the DetailsView’s fileds collection, edit the columns so that any EntitySets/EntityRefs are shown instead of their foreign keys.

Now if we run the app and choose Orders and then Details we will see something like this:

Order Details page with Order_Details at the bottom

Figure 5 – Output from our custom Details.aspx page

Note: I’ve left the OrderID in both the DetailsView and the GridView just to show that we are getting the correct records, you can remove them once you are happy that that is happening.

We need to ad a new class level variable:

protected MetaTable table1;

Then the Page_Init must updated:

protected void Page_Init(object sender, EventArgs e)
{
    DynamicDataManager1.RegisterControl(DetailsView1, true);
    DynamicDataManager1.RegisterControl(GridView1, false);
}

Listing 6 – Updated Page_Init

Also in the Page_Load event handler add the following line right after one for that default table so that its metadata for the second table can be referenced:

table1 = GridDataSource.GetTable();

and then in the NavigateUrl declaration change table to table1

Note: Creating table1 and assigning it through GridDataSource.GetTable() give us a reference to the GridView’s table. Which then allows us to get the correct URL from table1.GetActionPath(PageAction.Edit, GetDataItem())

We need to put the edit capability back into the GridView to do this all we have to do is copy them from the List.aspx PageTemplate and then add them to the beginning of the Columns collection:

<asp:TemplateField>
    <ItemTemplate>
        <asp:HyperLink ID="EditHyperLink" runat="server"
            NavigateUrl='<%# table1.GetActionPath(PageAction.Edit, GetDataItem()) %>'
        Text="Edit" />&nbsp;<asp:LinkButton ID="DeleteLinkButton" runat="server" CommandName="Delete"
            CausesValidation="false" Text="Delete"
            OnClientClick='return confirm("Are you sure you want to delete this item?");'
        />&nbsp;<asp:HyperLink ID="DetailsHyperLink" runat="server"
            NavigateUrl='<%# table1.GetActionPath(PageAction.Details, GetDataItem()) %>'
            Text="Details" />
    </ItemTemplate>
</asp:TemplateField>

Listing 6 – Adding the Edit, Delete and Details functionality

As you can see from running the app again the Edit, Delete and Details functionality is back.

With Edit, Delete and Details functionality

Figure 6 – With Edit, Delete and Details functionality

And we finish off by adding paging to the DetailsView

<asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="DetailsDataSource"
    OnItemDeleted="DetailsView1_ItemDeleted" CssClass="detailstable" FieldHeaderStyle-CssClass="bold"
    AutoGenerateRows="False" DataKeyNames="OrderID" AllowPaging="True">
    <PagerSettings Mode="NextPreviousFirstLast" />

Listing 7 – Adding paging to the DetailsView

I think that will do for this part of the series, we can add other features to the page in the next edition.

28 comments:

Matt said...

Thanks :-)

Matt said...

Thanks :-)

Anonymous said...

Stephen,
i did it the same way and i get a error message.

error CS0103: The name 'table1' does not exist in the current context.

its comming up when dynamic data tries to open the list.aspx page template.

Whats wrong?

Steve said...

It sounds like an error with your project youve not created a Linq to SQL DD website and then added an ADO.Net model or the otherway around?

Steve :D
p.s. try posting your problem here http://forums.asp.net/1145.aspx and give the whole error page as it helps a lot with diagnosing issues.

Mel said...

Hi Steve,

I'm having a problem when I copy a (listdetails) template into a custom page folder (as per your instructions) The errors say there's ambiguity because it's defined more than once... I tried changing the partial class name which works but becuase it is autogenerated it always reverts back.

Any help would be appreciated.

Cheers.
Mel

Steve said...

Hi Mel, can send me the the error as in copy an paste the error into an e-mail and I'll have a look.

Steve
steve@notaclue.net

Delly said...

Steve, what was the answer to Mels problem above, I get the same thing. Could it be because I am referencing a DataContext in an extrernal dll/namespace?

Thanks
Tom

Steve said...

See this thread http://forums.asp.net/t/1381287.aspx on the forum

Steve :D

niner said...

I've followed this example and I've tried to add another datagrid to show the client data, I've added ClientID to DetailsView DataKeyNames, and I've added the where clause to the LinqDataSource of this new gridview:

asp:ControlParameter ControlID="DetailsView1" Name="CustomerID" PropertyName="SelectedValue" Type="String"



I've also added the ClientID column to detailsview, but it doesn't work.
It seems as if one could only bind the master details gridview to the master detailsview by using it's primary key.

Any ideas on how to solve this?

Steve said...

Hi Niner, if you have multiple child grids to show have a look at this article here •An Advanced FieldTemplate with a GridView this basically uses a field template that is a gridview and allows you to show children relationships in a gridview. ans Also see A variation of Part 1 with the Details and SubGrid in Tabs

Hope this is some use.

Steve :D

niner said...

Would it be possible to have 'insert' functionality in the gridview?

Steve said...

Hi Niner, have a look at Dynamic/Templated Grid with Insert (Using ListView)

Steve :D

Anonymous said...

How can I make a custompage which will only show a few fields from five different tables?

Steve said...

I would create a view (if you don't need it to be updateable)

You can make a view updateable just give it a primary key and setup some SPROCS to do the Insert, Update and Delete.

Steve :D

billgrowjr said...

Steve,
As has already been stated. You are a genius. Dynamic data websites have a lot of potential for me but I am struggling with some of the fundamentals of custom pages. Specifically right now, when I see that a list.aspx generated for a table provides a hyperlink reference "View tables" for the foreign key attributes and then automaically provide page parameters in the hyperlink ?ID=[foreigh key value] that posts the foreign p key back to the table that uses it as a primary key, I am having a hard time understanding

1. why/how these additional entries are generated in the gridview
2. why the foreign key is automatically used when passed to the related table and how the related table control knows how to use that foreign key to filter the displayed results.

Steve said...

Hi billgrowjr,

1. there are generated byt the foreign key relationship column the Linq to SQL and Entity Framewoke creates, the actual foreign key columns are always hidden,

2. the foreign key values are auto matically picked up by the relavent filter and the filter applied.

What version of DD are you using?

Steve :)

Jason said...

Hey there, I am just following along on your example there and I have a few questions:

I have a table (PurchaseOrders) and it has a one to many relationship with another table (LineItems)

I am using the scaffolding TRUE option, so visual studio generates all of the pages for me.

I am trying to get the PurchaseOrder Insert page to allow me to create a new PurchseOrder as well as add multiple LineItems to that PurchaseOrder and then insert into the database (a SQL server 2008 backend) at once.

Can I customise one of the dynamic pages to do this?

Steve said...

Yes you could, and I have a field template that does just that, see it here: http://csharpbits.notaclue.net/2008/09/dynamic-data-and-field-templates.html

Steve

Jason said...

Great. Thanks, I will read over the article now, cheers.

Jason said...

Will there be many differences between the dynamic-data-field template version you have listed, and the visual studio 2010 dynamic linq to sql asp.net 4.0 version I am using?

Steve said...

Yes there are a number of differences between DD1 and DD4 but none are killers just look at the Page_Init in both and you will quickly see them.

Steve

Anonymous said...

For all of those who copy a page from the pageTemplate folder to the CustomPages folder and get the table already exist some place else error, if you make the CustomPage folder plural and then attach that name to the end of the namespace, that should make everything works as the documentation describes. It worked for me after much frustration and back and forth over the months.

Thanks

Steve said...

A good point but that is specific to Web Application Project not Website project.

Steve

bravemav said...

Hi Steve thanks for the post!

One question. How do I create a custom page for derived types?

I am able to create a custom page for my base type, but none of the pages for my derived types work. They all go to the standard page templates.

I have made sure that the folder names match the table names for the derived types to no avail.

Thanks

bravemav said...

Hi Steve, thanks for the post!

I am able to create custom pages for a base type, but not for any of its derived types. I have made sure that the custom page folders match the names of the derived types database tables to no avail.

Is this a limitation of Dynamic Data?

Thank you.

Steve said...

Not sure what you mean by derived type, if you want you can just e-mail me directly, my address is in the top right of this site.

Steve

Faizan said...

Hi Steve, Good Day!

I have been trying with your above post but due to difference of detailsview to formsview in DD4, I have not been able to progress. May you please suggest some way of doing the parent child page functionality (as your above post) in DD4. Better if using tabs for multi child tables :). THANKS

Steve said...

Hi Faizan, it would be real hard work getting that to work in DD now :) I have a new project coming out soon that has ALL my bits included in a new shiny Project Template you will be able to get it all then.

Steve