jQuery Dialog-Postback, но UpdatePanel не обновляется - PullRequest
8 голосов
/ 14 апреля 2011

Я хочу показать JQuery UI Dialog от Codebehind и мне нужно обновить его после постбэков.

Диалог является элементом управления для фильтрации и поиска данных. Таким образом, пользователь выбирает из DropDownLists и вводит текст в TextBoxes, нажимает кнопку «Применить», происходит асинхронная обратная передача, данные фильтруются в соответствии с выбором пользователя, и результат будет отображаться в GridView. Поэтому мне нужно обновить панель обновления вокруг GridView.

Асинхронная обратная передача работает с помощью следующих ссылок:

(в основном решение dlg.parent().appendTo(jQuery("form:first"));)

Проблема : я не могу обновить UpdatePanel ни с UpdateMode = "Always", ни вручную из codebehind через UpdatePanel.Update (). Я предполагаю, что это как-то связано с тем, что диалог не находится внутри UpdatePanel или что-то подобное. Надеюсь, кто-нибудь сможет мне помочь.

Некий источник:

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) {
        }
    });
}

Он вызывается из codebehind, когда BtnShowDialog (вне jQuery-Dialog) щелкается по

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

Обновление : я также заметил проблему в значениях обратной передачи. Все текстовые поля, если они пустые или нет, имеют запятую. Это означает, что элементы управления отображаются несколько раз в соответствии с: http://www.componentart.com/community/forums/t/60999.aspx

Я уверен, что оба вопроса связаны. Весь диалог со всеми его элементами управления будет воссоздан при каждой асинхронной обратной передаче, следовательно, все имена элементов управления существуют несколько раз в DOM (вызывая проблему с добавлением запятой в ViewState). Элементы управления видны только в панели инструментов FireBug / IE Deveoper, а не в HTML-источнике, поэтому я предполагаю, что jQuery вызывает эти проблемы. Как я могу утилизировать диалог или как я может предотвратить воссоздание (проверьте, если он уже существует) диалога? Это потому, что диалог находится внутри UpdatePanel или потому что он перемещен (через Javascript) за пределы UpdatePanel?

Уничтожение диалога перед асинхронной обратной передачей не решит проблему, потому что диалог просто исчезнет:

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

Ваша помощь очень ценится.


Решение : я закончил с использованием ModalPopupExtender из AjaxControlToolkit. После некоторых небольших проблем он работает как шарм с асинхронными обратными вызовами (не забывайте вызывать MPE.Show() в каждой функции code-behind, если вы хотите, чтобы всплывающее окно оставалось видимым). Я мог бы добавить больше кода, если кому-то интересно.

Ответы [ 4 ]

5 голосов
/ 15 апреля 2011

Я предполагаю, что это как-то связано с тем, что диалог не находится внутри UpdatePanel или что-то подобное.

Я также заметил проблему в значениях обратной передачи.Все текстовые поля, если они пустые или не имеют запятую.

Вы действительно правы в обоих случаях.Суть проблемы в том, что диспетчер сценариев «думает», что он должен обновить элемент, который jQuery фактически переместил в другое место на странице, что приведет к множественным копиям элемента и проблемам, о которых вы упоминали.

Я видел эту проблему при использовании вложенных UpdatePanels, но она может возникать и в других сценариях.

Это проблема, для которой обходные пути беспорядочные.

Вариант 1 -Измените исходный код для jQuery UI.Мне не повезло с быстрым исправлением;если не переписать весь плагин, я не мог найти, чтобы диалог работал правильно, без переупорядочения DOM.Кроме того, с этим маршрутом теперь вы «владеете» исходным кодом, потому что вы изменили его.

Вариант 2 - Настройте DOM всякий раз, когда страница отображается частично, чтобы удалить дублирующиеся элементы.Вы можете вывести некоторый дополнительный скрипт для очистки ложного дублирующего элемента.Мне не нравится этот подход, потому что он позволяет DOM находиться в недопустимом состоянии до тех пор, пока не запустится сценарий.

Вариант 3 - вручную отменить рендеринг UpdatePanel.Код выглядит примерно так:

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 );
}

Это также может сбить с толку проверку события, поскольку версии страницы клиента и сервера не совпадают (или, по крайней мере, ASP.Net не может сказать, что они это делают).Единственный способ найти эту работу - отключить проверку событий.

При правильной модели безопасности проверка событий не требуется на 100%, но мне не нравится, когда меня заставляют ее отключать.

Таким образом, это самый злой код, который я разместил на SO, и пушистые белые котята умрут, если вы его используете, но подход, похоже, работает.

Надеюсь, это поможет.

4 голосов
/ 07 сентября 2012

Вот как я решил ту же проблему.Он удаляет все старые диалоги и добавляет обновленный обратно в форму, чтобы работали постбэки.Я поместил следующий код в блок скрипта, который добавляется на страницу через ScriptManager.RegisterStartupScript в коде позади.

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

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

$('#divMyDialog').attr('id', 'divMyDialogAdded');
1 голос
/ 07 июля 2013

Я думаю, что Jquery и менеджер скриптов / панель обновлений при создании и синтаксическом анализе созданного скрипта с ней связываются, и вы должны правильно обработать событие в Trigger панели обновлений, чтобы использовать это в методе code code:

       UpdatePanel2.Update();

так что у меня есть эта проблема, и я могу решить ее с помощью приведенного ниже кода (это мой пример кода):

   <!--------- 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>

и в коде c #: protected void BtnUpMove_Click (отправитель объекта, 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 { }

    }
1 голос
/ 14 апреля 2011

Я не уверен, но это, вероятно, правильно, и потому, что он работал со мной, когда я использовал

AjaxControlToolkit.ToolkitScriptManager.RegisterStartupScript

Попробуйте это?

ScriptManager.RegisterClientScriptBlock(Me.Page, GetType(Page), "showChargeFilterDialog", "createChargeFilterDialog();$('#Dialog_ChargeFilter').dialog('open');", True)
...