UpdatePanel с типом ввода, отличным от текста в html5

Я разрабатываю проект с открытым исходным кодом для рендеринга HTML5 с использованием ASP.NET. Здесь вы можете посмотреть: http://asphtml5.codeplex.com/

теперь у меня возникла проблема с панелью обновления при отправке возвращаемых значений, которые имеют тип, отличный от "text". как вы могли бы знать, html 5 представил несколько типов ввода, например "number", "tel", "search" и т.д. Теперь, если я создам такие элементы управления, все нормально работает в нормальных ситуациях, но если я помещу их внутри UpdatePanel, никакое значение не будет отправлено назад, а значение будет reset.

вот небольшой фрагмент кода, который вызывает ту же ошибку:

    <asp:UpdatePanel runat="server" ID="UP">
        <ContentTemplate>
            <p>
                Enter A Number:
                <asp:TextBox runat="server" ID="Number2" type="number" />
            </p>
            <asp:Button Text="Submit" runat="server" ID="BtnSubmit" OnClick="BtnSubmit_Click" />
            <p>
                You entered :
                <asp:Label Text="" ID="LblValue" runat="server" />
            </p>
        </ContentTemplate>
    </asp:UpdatePanel>

если вы протестируете этот код в браузере, поддерживающем html 5, скажем, Chrome в качестве примера, будет показано поле Numeric Up-Down. но если вы нажмете кнопку "Отправить", она потеряет введенное значение.

вот код для обработчика событий:

        protected void BtnSubmit_Click(object sender, EventArgs e)
        {
            LblValue.Text = Number2.Text;
        }

то, что я уже пробовал, читает коды классов UpdatePanel, ScriptManager и ScriptManagerProxy, ничего не найдено.

Мне кажется, мне может понадобиться создать собственные классы UpdatePanel и /ScriptManager для использования.

Может кто-нибудь мне помочь, и скажите, где проверить.

Ответ 1

Хорошо, я понял, как это исправить. Прежде всего я нашел проблему с кодом, который предоставил Тим Медора. Все это было ошибкой модификатора this.. Итак, этот JavaScript исправил проблему:

var _textTypes = /^(text|password|hidden|search|tel|url|email|number|range|color|datetime|date|month|week|time|datetime-local)$/i;
function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
...
if (_textTypes.test(type) ||
       (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
       formBody.append(encodeURIComponent(name));
       formBody.append('=');
       formBody.append(encodeURIComponent(element.value));
       formBody.append('&');
...
}

Теперь мне пришлось ввести мою функцию в ScriptResource.axd. На данный момент я нашел способ, который, кажется, работает:

Я создал класс ScriptResouceHandler, который расширяет System.Web.Handlers.ScriptResourceHandler в пространстве имен DotM.Html5.Handlers.

в своем ProcessRequest я назвал base.ProcessRequest(context) для выполнения своей работы. Но я хотел добавить свою функцию в одну оригинальную функцию рендеринга. Я узнал, что это было тогда, когда был зашифрован ZSystem.Web.Extensions,4.0.0.0,,31bf3856ad364e35|MicrosoftAjaxWebForms.debug.js|.

Другая проблема заключалась в том, что в System.Web.Handlers.ScriptResourceHandler, метод 'Page.DecryptString', который является внутренним, вызывается для дешифрования параметра строки запроса. поэтому для меня не было способа вызвать этот метод через отражение.

вот код:

        protected sealed override void ProcessRequest(HttpContext context)
        {
            base.ProcessRequest(context);
            if (CypherContainsAjax(context.Request.QueryString["d"]))
                context.Response.Write(OnFormSubmit);
        }

        private bool CypherContainsAjax(string cypher)
        {
            var text = DecryptString(cypher);
            if (text == null)
                return true; //Then Add it everywhere. What else could I do? :D
            return text.Contains("MicrosoftAjaxWebForms");
        }

        private string DecryptString(string cypher)
        {
            if (PageDecryptString == null)
                return null;
            return (string)PageDecryptString.Invoke(null, new object[] { cypher });
        }
        private static MethodInfo PageDecryptString;
        static ScriptResourceHandler()
        {
            PageDecryptString = typeof(Page).GetMethod("DecryptString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
        }

Вы можете назвать это своего рода уродливым взломом...

Ответ 2

Интересно, что структура ASP.NET 4.0 AJAX, похоже, знает типы ввода HTML 5 (см. код), но, как отметил @TimSchmelter, это подтверждается Microsoft как ошибка.

Это может дать вам отправную точку для отладки поведения и/или переопределения поведения по умолчанию и поиска решения.

Это также может быть ошибка в коде обработки на стороне сервера для этих типов входных данных, хотя я не уверен, почему они должны/должны заботиться об обратной обратной связи async против обычной обратной передачи.

this._textTypes = /^(text|password|hidden|search|tel|url|email|number|range|color|datetime|date|month|week|time|datetime-local)$/i;

function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
        var i, l, continueSubmit = true,
            isCrossPost = this._isCrossPost;
        this._isCrossPost = false;
        if (this._onsubmit) {
            continueSubmit = this._onsubmit();
        }
        if (continueSubmit) {
            for (i = 0, l = this._onSubmitStatements.length; i < l; i++) {
                if (!this._onSubmitStatements[i]()) {
                    continueSubmit = false;
                    break;
                }
            }
        }
        if (!continueSubmit) {
            if (evt) {
                evt.preventDefault();
            }
            return;
        }
        var form = this._form;
        if (isCrossPost) {
            return;
        }
        if (this._activeDefaultButton && !this._activeDefaultButtonClicked) {
            this._onFormElementActive(this._activeDefaultButton, 0, 0);
        }
        if (!this._postBackSettings || !this._postBackSettings.async) {
            return;
        }
        var formBody = new Sys.StringBuilder(),
            count = form.elements.length,
            panelID = this._createPanelID(null, this._postBackSettings);
        formBody.append(panelID);
        for (i = 0; i < count; i++) {
            var element = form.elements[i];
            var name = element.name;
            if (typeof(name) === "undefined" || (name === null) || (name.length === 0) || (name === this._scriptManagerID)) {
                continue;
            }
            var tagName = element.tagName.toUpperCase();
            if (tagName === 'INPUT') {
                var type = element.type;
                if (this._textTypes.test(type)
                    || ((type === 'checkbox' || type === 'radio') && element.checked)) {
                    formBody.append(encodeURIComponent(name));
                    formBody.append('=');
                    formBody.append(encodeURIComponent(element.value));
                    formBody.append('&');
                }
            }
            else if (tagName === 'SELECT') {
                var optionCount = element.options.length;
                for (var j = 0; j < optionCount; j++) {
                    var option = element.options[j];
                    if (option.selected) {
                        formBody.append(encodeURIComponent(name));
                        formBody.append('=');
                        formBody.append(encodeURIComponent(option.value));
                        formBody.append('&');
                    }
                }
            }
            else if (tagName === 'TEXTAREA') {
                formBody.append(encodeURIComponent(name));
                formBody.append('=');
                formBody.append(encodeURIComponent(element.value));
                formBody.append('&');
            }
        }
        formBody.append("__ASYNCPOST=true&");
        if (this._additionalInput) {
            formBody.append(this._additionalInput);
            this._additionalInput = null;
        }

// truncated for length

Ответ 3

Это уже было исправлено в .Net 4 Обновление надежности 1 (есть также версия 2, но она не содержит первую): http://support.microsoft.com/kb/2533523

Но если вы используете AjaxControlToolkit, он использует собственную внутреннюю MicrosoftAjaxWebForms.js, которая является более старой версией, и официальной ошибки для нее пока нет. Вы можете использовать мое решение здесь: http://ajaxcontroltoolkit.codeplex.com/workitem/27041

Итак, вы можете либо включить фиксированный ToolkitScriptManager с вашим проектом (раздутый, я знаю), либо вы можете попробовать включить новую версию MicrosoftAjaxWebForms.js, поэкспериментируя с свойствами ванильного ScriptManager AjaxFrameworkMode = "Явные", скрипты или CompositeScript.

Используйте свойство AjaxFrameworkMode для включения всех файлов Microsoft Ajax script, чтобы отключить все файлы Microsoft Ajax script или явно включить отдельные файлы script.

Коллекция Scripts не содержит основных сценариев библиотеки Microsoft Ajax. Сценарии в основной библиотеке отображаются автоматически; они не должны быть зарегистрированы в элементе управления ScriptManager. Однако, если вы хотите переопределить ядро ​​ script или любой элемент управления script и заменить другую версию script, вы можете добавить свою версию в коллекцию Scripts.

<asp:ScriptManager runat="server">
  <Scripts>
    <asp:ScriptReference Path="~/MicrosoftAjaxWebForms.js" />
  </Scripts>
</asp:ScriptManager>

Вы можете получить новую версию MicrosoftAjaxWebForms.js из ресурсов сборки System.Web.Extensions(с .Net Reflector) на машинах с установленным обновлением надежности.