jQuery Dialog-Postback but UpdatePanel doesn't

2019-01-09 13:18发布

i want to show a jQuery UI Dialog from Codebehind and need to refresh it after postbacks.

The dialog is a control to filter and find data. So the user selects from DropDownLists and enters text in TextBoxes, clicks a "Apply-Button", an asynchronous postback happens, the data gets filtered according to the user's selection and the result will be shown in a GridView. Therefore i need to update the UpdatePanel around the GridView.

The asynchronous postback works with help from these links:

(Basically the dlg.parent().appendTo(jQuery("form:first"));-Solution)

Problem: I cannot update the UpdatePanel neither with UpdateMode="Always" nor manually from codebehind via UpdatePanel.Update(). I assume that it has something to do with the Dialog not being inside of the UpdatePanel or something similar. Hopefully somebody can help me along.

Some source:

function createChargeFilterDialog() {
    //setup dialog
    $('#Dialog_ChargeFilter').dialog({
        modal: true,
        resizable: false,
        autoOpen: false,
        draggable: true,
        hide: "Drop",
        width: 850,
        height: 600,
        position: "center",
        title: "Charge-Filter",
        buttons: {
            "Close": function () {
                $(this).dialog("close");
            }
        },
        open: function (type, data) {
            $(this).parent().appendTo(jQuery("form:first"))
        },
        close: function (type, data) {
        }
    });
}

It gets called from codebehind when BtnShowDialog(outside the jQuery-Dialog) gets clicked via

AjaxControlToolkit.ToolkitScriptManager.RegisterStartupScript _
            (Me.Page, GetType(Page), "showChargeFilterDialog", "createChargeFilterDialog();$('#Dialog_ChargeFilter').dialog('open');", True)

Update: i've also noticed a problem in the postback-values. All TextBoxes if empty or not have a comma appended. This indicates that controls are rendered multiple times according to: http://www.componentart.com/community/forums/t/60999.aspx

I'm sure that both issues are related. The whole dialog with all its controls will be recreated in every asynchronous postback, hence all control names exist multiple times in the DOM(causing the ViewState comma-appending issue). The controls are only visible in FireBug/IE Deveoper Toolbar and not in HTML-Source, hence i assume that jQuery causes these problems. How can i dispose the dialog or how can i prevent the recreation(check if already exists) of the dialog? Is this because the dialog is inside an UpdatePanel or because it's moved(via Javascript) outside the UpdatePanel?

Destroying the dialog before async postback doesn't solve the problem because the dialog will simply disappear:

<asp:Button ID="BtnApplyFilter" OnClientClick="$('#Dialog_ChargeFilter').dialog('destroy');" ... />

Your help is greatly appreciated.


Solution: i ended with using the ModalPopupExtender from the AjaxControlToolkit. After some small issues its working like a charm with asynchronous postbacks(don't forget to call MPE.Show() in every code-behind function if you want the popup to stay visible). I could add more code if anybody is interested.

4条回答
家丑人穷心不美
2楼-- · 2019-01-09 13:28

I think Jquery and script manager/update panel when generate and parse script created confilct it , and you should handle event in Trigger of update panel correctly in order to use this in code behind method :

       UpdatePanel2.Update();

so i have this problem and can solved it by below code (it's my sample code) :

   <!--------- show for dialog --->
<div id="dialog" style="direction: rtl;">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <asp:UpdatePanel ID="UpdatePanel2" runat="server" RenderMode="Block" UpdateMode="Conditional" EnableViewState="True" ChildrenAsTriggers="True">
        <ContentTemplate>
            <div>
                <table>
                    <tr>
                        <td>
                            <asp:ListBox ID="lstSLA" runat="server" SelectionMode="Single" Width="250px" Height="350px">
                            </asp:ListBox>
                        </td>
                        <td valign="middle">
                            <asp:Button ID="BtnUpMove" runat="server" Text=" top" OnClick="BtnUpMove_Click" />
                            <div>
                                <br />
                            </div>
                            <asp:Button ID="BtnDownMove" runat="server" Text="down" OnClick="BtnDownMove_Click" />
                        </td>
                    </tr>
                    <tr>
                        <td colspan="2" align="center">
                            <asp:Button ID="btnSavePeriority" runat="server" Text="Save" OnClick="btnSavePeriority_Click" />
                        </td>
                        <td>
                        </td>
                    </tr>
                </table>
            </div>
        </ContentTemplate>
        <Triggers>
            <asp:PostBackTrigger ControlID="btnSavePeriority" />
            <asp:AsyncPostBackTrigger ControlID="BtnUpMove" EventName="Click" />
            <asp:AsyncPostBackTrigger ControlID="BtnDownMove" EventName="Click" />
        </Triggers>
    </asp:UpdatePanel>
</div>

and in c# code : protected void BtnUpMove_Click(object sender, EventArgs e) { int SelectedIndex = lstSLA.SelectedIndex;

        if (SelectedIndex == -1) // nothing selected
        {
            UpdatePanel2.Update();
            return;
        }
        if (SelectedIndex == 0) // already at top of list  
        {
            UpdatePanel2.Update();
            return;
        }

        ListItem Temp;
        Temp = lstSLA.SelectedItem;

        lstSLA.Items.Remove(lstSLA.SelectedItem);
        lstSLA.Items.Insert(SelectedIndex - 1, Temp);
        UpdatePanel2.Update();
        lstSLA.SelectedIndex = -1;
    }
    protected void BtnDownMove_Click(object sender, EventArgs e)
    {
        int SelectedIndex = lstSLA.SelectedIndex;

        if (SelectedIndex == -1)  // nothing selected  
        {
            UpdatePanel2.Update();
            return;
        }
        if (SelectedIndex == lstSLA.Items.Count - 1)  // already at top of list            
        {
            UpdatePanel2.Update();
            return;
        }

        ListItem Temp;
        Temp = lstSLA.SelectedItem;

        lstSLA.Items.Remove(lstSLA.SelectedItem);
        lstSLA.Items.Insert(SelectedIndex + 1, Temp);
        UpdatePanel2.Update();
        lstSLA.SelectedIndex = -1;

    }
    protected void btnSavePeriority_Click(object sender, EventArgs e)
    {
        if (lstSLA.Items.Count == 0) return;
        try
        {
            var db = DatabaseHelper.GetITILDataAccess();
            ServiceLevel srvlvl = new ServiceLevel();
            int priority = 1;
            foreach (ListItem ls in lstSLA.Items)
            {
                srvlvl = new ServiceLevel();
                srvlvl = db.ServiceLevels.Single(p => p.ID == long.Parse(ls.Value));
                srvlvl.priority = priority;
                priority++;
                ServiceLevelManagement.Update(srvlvl);

            }
            ShowMessage(ITILMessages.InsertComplete);
        }
        catch (Exception ex)
        { }
        finally { }

    }
查看更多
对你真心纯属浪费
3楼-- · 2019-01-09 13:37

I assume that it has something to do with the Dialog not being inside of the UpdatePanel or something similar.

i've also noticed a problem in the postback-values. All TextBoxes if empty or not have a comma appended.

You are indeed correct on both counts. The crux of the problem is that the Script Manager "thinks" it is supposed to update an element which jQuery has actually moved to a different location on the page, thus resulting in multiple copies of the element and the problems you have mentioned.

I've seen this problem using nested UpdatePanels, but it may occur in other scenarios as well.

This is a problem for which the workarounds are messy.

Option 1 - Change the source code for jQuery UI. I did not have any luck with a quick fix; short of rewriting the entire plugin, I couldn't find an easy to have the dialog work correctly without reordering the DOM. Also, with that route, now you "own" the source code because you have modified it.

Option 2 - Adjust the DOM whenever the page is rendered partially to remove the duplicate elements. You can output some additional script to cleanup the spurious duplicate element. I don't like this approach because it allows the DOM to be in an invalid state until the script runs.

Option 3 - Manually override the rendering of the UpdatePanel. Code looks something like this:

private bool _hasDomPresence
{
    get
    {
        return ViewState["__hasDomPresence"] == null ? false : (bool)ViewState["__hasDomPresence"];
    }
    set
    {
        ViewState["__hasDomPresence"] = value;
    }
}

protected override void OnLoad( EventArgs e )
{
    if( !ScriptManager.GetCurrent( this.Page ).IsInAsyncPostBack )
    {
         // a full postback means we no longer have a DOM presence
         _hasDomPresence = false;
    }

    base.OnLoad( e );
}

protected virtual void ShowDetailDialog()
{
    // code to show the offending dialog

    // we are showing it, so note the fact that it now has a DOM presence
    _hasDomPresence = true;
}  

protected override void Render( HtmlTextWriter writer )
{
    foreach( Control c in this.Controls )
    {
        //
        // find the offending control's parent container prior to it being rendered
        // In my scenario, the parent control is just a server-side DIV
        if( c == this.DetailDialog )
        {
            //
            // here, I am checking whether or not the panel actually needs to be
            // rendered. If not, I set it to invisible, thus keeping a new DOM
            // element from being created.
            if( !this.DetailUpdatePanel.IsInPartialRendering && _hasDomPresence )
            {
                this.DetailUpdatePanel.Visible = false;
            }
        }
    }

    base.Render( writer );
}

This also will confuse event validation because the client and server versions of the page don't match (or at least ASP.Net can't tell that they do). The only way I could find to make this work was to turn off event validation.

With a proper security model, event validation isn't 100% necessary but I don't like being forced to turn it off.

In summary, this is the most evil code I've posted on SO and fluffy white kittens will die if you use it, but the approach does seem to work.

Hope this helps.

查看更多
成全新的幸福
4楼-- · 2019-01-09 13:46

Here is how I solved the same issue. It removes any old dialogs and adds the newly updated one back to the form so the postbacks work. I put the following code in a script block that gets added to the page via ScriptManager.RegisterStartupScript in the code behind.

$('#divMyDialogAdded').remove();

$('#divMyDialog').dialog({
    autoOpen: false,
    modal: true
}).parent().appendTo('form:first');

$('#divMyDialog').attr('id', 'divMyDialogAdded');
查看更多
混吃等死
5楼-- · 2019-01-09 13:48

I am not sure but this is probably right ans bz its work form me when i was using

AjaxControlToolkit.ToolkitScriptManager.RegisterStartupScript

try this ?

ScriptManager.RegisterClientScriptBlock(Me.Page, GetType(Page), "showChargeFilterDialog", "createChargeFilterDialog();$('#Dialog_ChargeFilter').dialog('open');", True)
查看更多
登录 后发表回答