Using User Controls as Page Templates in Dynamic Data

Dynamic Data has the concept of Page Templates, which are pages that live under ~/DynamicData/PageTemplates and are used by default for all tables.  Recently a user on the forum asked whether they could use User Controls instead of Pages for those templates.

To me, this means potentially two distinct scenarios, and I tried to address both in this post:

  1. Using routing: in this scenario, you still want all your requests to go through the routing engine, but have the user control templates somehow get used.  The URLs here would still  look identical to what they are in a default Dynamic Data app, e.g.  /app/Products/List.aspx (or whatever you set them to be in your routes).

  2. No routing: in this scenario, you want the URL to go directly to a specific .aspx page, and then have that page do the right thing to use Dynamic Data through the User Control templates.

Creating the User Controls

First, let’s look at what will be the same for both cases.  Under ~/DynamicData/PageTemplates, I deleted all the aspx files (to prove that they’re not used), and instead created matching ascx files (i.e. User Controls).  Those user controls contain essentially the same things that were in the pages, minus all the ‘outer’ stuff (e.g. things that relate to the master page).

Making it work using routing

Now let’s look specifically at case #1 above, where we want to use routing.  First, we need to make a small change to the route to use a custom route handler:

routes.Add(new DynamicDataRoute("{table}/{action}.aspx") {
Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
Model = model,
RouteHandler = new CustomDynamicDataRouteHandler()

Note how we set RouteHandler to our own custom handler type.  Now let see what this type looks like:

public class CustomDynamicDataRouteHandler : DynamicDataRouteHandler {
public override IHttpHandler CreateHandler(DynamicDataRoute route, MetaTable table, string action) {
// Always instantiate the same page. The page itself has the logic to load the right user control
return (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath("~/RoutedTestPage.aspx", typeof(Page));

It’s basically a trivial handler which always instantiates the same page!  That may look strange, but the idea is that all the relevant information is carried by the route data, which that page can then make use of.  Now let’s see what we’re doing in this one page.  It’s itself pretty trivial, with just a bit of logic in its Page_Init:

protected void Page_Init(object sender, EventArgs e) {
// Get table and action from the route data
MetaTable table = DynamicDataRouteHandler.GetRequestMetaTable(Context);
var requestContext = DynamicDataRouteHandler.GetRequestContext(Context);
string action = requestContext.RouteData.GetRequiredString("action");

// Load the proper user control for the table/action
string ucVirtualPath = table.GetScaffoldPageVirtualPath(action);

The first 3 lines show you what it takes to retrieve the MetaTable and action from the route date.  Then the next couple lines load the right user control for the situation.  GetScaffoldPageVirtualPath is a simple helper method which has logic to locate the proper ascx:

public static string GetScaffoldPageVirtualPath(this MetaTable table, string viewName) {
string pathPattern = "{0}PageTemplates/{1}.ascx";
return String.Format(pathPattern, table.Model.DynamicDataFolderVirtualPath, viewName);

And that’s basically it for the routed case!

Making it work without routing

Now let’s look at the non-routed case.  Here, the routes are not involved, so the URL goes directly to a page.  That page is similar to the one above, but with some key differences.  Here is what it does in it Page_Init:

protected void Page_Init(object sender, EventArgs e) {
// Get table and action from query string
string tableName = Request.QueryString["table"];
string action = Request.QueryString["action"];

// Get the MetaTable and set it in the dynamic data route handler
MetaTable table = MetaModel.Default.GetTable(tableName);
DynamicDataRouteHandler.SetRequestMetaTable(Context, table);

// Load the proper user control for the table/action
string ucVirtualPath = table.GetScaffoldPageVirtualPath(action);

The key difference  is that it can’t rely on route data being available, so it needs to get the table and action information from  somewhere else.  You can come up with arbitrary logic for this, but the most obvious way is to just use the query string (e.g. ?table=Products&action=List).

Then  it does sort of the reverse of the case above, and sets the MetaTable into the route handler.  Even though routing is not used, Dynamic Data tries to get the MetaTable from DynamicDataRouteHandler, so having it there allows many things to just work.

And finally, it loads the user control using the exact same steps as above.  And that’s that!

The code is attached below.

Comments (9)

  1. Track back from

  2. Mark Kamoski says:

    This is super slick. I really appreciate it. As you probably know, your solution also works on URLs to load Edit ASCX control, such as… <asp:hyperlink id="HyperLink3" runat="server" navigateurl="~/NonRoutedTestPage01.aspx?table=Products&action=Edit&ProductID=2">Test page that doesn’t use routing, to load a form for Edit.</asp:hyperlink> …so that is really great. Thank you. — Mark Kamoski

  3. Please post corrections/new submissions to the Dynamic Data Forum . Put FAQ Submission/Correction in

  4. George says:

    Is it possible to use multiple routing handlers is a nested form (as in composite or decorator patterns) so one routing handler serves as the input for the other (in a similar way streams are used which helps in handling security, etc.)?

    If not, what is the bast way to achieve this?

  5. Maya says:

    That is exactly what I was looking for. Great :-).

  6. LEroy says:

    Somehow after converting it to vb… i seem to be getting an error:

    You need to have a DynamicManager control on the page and register your data control with it in order to use a DynamicQueryStringParameter.

    Please advice.

  7. Lim says:

    For Vb, You need to move the DynamicManager.RegisterControl method, and Gridview ColumnGenerator from the Page_Init to Page_Load.

  8. LEroy says:

    Thanks Lim.

    However the inline select and update commands are not working.

    On Select: – Columns all disappear

    On Update – Data is not updated.

    Please assist.

  9. Sanjay says:

    This is a brilliant article – ive been searching the web for a while before i found it.

Skip to main content