I am developing an open source project for rendering HTML5 using ASP.NET. Here you can take a look: http://asphtml5.codeplex.com/
now I have a problem with update panel in posting back the input values that have type other than 'text'. as you might know, html 5 has introduced several input types, for example 'number', 'tel', 'search', etc. Now if I render such controls, everything works fine in normal situations, but if I put them inside an UpdatePanel, no value will be posted back and the value will be reset.
here is a small piece of code that produces the same error:
<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>
if you test this code on a browser that supports html 5, lets say Chrome as an example, a Numeric Up-Down field will be shown. but if you click on the submit button, it will lose the value that you have entered.
here is the code for event handler:
protected void BtnSubmit_Click(object sender, EventArgs e)
{
LblValue.Text = Number2.Text;
}
what I have already tried is reading UpdatePanel, ScriptManager and ScriptManagerProxy classes codes, nothing found.
I think I might need to create my own UpdatePanel and/or ScriptManager classes for use.
Could anyone help me, and tell me where to check.
Interestingly enough, the ASP.NET 4.0 AJAX framework does seem to be aware of HTML 5 input types (see code), yet as @TimSchmelter pointed out, this is confirmed by Microsoft as a bug.
This may give you a starting point for debugging the behavior and/or overriding the default behavior and finding a solution.
It could also be an error on the server-side processing code for these input types, although I'm not sure why they should/would care about an async postback versus a normal postback.
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
All right then, I figured out a way to fix that.
First of all I found the problem with the code that Tim Medora provided. It all was the this.
modifier's fault. So this JavaScript fixed the problem:
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('&');
...
}
Now I had to inject my function into ScriptResource.axd
.
For now I found a way that seems to work:
I created a class ScriptResouceHandler
that extends System.Web.Handlers.ScriptResourceHandler
in namespace DotM.Html5.Handlers.
in its ProcessRequest
I called base.ProcessRequest(context)
to do its job. But I wanted to add my function to the one rendering original function. I found out that it was when the encrypted ZSystem.Web.Extensions,4.0.0.0,,31bf3856ad364e35|MicrosoftAjaxWebForms.debug.js|
was passed.
Another problem was that in System.Web.Handlers.ScriptResourceHandler
, 'Page.DecryptString' method which is internal is called to decrypt the query string param.
so there was no way for me to invoke that method via reflection.
here is the code:
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);
}
You may call this some kind of ugly hacking ...
This was already fixed in .Net 4 Reliability Update 1 (there is also a version 2, but it does not contain the first one): http://support.microsoft.com/kb/2533523
But if you are using AjaxControlToolkit, it uses its own internal MicrosoftAjaxWebForms.js which is an older fork, and there is still no official fix for it - you can use mine solution from here: http://ajaxcontroltoolkit.codeplex.com/workitem/27041
So - you can either include the fixed ToolkitScriptManager with your project (a bloat, I know), or you can try to include the new version of MicrosoftAjaxWebForms.js by experimenting with vanilla ScriptManager properties AjaxFrameworkMode="Explicit", Scripts or CompositeScript.
Use the AjaxFrameworkMode property to enable all Microsoft Ajax script files, to disable all Microsoft Ajax script files, or to explicitly include individual script files.
The Scripts collection does not contain the core Microsoft Ajax Library scripts. The scripts in the core library are rendered automatically; they do not have to be registered with the ScriptManager control. However, if you want to override a core script or any control script and substitute a different version of the script, you can add your version to the Scripts collection.
<asp:ScriptManager runat="server">
<Scripts>
<asp:ScriptReference Path="~/MicrosoftAjaxWebForms.js" />
</Scripts>
</asp:ScriptManager>
You can get the new version of MicrosoftAjaxWebForms.js from System.Web.Extensions assembly Resources (with .Net Reflector) on machines with installed Reliability Update 1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With