Question

In a scaffolded page in ASP.NET Dynamic Data, if the entity has a foreign key field, and the value you seek is not in the the primary key table, i.e. is not in the drop-down, you have to abandon your edits to the entity, add the sought foreign key value to its table, and return to your original entity.

How could I go about adding a 'New' link/button to the foreign key field template, that would open a new window (make a Panel visible) where you can add the sought value, and then refresh the drop-down?

Was it helpful?

Solution

You mean like in the django admin ui ;). I'm currently trying to implement that feature, i'll post the code here if I get it to work.

EDIT:

Ok, I got that to work, complete django style... It's kinda long to explain but simple in fact.

Files to create:

A admin_popup.master to have a nice popup page (copy the admin.master without the header).

A popup_Insert.aspx with admin_popup.master as master. (copy the Insert.aspx)

Modifications

To your admin.master.cs: add this:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType(), "refresh_fks", @"
    var fk_dropdown_id;
    function refresh() {
        __doPostBack(fk_dropdown_id,'refresh');
    };", true);
}

In your admin_popup.master, add these attributes to the body tag (it's used to resize the poup)

<body style="display: inline-block;" onload="javascript:resizeWindow();">

In your admin_popup.master.cs

protected override void  OnInit(EventArgs e)
{
    base.OnInit(e);

    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType(), "refresh_fks", @"
    var fk_dropdown_id;
    function refresh() {
        __doPostBack(fk_dropdown_id,'refresh');
    };", true);

    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType(), "resize_window", @"function resizeWindow() {
        window.resizeTo(document.body.clientWidth + 20, document.body.clientHeight + 40);
        window.innerHeight = document.body.clientHeight + 5;
        window.innerWidth = document.body.clientWidth;
    }", true);

    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType(), "update_parent", @"function updateParent() {
        window.opener.refresh();
    }", true);        
}

In popup_Insert.aspx.cs, replace these two functions:

protected void DetailsView1_ItemCommand(object sender, DetailsViewCommandEventArgs e) {
    if (e.CommandName == DataControlCommands.CancelCommandName)
        System.Web.UI.ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "Close_Window", "self.close();", true); 
}

protected void DetailsView1_ItemInserted(object sender, DetailsViewInsertedEventArgs e) {
    if (e.Exception == null || e.ExceptionHandled) {
        System.Web.UI.ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "Close_Window", "window.opener.refresh(); self.close();", true); 
    }
}

In ForeignKey_Edit.ascx, add a LinkButton (ID=LinkButton1) and in ForeignKey_Edit.ascx.cs, replace that function

protected void Page_Load(object sender, EventArgs e) {
    if (DropDownList1.Items.Count == 0)
    {
        if (!Column.IsRequired) {
            DropDownList1.Items.Add(new ListItem("[Not Set]", ""));
        }

        PopulateListControl(DropDownList1);
        LinkButton1.OnClientClick = @"javascript:fk_dropdown_id = '{0}';window.open('{1}', '{2}', config='{3}');return false;".FormatWith(
            DropDownList1.ClientID,
            ForeignKeyColumn.ParentTable.GetPopupActionPath(PageAction.Insert),
            "fk_popup_" + ForeignKeyColumn.ParentTable.Name, "height=400,width=600,toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,directories=no,status=no");
    }
    if (Request["__eventargument"] == "refresh")
    {
        DropDownList1.Items.Clear();
        if (!Column.IsRequired)
        {
            DropDownList1.Items.Add(new ListItem("[Not Set]", ""));
        }

        PopulateListControl(DropDownList1);
        DropDownList1.SelectedIndex = DropDownList1.Items.Count - 1;
    }
}

And finally the two extentions functions I use (put it where you want to):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Web.DynamicData;
using System.Web.UI;

public static class Utils
{
    [DebuggerStepThrough]
    public static string FormatWith(this string s, params object[] args)
    {
        return string.Format(s, args);
    }

    public static string GetPopupActionPath(this MetaTable mt, string action)
    {
        return new Control().ResolveUrl("~/{0}/popup_{1}.aspx".FormatWith(mt.Name, action));
    }
}

In your global.asax, register the new route by changing that line:

Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert|popup_Insert" }),

Ok I hope i didn't forgot anything... It certainly could be improved, but it works. Ok I hope some people will fint that useful, it makes ASP.NET Dynamic Data a lot more better ;). I'm goind to take a look at many-to-many relationships now.

OTHER TIPS

ATTN: VB.net USERS

If you have been like me and fuming at the mouth over how convoluted and a mess Dynamic Data is, this is for YOU! It has taken me over 100 hours just to figure out the basics of DD (even though I am very versed in other database programming techiniques).

Guillaume's solution is much easier than Naughton's for us. After 15+ hours of trying to translate Naughton's I tried to translate Guillaume's code which took me only 2 hours to get to work. Here it is in the same order. (Note: cs extensions of course translate to vb extensions)

  1. Ignore the admin.master.cs directions.

  2. Do the admin_popup.master code. If you are still in VS 2008 or older you'll get a CSS error on the inline block quote. Ignore the error.

  3. MASTER FILE OnInit Sub:

    Protected Overrides Sub OnInit(ByVal e As EventArgs)
    MyBase.OnInit(e)
    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType, "refresh_fks", "     var fk_dropdown_id;     function refresh() {         __doPostBack(fk_dropdown_id,'refresh');     };", True)
    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType, "resize_window", "function resizeWindow() {         window.resizeTo(document.body.clientWidth + 120, document.body.clientHeight + 120);         window.innerHeight = document.body.clientHeight + 5;         window.innerWidth = document.body.clientWidth;     }", True)
    System.Web.UI.ScriptManager.RegisterClientScriptBlock(Page, Page.GetType, "update_parent", "function updateParent() {        window.opener.location.reload(true);     }", True)
    

    End Sub

  4. Replace the events in (popup_Insert.aspx.vb) under your custom pages with this:

    Protected Sub DetailsView1_ItemCommand(ByVal sender As Object, ByVal e As DetailsViewCommandEventArgs)
    If e.CommandName = DataControlCommands.CancelCommandName Then
        System.Web.UI.ScriptManager.RegisterClientScriptBlock(Me, Me.GetType, "Close_Window", "self.close();", True)
    End If
    

    End Sub Protected Sub DetailsView1_ItemInserted(ByVal sender As Object, ByVal e As DetailsViewInsertedEventArgs) If e.Exception Is Nothing OrElse e.ExceptionHandled Then System.Web.UI.ScriptManager.RegisterClientScriptBlock(Me, Me.GetType, "Close_Window", "window.opener.location.reload(true); self.close();", True) End If End Sub

  5. Create a custom FieldTemplate off the ForeignKey_Edit and name it ForeignLinkKey_Edit.ascx. After the dropdownlist1 control, add two spaces ( ) and then create the asp:LinkButton as he stated. Put text like "Add __" or something like that. Replace the Page Load function with this:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
    If DropDownList1.Items.Count = 0 Then
        If Not Column.IsRequired Then
            DropDownList1.Items.Add(New ListItem("[Not Set]", ""))
        End If
        PopulateListControl(DropDownList1)
    
        LinkButton1.OnClientClick = "javascript:fk_dropdown_id = '" & DropDownList1.ClientID & _
    

    "';window.open('" & ForeignKeyColumn.ParentTable.GetActionPath("Insert").Replace("Insert.aspx", "popup_Insert.aspx") & _ "', '" & "fk_popup_" & ForeignKeyColumn.ParentTable.Name & "', config='" & _ "height=400,width=400,toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,directories=no,status=no" & "');return false;" End If If Request("__eventargument") = "refresh" Then DropDownList1.Items.Clear() If Not Column.IsRequired Then DropDownList1.Items.Add(New ListItem("[Not Set]", "")) End If PopulateListControl(DropDownList1) DropDownList1.SelectedIndex = DropDownList1.Items.Count - 1 End If End Sub

  6. Ignore the extensions functions.

  7. Do the updated routing suggested. Note: If you are using custom routes, you'll have to tinker with it until the routing is correct.

Or I have just created two server controls and a blog post here: A Popup Insert control for Dynamic Data that does pretty much the same but encapsulates the popup functionlity in the server controls passing a value bak from the popup windows to main windows and popup button.

Any new features in Dynamic Data in the VS2010 RC? Will we still have to resort to hacks such as these for simple Master-Details in Dynamic Data under this RC?

Looking forward to some blog posts on DD under VS2010 RC...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top