Asp – New button for Foreign Key fields in Dynamic Data

asp.netdynamic-data

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?

Best Answer

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.

Related Topic