My scenario:
I have a validator in an UpdatePanel.
I want to combine my scripts so I am using CompositeScript in ScriptManager, and including a reference to WebUIValidation.js
I am using .NET 4.0
My problem:
When I asynchronously update the panel, .NET loads WebUIValidation.js (in a Scriptresource.axd file) in the async response, even though it has been loaded in the initial CompositeScript-generated script. This is a problem because I have custom code that hijacks some functions in WebUIValidation.js, and the async response overrides my hijacks.
If you move the reference to WebUIValidation.js to Scripts in ScriptManager, there is no problem.
If you were to have WebUIValidation.js as the only item in CompositeScript (pointless I know) then there is no problem.
This async reload does not happen with other .NET library scripts, e.g. WebForm.js
What I want to find out:
is there a reason why WebUIValidation.js is loaded in the async response when it is already included in the CompositeScript?
Someone has posted a similar (but not duplicate) issue today, and is veering towards saying that WebUIValidation.js might not be handled by ScriptManager. Can anyone verify this?
To replicate use the following two files
test1.js
// To be added to the composite script
console.log('Test 1 Loaded');
test.aspx
<%# Page Language="vb" AutoEventWireup="false" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title></title>
</head>
<body>
<script language="VB" runat="server" runat="server">
Protected Sub ButtonClicked(ByVal sender As Object, ByVal e As System.EventArgs) Handles TestButton.Click
ButtonClickFeedback.Text = "Button clicked At " & Date.Now.ToString & "; Look at the scripts in your Developer Tools, there is now a separate script for WebUIValidation.js loaded, in spite of the composite script."
End Sub
</script>
<form runat="server">
<asp:ScriptManager runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference Path="~/test.js" />
<asp:ScriptReference Name="WebUIValidation.js" Assembly="System.Web" />
</Scripts>
</CompositeScript>
</asp:ScriptManager>
<h1>WebUIValidation.js, CompositeScript and UpdatePanel test</h1>
<asp:UpdatePanel runat="server" ID="ButtonUpdatePanel">
<ContentTemplate>
<asp:Label runat="server" >This is a section with a validator that is within an UpdatePanel. If you look at the scripts loaded, you will see the composite script in the detail.</asp:Label>
<asp:Textbox ID="TestInputForValidator" runat="server" Text="This is populated so it will validate"/>
<asp:RequiredFieldValidator runat="server" ControlToValidate="TestInputForValidator" ErrorMessage="You must write something" /><br />
<asp:Button ID="TestButton" Text="Click Me!" runat="server" /><br />
<asp:Literal ID="ButtonClickFeedback" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="TestButton" />
</Triggers>
</asp:UpdatePanel>
</form>
</body>
</html>
If you use your Developer Tools to inspect what scripts are being loaded in, you should see an additional Scriptresource.axd (that contains WebUIValidation.js) getting loaded in after clicking the button, in spite of the existence of a Scriptresource.axd with the composite script. test.js is just a sample js file to simulate the idea of composite scripts.
Sharing my investigation on why it's loading these scripts again (WebUIValidation.js or Focus.js). I've just found about Focus.js for now.
First, the origination of the http request is in the partial update generated by the update panel. If you look at the reponse to the asynchronous xhr POST request, you'll have something like this. Note almost at the end the ScriptResource.axd url. This is processed by the ajax framework on the client side and since it's a script block with a path, it gets loaded:
1|#||4|2999|updatePanel|ctl00_LoginContent_ctl00|
<div id="ctl00_LoginContent_...">[...html content here]</div>|
0|hiddenField|__LASTFOCUS||
0|hiddenField|__EVENTTARGET||
0|hiddenField|__EVENTARGUMENT||
904|hiddenField|__VIEWSTATE|UdWhNvH6wpBcPOigY[...]SIphbw==|
8|hiddenField|__VIEWSTATEGENERATOR|25748CED|
176|hiddenField|__EVENTVALIDATION|q+FUEVGVj+t[...]AzAm|
0|asyncPostBackControlIDs|||
0|postBackControlIDs|||
26|updatePanelIDs||tctl00$LoginContent$ctl00,|
0|childUpdatePanelIDs|||
25|panelsToRefreshIDs||ctl00$LoginContent$ctl00,|
2|asyncPostBackTimeout||90|
14|formAction||./Default.aspx|
119|scriptBlock|ScriptContentNoTags|function PopulateTimeZoneOffset(){[my own js here...]}|
154|scriptBlock|ScriptPath|/ScriptResource.axd?d=Uup1Lt[...]q450&t=ffffffffd4ee116f|
31|focus||ctl00_LoginContent_LoginControl|
Now debugging server side code, loading the .net assemblies symbols from https://referencesource.microsoft.com/ (with VS configuration as described there).
PageRequestmanager.cs
private void ProcessFocus(HtmlTextWriter writer) {
// Roughly stolen from Whidbey Page.cs
if (_requireFocusScript) {
Debug.Assert(ClientSupportsFocus, "If ClientSupportsFocus is false then we never should have set _requireFocusScript to true.");
string focusedControlId = String.Empty;
// Someone calling SetFocus(controlId) has the most precedent
if (!String.IsNullOrEmpty(_focusedControlID)) {
focusedControlId = _focusedControlID;
}
else {
if (_focusedControl != null && _focusedControl.Visible) {
focusedControlId = _focusedControl.ClientID;
}
}
if (focusedControlId.Length > 0) {
// Register focus script library
string focusResourceUrl = _owner.GetScriptResourceUrl("Focus.js", typeof(HtmlForm).Assembly);
EncodeString(writer, ScriptBlockToken, "ScriptPath", focusResourceUrl);
// *********** THIS ENCODESTRING OUTPUTS THE PROBLEM !!!
// Send the target control ID to the client
EncodeString(writer, FocusToken, String.Empty, focusedControlId);
}
}
}
We are deep inside Page.ProcessRequest, and now in Page.Render, RenderPageCallback and ProcessFocus. The highlighted EncodeString near the end writes directly to the writer things like "len|type|id|content|", including writer.Write(content); where content is "/ScriptResource.axd?d=Uup1IW...q450&t=ffffffffd4ee116f". There is no check to see if this script is already registered with the ScriptManager, it's not calling ScriptManager.RegisterXXXX.
So it seems to me that's the cause of getting another http request for something that's already loaded. ProcessFocus is done as part of "Render", far too late to use any ScriptManager functinality.
I can't think of a way to avoid this http request (apart from not using any SetFocus type thing from the .net framework).
(Running VS 2015, .net framework 4.6.2, ajax control toolkit 17.1)
Related
AjaxFileUpload works on visible TabContainer control TabPanels yet not on ones that are initially invisible and then set to visible.
I believe the issue would be resolved if the visibility property of the TabPanels is set by JavaScript rather than from the server but doesn't know how to do it.
Please help me to fix this issue. Thanks.
ASPX Code:
<%# Page Language="VB" AutoEventWireup="false" CodeFile="AjaxFileUpload.aspx.vb" Inherits="_Default" %>
<%# Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server" EnablePageMethods="true"></asp:ToolkitScriptManager>
<p>AjaxFileUpload works on visible TabContainer control TabPanels yet not on ones that are initially invisible and then set to visible.</p>
<p>I believe the issue would be resolved if the visibility property of the TabPanels is set by JavaScript rather than from the server.</p>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:TabContainer ID="TabContainer1" runat="server" ActiveTabIndex="0">
<asp:TabPanel ID="TabPanel1" runat="server" HeaderText="TabPanel 1">
<ContentTemplate>
<asp:Button ID="btnShow" runat="server" Text="Show"></asp:Button>
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="TabPanel2" runat="server" HeaderText="TabPanel 2" Visible="false">
<ContentTemplate>
<asp:Button ID="btnHide" runat="server" Text="Hide"></asp:Button>
<asp:AjaxFileUpload ID="AjaxFileUpload1" runat="server" AllowedFileTypes="txt,xls,xlsx,doc,docx,msg,pdf,bmp,gif,jpg,jpeg,png" MaximumNumberOfFiles="5" Width="500px" OnUploadComplete="AjaxFileUpload1_OnUploadComplete"></asp:AjaxFileUpload>
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
Backend VB.Net Code:
Imports System.IO
Imports AjaxControlToolkit
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub btnShow_Click(sender As Object, e As System.EventArgs) Handles btnShow.Click
If TabPanel2.Visible = False Then TabPanel2.Visible = True
TabContainer1.ActiveTabIndex = 1
AjaxFileUpload1.Visible = True
End Sub
Protected Sub btnHide_Click(sender As Object, e As System.EventArgs) Handles btnHide.Click
If TabPanel2.Visible = True Then TabPanel2.Visible = False
End Sub
Protected Sub AjaxFileUpload1_OnUploadComplete(ByVal sender As Object, ByVal e As AjaxFileUploadEventArgs)
Dim strPath As String = Server.MapPath("~/Uploads")
If Not Directory.Exists(strPath) Then Directory.CreateDirectory(strPath)
Dim sFilename As String = System.IO.Path.GetFileName(e.FileName)
Dim sUploadPath As String = "~/Uploads/"
AjaxFileUpload1.SaveAs(Server.MapPath(sUploadPath) + sFilename)
Dim filePath As String = Server.MapPath("~/Uploads/" & e.FileName)
Dim filename As String = Path.GetFileName(filePath)
End Sub
End Class
Actually, even if you set a textbox with visible=false, then the control is NOT sent down to the web page. So, you can't client side even turn that simple text box to become visible.
Use style, and display none.
The page first time is initialized, and that includes js wriing up the ajax up-loader.
So, put the mess in a div (with the control, and anything else you need to hide/show)
, say like this:
<div id="uploader" runat="server" style="display:none;width:50%">
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1" runat="server"
ChunkSize="8192"
ClientIDMode="Static"
ViewStateMode="Enabled" />
</div>
So, if you are in SERVER side code (some button postback), then you can show the "div" like this:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.uploader.Style("display") = "inline"
End Sub
and to hide:
Me.uploader.Style("display") = "none"
Remember, while we are in a post back, all that "jazz" JavaScript STILL needed to do it dance on the FIRST page load. (so, we can't use visible = false) on first page load (and against the control or div). You need the full page to load - including the truckload of code that ONLY runs the FIRST time the ajaxupload control loads. If it not a first page load - the control will not setup and initialize correctly.
So, by using style, then the control is rendered when the page is first loaded. And this concept not only applies to the ajax, but say for a hidden text box or what not? Again, using style with display none.
In fact, DURING the process of a user selecting files, and up-loading with that control? Well, you don't want a post back. But when the whole up-load is done? And the server side events (such as saving the files), then I often DO WANT some final code, or even the web page to move on. I do want a final post-back to move on. But, I need a post-back client side!
In place of using a JaveScript _dopostback? (which forces you to write parameter code in the server side page load event? I just drop a asp button on the form some place, and HIDE it (of course with style="display:none").
Now I can use client side js to to "click" of that hidden button. The beauty is then the server side event code stub for the single button code runs, and I don't have write up event code with _doPostBack + code in load event for a code stub server side to run!
Now, in the above, I displayed the up-loader using a server side code-behind.
But, you can ALSO rather easy have client side code to hide or show that div.
If you using jQuery()?
Well, jQuery has .hide(), and .show() (and guess what!!! - .hide()/.show() actually sets the style for you!!! - so even jQuery assumes that you hide or show controls with style, since using the "SomeControl.visible" is near use-less (since anything set visible = false is NOT rendered client side - so then you can't hide/show anyway!!!).
So, to hide/show that with jQuery, you could use this client side:
function show() {
$('#uploader').show();
}
function hide() {
$('#uploader').hide();
}
And, if you not using jQuery, and want to hide/show the div? (javascript).
This will work:
function showl2() {
var mydiv2 = document.getElementById('<%=uploader.ClientID%>');
mydiv2.style.display = "inline";
}
function hidel2() {
var mydiv = document.getElementById('<%=uploader.ClientID%>');
mydiv.style.display = "none";
}
So, the simple concept here is that the control has to render on the page load and must do the first time. So, it has to come down to the browser side. Any control not visible will not be sent down to the client side. However, using css means the control is sent down client side - in all cases.
Now, after a upload, and I want a full post back of the page? Well, as note, this can be difficult, since after the ajax server side events run (such as saving the file), you do NOT get a final post back when all is said and done.
So, I set a client side event for the ajax up-loader like this:
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1" runat="server"
AllowedFileTypes="pdf,zip"
ChunkSize="8192"
OnClientUploadCompleteAll="UpLoadDone"
/>
So, note the above client side event - all code (both server and client is done). But we now want a postback from the client side.
So, in the UpLoadDone routine?
I have this:
function uploaddone() {
document.getElementById('<%= btnProof.ClientID %>').click();
}
So, I get a post back and ALSO the code in the button stub server side also runs. And that btProof is hidden with style. But, it is a regular asp.net button. I just wanted it to be clicked after the ajax file up-load is done. But, there not really a way to get the ajax up-load to fire a postback when all is said and one. But, once again:
The concept of a hidden control with style is used - since if I hide that button with .visible = false, then in fact the button would not exist client side, and never be rendered or placed into the browser client side.
I create a chart, and save it in the path server.mappath("/images/chart/chartname.png"), and if I go to the path, the image is there, and I can open it on my computer, but what I would really like to do is display the image on the page after I create it, preferably asynchronously. I've tried putting a asp:image control in the update panel and changing the url, but that doesn't work. I've tried a bunch of different ways, if someone could point me to the right direction, that would be great. I won't post any code simple because what I have so far IS working. Once I start attempting to print the image I'll post snippets if I have answers.
Edit: Here is some code
The button event that starts the whole thing:
Protected Sub btnCreate_Click(sender As Object, e As EventArgs) Handles btnCreate.Click
Dim worker As New backgroundWorker
'Check input
worker.RunWorker({})
Session("worker") = worker
Timer1.Enabled = True
End Sub
This starts the worker, which generates the chart fine, and starts the timer which is used to update the panel. Eventually in that code, this happens
Dim imgpath As String = Server.MapPath("images/chart/test.png")
chart.SaveImage(imgpath, ChartImageFormat.Png)
chartImg.ImageUrl = "~/images/chart/test.png"
Now, I know that the panel is being updated after this code is executed, because I output some messages to a multiline textbox, and they do appear. All these controls are also in the contentTemplate of the updatePanel.
Setting the ImageUrl property of an ASP.Net image control, while within and update panel, should do the trick without having to use Javascript, although that method can be very efficient. UpdatePanels tend to be "heavy", that is the ScriptManager, UpdatePanel, and ASP.Net ViewState tend to send a lot of information back and forth via their AJAX methods. You can see what I mean by using Fiddler to watch your web traffic.
That being said, I was able to achieve the desired effect in the following manner. I have two static images, Image1.jpg and Image2.jpg, but it should work with your dynamically generated image, as long as the URL is correct.
Here's the text of my ASPX page:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Image ID="Image1" runat="server" ImageUrl="~/Image1.jpg" Width="300"></asp:Image><br />
<asp:Button runat="server" ID="btnSwitchImage" Text="Switch" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
And here is the code-behind:
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load( object sender, EventArgs e )
{
btnSwitchImage.Click += new EventHandler( btnSwitchImage_Click );
}
void btnSwitchImage_Click( object sender, EventArgs e )
{
Image1.ImageUrl = "~/Image2.jpg";
}
}
As long as the button and image controls are within the UpdatePanel's ContentTemplate everything should be relayed via AJAX. Once again, use Fiddler to confirm this.
If this example works for you but you still can't get your application working try posting some code in your question. It might help everyone analyze the problem directly.
As per my experience uploading of files does not work in asynchronous mode.You should use Jquery library to upload file asynchronously.
Check this link.
http://www.9lessons.info/2011/08/ajax-image-upload-without-refreshing.html
I do not know ajax but you can do it with the help of javascript. Here is the code I tested on my system which works fine.
<body>
<p>
welcome to judge site</p>
<form id="form1" runat="server">
<script language="javascript" type="text/javascript">
function PrintImage()
{
printWindow = window.open ("", "mywindow","location=1,status=1,scrollbars=1,width=600,height=600");
printWindow.document.write("<div style='width:100%;'>");
printWindow.document.write("<img id='img' src='" + document.getElementById('iconId').src + "'/>");
printWindow.document.write("</div>");
printWindow.document.close();
printWindow.print();
}
</script>
<div>
<img id="iconId" alt="photo" src="iconId.png" style="width: 500px; height: 360px" /><br />
<input type="button" id="btnPrint" value="Print Image" onclick="PrintImage()" />
</div>
</form>
There are other more examples you can find on following link (Above code is also from this source but edited by me to make it work on my system)
Visit http://forums.asp.net/p/1463001/3369521.aspx
First off, I know that what I am doing here seems entirely impractical and not good design, but I am trying to increase performance in this ASPX that contains 8,000+ lines of markup. Because of the complexity of this page (not to mention messiness) and short deadline, rewriting it to use clientside binding with AJAX/JSON is just not an option, so I have to continue to use serverside binding.
The page I am working on contains around 13 individual sections, each one loading its own entity from the database. Right now, the page initially loads ALL entities synchronously, so you can imagine that this page can sometimes take 5 seconds or longer to load. My goal here is to employ a quick fix that will load these sections only when the section is expanded so that we load only the sections that are requested by the user, thus increasing performance and conserving database resources.
The sample code below should be easy to paste right into a VB.NET WebForm if you're interested in trying this out for yourself. Just name the page asyncupdatepanels.aspx.
The problem:
Overall, my solution is working fairly well. In cmUpdate_Click, I use Threading.Thread.Sleep(2000) to simulate a call to the database to retrieve data. When you click one of the buttons, it pauses for 2 seconds and then sets the appropriate Panel's .Visible property to True.
The issue occurs when you click one button and then click the the other before the first one is finished updating. For example, if you click Show Panel 1 then quickly click Show Panel 2, only Panel 2 shows even though both button clicks are triggered in the codebehind.
Maybe asynchronous UpdatePanel is the wrong term to use here. Regardless, I need to find a way to show the panels as if they were executed in separate asyncronous threads. I want to be able to click these buttons pretty much near the same time and have both panels show.
If anyone has any other solutions to my problem that will not require major changes to the way I bind controls in each section, I'd love to hear it. The method I am using now is pretty much a hack, but it will work for now until we eventually rewrite this whole thing in MVC/c#.
Edit: The production code doesn't actually call a Javascript function by use of a button's OnClientClick. Instead, it uses a jQuery accordion. I just wanted to keep the sample code simple. For now, focus on __doPostBack("<%=cmUpdate.ClientID %>", ButtonId); regardless of how it's ultimately called.
ASPX
<%# Page Language="vb" AutoEventWireup="false" EnableEventValidation="false" CodeBehind="asyncupdatepanels.aspx.vb" Inherits="JsonJqueryDevex.asyncupdatepanels" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script language="javascript" type="text/javascript">
function UpdateIt(ButtonId) {
__doPostBack("<%=cmUpdate.ClientID %>", ButtonId);
return false;
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<asp:Button ID="cmShow1" Text="Show Panel 1" ClientIDMode="Static" OnClientClick="javascript:return UpdateIt(this.id);" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:Panel ID="pnl1" Visible="false" runat="server">
Panel 1 content
</asp:Panel>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="cmUpdate" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
<asp:Button ID="cmShow2" Text="Show Panel 2" ClientIDMode="Static" OnClientClick="javascript:return UpdateIt(this.id);" runat="server" />
<asp:UpdatePanel ID="UpdatePanel2" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:Panel ID="pnl2" Visible="false" runat="server">
Panel 2 content
</asp:Panel>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="cmUpdate" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
<div style="display: none">
<asp:UpdatePanel UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:Button ID="cmUpdate" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</div>
</form>
</body>
</html>
Codebehind:
Public Class asyncupdatepanels
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
Private Sub cmUpdate_Click(sender As Object, e As System.EventArgs) Handles cmUpdate.Click
Dim Param As String = Request("__EVENTARGUMENT")
Threading.Thread.Sleep(2000)
Select Case Param
Case "cmShow1"
pnl1.Visible = True
Case "cmShow2"
pnl2.Visible = True
End Select
End Sub
End Class
How about disabling the appropriate buttons on click?
Say,
function UpdateIt(ButtonId) {
$('#<%=cmShow1.ClientID %>').attr('disabled', true);
$('#<%=cmShow2.ClientID %>').attr('disabled', true);
__doPostBack("<%=cmUpdate.ClientID %>", ButtonId);
return false;
}
Then, in your code behind, after the sleep, enable them again (cmShow1.Enabled = true / cmShow2.Enabled = true) - the UpdatePanel call will handle the rest.
I would do an AJAX call to your server-side in the page_load event of the page where your updated panel is. You would then call the Update method of your update panel when your processing is done.
You won't have to wait for the processing to be done to do whatever you want to do meanwhile.
Javascript(with jQuery):
function ajaxCall() {
$.ajax({
url: "YourPage.aspx"
});
}
You can process your AJAX call in the Page_Load in your .NET.
I know that you said using AJAX wouldn't be a good option, but this is fairly short and simple.
Below is the code I've currently implemented.
if (!Page.ClientScript.IsStartupScriptRegistered(Page.GetType(), scriptKey))
{
ScriptManager scriptManager = ScriptManager.GetCurrent(page);
if (scriptManager != null && scriptManager.IsInAsyncPostBack)
{
//if a MS AJAX request, use the Scriptmanager class
ScriptManager.RegisterStartupScript(Page, Page.GetType(), scriptKey, script, true);
}
else
{
//if a standard postback, use the standard ClientScript method
Page.ClientScript.RegisterStartupScript(Page.GetType(), scriptKey, script, true);
}
}
I'm doing as suggested in this answer so that I can register startup script on both times i.e. when there is partial postback and a full postback.
The problem is Page.ClientScript.IsStartupScriptRegistered(Page.GetType(), scriptKey) always (even when the script is registered before) returns false when it is partial postback. And I couldn't find ScriptManager.IsStartupScriptRegistered (static) method. As a result of this, additional script is emitted on all partial/async postbacks.
Please note that I'm using script manager of AjaxControlToolkit version 4.1 i.e. ToolkitScriptManager in my masterpage. But I don't thing it has something to do with this.
UPDATE
<asp:UpdatePanel ID="ContactDetailsUpdatePanel" UpdateMode="Conditional" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="UpdateContactDetailsButton" EventName="Click" />
</Triggers>
<ContentTemplate>
<div id="ContactDetailsContent" class="contact_details_content">
<div class="customer_contactdetails_left_pane">
<div class="customer_name_field">
<asp:Label ID="CustomerNameLabel" runat="server" Text="Customer" />
<asp:TextBox ID="CustomerNameValue" runat="server" />
</div>
<div class="customer_address_field">
<asp:Label ID="CustomerAddressLabel" runat="server" Text="Address" />
<asp:TextBox ID="CustomerAddressValue" runat="server" />
<asp:TextBox ID="CustomerAddressValue1" runat="server" />
<asp:TextBox ID="CustomerAddressValue2" runat="server" />
<asp:TextBox ID="CustomerAddressValue3" runat="server" />
</div>
<div class="customer_postcode_field">
<asp:Label ID="CustomerPostcodeLabel" runat="server" Text="Postcode" />
<asp:TextBox ID="CustomerPostcodeValue" runat="server" />
</div>
</div>
<div class="customer_contactdetails_right_pane">
<div>
<asp:Label ID="CustomerContactLabel" runat="server" Text="Contact" />
<asp:TextBox ID="CustomerContactValue" runat="server" />
</div>
<div>
<asp:Label ID="CustomerTelephoneLabel" runat="server" Text="Telephone" />
<asp:TextBox ID="CustomerTelephoneValue" runat="server" />
</div>
<div>
<asp:Label ID="CustomerMobileLabel" runat="server" Text="Mobile" />
<asp:TextBox ID="CustomerMobileValue" runat="server" />
</div>
<div>
<asp:Label ID="CustomerFaxLabel" runat="server" Text="Fax" />
<asp:TextBox ID="CustomerFaxValue" runat="server" />
</div>
<div>
<asp:Label ID="CustomerEmailLabel" runat="server" Text="Email" />
<asp:TextBox ID="CustomerEmailValue" runat="server" />
</div>
<div>
<asp:Label ID="CustomerWebLabel" runat="server" Text="Web" />
<asp:TextBox ID="CustomerWebValue" runat="server" />
</div>
</div>
</div>
<div class="update_button_field">
<asp:Button ID="UpdateContactDetailsButton" runat="server" Text="Update"
onclick="UpdateContactDetailsButton_Click" />
</div>
</ContentTemplate>
</asp:UpdatePanel>
Thanks in advance.
NOTE: To be able to understand the progress on this problem, please see the comments on this answer before replying.
UPDATE
I have implemented a temporary solution to this problem by putting a check in javascript that if the script is already executing then do not execute twice. Javascript is still being spitted multiple times on every partial postback. Couldn't prevent it.
As the views to this post are increasing, I can see that there are other people who might also want answer to this problem.
If you are using this;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "noPasswordMatch", script, true);
Then to check if it has been registered you must use this:
if (Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "noPasswordMatch"))
if (Page.ClientScript.IsClientScriptBlockRegistered("noPasswordMatch")) doesn't work!
I ran into this same issue when writing a composite control in ASP.Net. When the control was inside an update panel Page.ClientScript.IsStartupScriptRegistered didnt work. From within the method protected override void CreateChildControls() i was doing something like
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), initializeTokenInputScriptKey, initializeTokenInputScript, true);
Hence I ran into a situation similar to what you describe here. What solved my problem was passing the control and its type instead of the page and page type to ScriptManager.RegisterStartupScript. Hence the code now looks,
ScriptManager.RegisterStartupScript(this, this.GetType(), initializeTokenInputScriptKey, initializeTokenInputScript, true);
Once I did this change I no longer needed to check Page.ClientScript.IsStartupScriptRegistered. Now my control works with or without update panels. No unnecessary js spitouts either. Hope this helps
I had implemented a temporary solution to this problem by putting a check in javascript that if the script is already executing then do not execute twice. Javascript is still being spitted multiple times on every partial postback. Couldn't prevent it.
I've written an extension method to check whether the script has already been registered with the ScriptManager. You can use the same principle to check startup scripts:
public static bool IsClientScriptBlockRegistered(this ScriptManager sm, string key)
{
ReadOnlyCollection<RegisteredScript> scriptBlocks = sm.GetRegisteredClientScriptBlocks();
foreach (RegisteredScript rs in scriptBlocks)
{
if (rs.Key == key)
return true;
}
return false;
}
Keep in mind your first line of code is the inverse of the method return value because of the !.
if (!Page.ClientScript.IsStartupScriptRegistered(Page.GetType(), scriptKey))
If IsStartupScriptRegistered is returning false as you say, then the if statement should evaluate true because of the !. This should cause the script to be registered as expected.
Your code is based on my answer here, which was based on ASP.NET AJAX 1.0 and ASP.NET 2.0. It may have something to do with .NET 3.5, although I believe I've used the above already in a newer project we did under 3.5 and it worked fine...
Can you post some markup to go with the code?
EDIT: Thanks for posting the markup.
I notice 2 things now:
You mentioned you are using ToolkitScriptManager. It is a control that inherits from ScriptManager. I didn't notice this before, but your code is still referencing ScriptManager directly. You said it was during async postbacks that the script isn't working, which leads me to believe it is an issue with your reference to ScriptManager. I've never used ToolkitScriptManager before so I can't give you exact code, but I can tell you that you'll likely need to update your code-behind, changing all references to ScriptManager and its methods/properties to the equivalent in ToolkitScriptManager.
Try adding a breakpoint on the if statement and make sure it's evaluation to true. I wouldn't be surprised if scriptManager is null, or scriptManager.IsInAsyncPostBack is false because you're using ToolkitScriptManager.
ScriptManager scriptManager = ScriptManager.GetCurrent(page);
if (scriptManager != null && scriptManager.IsInAsyncPostBack)
{
//if a MS AJAX request, use the Scriptmanager class
ScriptManager.RegisterStartupScript(Page, Page.GetType(), scriptKey, script, true);
}
Lastly - Your markup looks okay, other than you don't need the <Triggers> section. Triggers allow you to specify control which are outside of your update panel to cause partial renders. Any child control of the update panel within the <ContentTemplate> section will do this automatically. The button you are targeting in the Triggers section is already within the updatepanel. While I don't think this is the cause of your issue, I'd remove it anyway.
Hope this helps.
Did you mean this: ClientScriptManager.IsStartupScriptRegistered Method
I have a legacy system where I want to improve the user experience.
On one of the forms there are 2 drop down listboxes; the change of one ddl cascades to the next.
On the current form, this is acheived by selectedindexchanged event from which the other ddl can be repopulated.
Although this works, in order for the event to be fired, the control has to have the autopostback=true property set.
This postback creates a bad user experience.
So the simple solution is to put the ddls into an update panel control. However doing this has the odd knock on effect of disabling my JQuery Cluetip controls. I do not know why this is.
In which case it is time I finally got round to populating my controls using JQuery and/or AJAX.
So I have tried the following solution below in a test application.
However when I click on the submit button, the changes I make to the Sub Categories ddl are not being picked up in the code behind event for my button (a server side control).
The reason appears to be that the client side updates happened after the last postback, and so on the server the control still has the previous values.
How do I pick up the new values on server side, or should I try something else?
I am using a server control button because that is what the orginal legacy code does, and changing everything to client side would be a big job.
The web page;
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<link href="CSS/RM.css" rel="stylesheet" type="text/css" />
<script type='text/javascript' src='http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js'></script>
<script type='text/javascript' src='Default.js'></script>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>Test Categories</h1>
<table>
<tr>
<th class="wid3">Category:</th>
<td class="wid4"><asp:DropDownList
ID="Categories_ddl" runat="server" DataSourceID="odsCategories"
DataTextField="Category1" DataValueField="Category_ID"
>
</asp:DropDownList>
</td>
<th class="wid3">Sub Category:</th>
<td class="wid4"><select
ID="Sub_Categories_ddl" runat="server" DataSourceID="odsSub_Categories_4_Category"
DataTextField="Sub_Category1" DataValueField="Sub_Category_Id">
</select></td>
</tr>
</table>
<asp:ObjectDataSource runat="server" ID="odsCategories"
TypeName="ClassLibraryRiskManCategories.DAL" SelectMethod="GetCategories" />
<asp:ObjectDataSource runat="server" ID="odsSub_Categories_4_Category"
TypeName="ClassLibraryRiskManCategories.DAL" SelectMethod="GetSubCategories">
<SelectParameters>
<asp:ControlParameter ControlID="Categories_ddl" Name="categoryId"
PropertyName="SelectedValue" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
<div style="padding:100px"><asp:Button runat="server" ID="btnTest" Text="Submit"
onclick="btnTest_Click" /></div>
</div>
</form>
</body>
</html>
The JScript
$(document).ready(function() {
$("#Categories_ddl").change(function() {
$("#Sub_Categories_ddl").html("");
var CategoryId = $("#Categories_ddl > option:selected").attr("value");
if (CategoryId != 0) {
$.getJSON('LoadSubCategories.ashx?CategoryId=' + CategoryId, function(Sub_Category1) {
$.each(Sub_Category1, function() {
$("#Sub_Categories_ddl").append($("<option></option>").val(this['Sub_Category_Id'].toString()).html(this['Sub_Category1']));
});
});
}
});
});
ASP.NET cannot really be told to commit any changes to a control between postbacks unless they are performed in a fashion that it expects (such as editing a textbox). Arbitrarily changing a DropDownList's options outside of a server event is one case of a non-trackable change (at least in any reasonable sense). Fortunately, using an UpdatePanel as you were before is a viable approach and correcting the issue you described with your tooltips is actually quite straightforward.
To address the issue of your jQuery Cluetips not appearing after an UpdatePanel postback, you will need to reinitialize the plugin just after the partial refresh as it is no longer registered to elements existing in the DOM (since your the partial refresh replaced those elements). You can achieve this by wrapping the plugin initializer in a javascript method called pageLoad():
function pageLoad() {
// do some javascript here
}
Any javascript method called pageLoad() will automatically be called by ASP.NET after every full and partial page load. With this technique you will be able to restore your jQuery Cluetips after you repopulate the DropDownLists from the UpdatePanel's postback. Take a look at this excellent article on the ASP.NET AJAX Client Life-Cycle Events for more information.