Execute javascript code at the end of event handler - c#

I'm building a web application on ASP.NET with C#.
On a button click I show a loading image, while the database query is being executed. Then I dynamically create an Excel file and send it to the client like this:
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=" + filename + ".xlsx");
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.Unicode;
HttpContext.Current.Response.BinaryWrite(p.GetAsByteArray());
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
I get the dialog box, and the loading image stays there.
I've tried placing call to a javascript function (with ClientScript.RegisterStartupScript function) before the code above, it didn't work. As I understand all javascript code is run after all the code-behind has executed, but in this case it doesn't execute at all once the file is sent to the client.
I've also tried creating a separate thread, and removing the loading image there. I put the breakpoint to trace it, code in the thread does execute, but the image still stays there.
Does anyone have an idea how this can be handled? Thank you!

You can only send or transmit 1 mime type in one request/response cycle. (My knowledge in this area is debatable).
That said, you can design a hack around this. Use an iframe on the client to "download the file". You can point its src to an ashx file that does the same.
You need to wire the iframe's onload event, so your web page has someway of knowing that download is done; thats where you can execute your logic.
Solution Update:
Well, after digging around, I've discovered my answer is half-baked!
The issue is that iframes don't trigger their onload event after they download something. The onload event will trigger iff there the url pointed to by src actually navigates to a different page. This is by design I suppose. And I learn that today!
So what then is the work-around?!
Fortunately, you can transmit cookies to the client. On the client your web page has to keep polling for the presence of this cookie. So once your web page is able to detect the presence of the cookie, it means that the browser has completed with the download request. This has been discussed in great detail in the following post:
http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx
I'll just show you some code relating to the handler file (which simulates a download), and the client (which has an iframe doing the job). This should pretty much give you the gist:
Webform1.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApp.FileDownload.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>iFrame Download</title>
<script type="text/javascript" src="Scripts/jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="Scripts/jquery.cookie.js"></script>
<script type="text/javascript">
function foo() {
console.log('foo');
//execute post-download logic here
}
$(function () {
$('input').click(function () {
//make sure we get rid of the
//cookie before download
$.removeCookie('downloaded');
var intrvl = setTimeout(function () { //this function polls for the cookie through which we track that the file has indeed been downloaded
console.log('timer');
var value = $.cookie('downloaded');
if (value == 'true') {
clearTimeout(intrvl);
foo();
}
}, 1000);
//this initiates the download
$('iframe').attr({
'src': 'download.ashx?id=' + $('#tbxRandomNumber').val()
});
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="tbxRandomNumber" runat="server"></asp:TextBox>
<input type="button" value="Download" />
<iframe src="about:blank" style="display:none"></iframe>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Next Random Number" />
</div>
</form>
</body>
</html>
I've made used of jquery cookies plugin to help me with handling cookies.
download.ashx:
using System;
using System.Web;
namespace WebApp.FileDownload
{
/// <summary>
/// Summary description for download
/// </summary>
public class download : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.SetCookie(new HttpCookie("downloaded","true")); //setting cookie in the response
string id = context.Request.QueryString["id"] == null ? "NULL" : context.Request.QueryString["id"];
string str = string.Format("Content with id {0} was generated at {1}", id, DateTime.Now.ToLongTimeString());
context.Response.AddHeader("Content-Disposition", "attachment; filename=test.txt");
context.Response.AddHeader("Content-Length", str.Length.ToString());
context.Response.Write(str);
context.Response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
}
}

It looks like you have a couple of misunderstandings here. You only have one request, and one response from the server. Making new threads is something that only happens on the server, and won't create additional responses.
When you're sending the Excel file, you're using:
HttpContext.Current.Response.Clear();
By clearing the response, you're losing the JavaScript that you added previously. It will never get to the client.
If the processing is fairly trivial (always just a couple of seconds), I'd just set the loading animation to run for a couple of seconds and stop, by setting a timeout on the initial onclick event. It's not perfect, but it'll give the user some immediate feedback.
If the processing is going to take a long or very variable amount of time, then the animation is more important to get right. You can try loading your Excel file in a hidden <iframe>, and attaching an onload event to remove the loading animation.
You would need to create a separate page to handle generating the Excel file, rather than doing it in a server-side OnClick handler. However, I seem to remember that support for onload events on <iframe> can be spotty with older IE versions.

javascript run in the client when page is loading in the browser. You may have a hidden textbox, at the end of you event yo can put a value into that textbox:
txtHidden.Text = "Hola Mundo"
You have to check the value on page load:
<script type="text/javascript">
$(document).ready(function(){
if($("#txtHidden").length > 0 && $("#txtHidden").val() != '')
{
alert($("#txtHidden").val());
}
});
</script>
You can put this in a web user control.
Another solution:
<div class='button' id='btnGenerateDownload' onClick='GenerateDownload(this)'>
Click here <div id='loadingImage' class='loadingImage'></div>
</div>
JQuery:
function GenerateDownload(caller)
{
//add loading gif:
var $loagingGIF = $(caller).children('#loadingImage').eq(0);
$loagingGIF.addClass('loadingImage');
var fileGeneratorUrl = 'ghFileGenerator.ashx';
var downloadHandlerUrl = 'ghDownloadHandler.ashx';
$.post({data: "File1"}, function(response){
//remove gif
$loagingGIF.removeClass('loadingImage');
if(response != '') //file key
{
downloadHandlerUrl += '?key=' + response;
var $link = $("<a />").attr('href', downloadHandlerUrl).html('download');
$link.appendTo($(caller));
}
});
}
css:
.loadingImage{background: transparent url(images/loading.gif);}
.ashx:
string filekey = context.Current.Request.Form("key");

Related

Update UI from code behind in real time

I've created a socket listener, and I need to display a div (keep it hidden, then make it visible), when the server detects a certain socket data.
I've tried to use a thread, but it doesn't update the UI in realtime, only if the page is reloaded or if you do a post back.
Here is an example of what I want to do, in this case I only want to update a textbox with codebehind data, I would like to do it without ajax, javascript or jQuery, if possible.
Code Behind:
protected void Page_Load(object sender, EventArgs e)
{
Thread t = new Thread(TestThread);
t.Start();
}
private void TestThread()
{
for (int i = 0; i <= 1000000000; i++)
{
myTextbox.Text += "1";
}
}
Webpage:
<asp:UpdatePanel runat="server" ID="myPanel" >
<ContentTemplate>
<asp:TextBox runat="server" ID="myTextbox" />
</ContentTemplate>
</asp:UpdatePanel>
what you are trying to do is change the UI (modify client components) from server code (code behind) in asp.net when the server already sent the data to the client.
you have to use client side scripting/coding.
As far as I know, this is not possible without using ajax or JavaScript.
ASP.NET is only responsible for generating the html that is sent from your web-server to the client. Once that html is rendered in the browser, the only way to update the html is via JavaScript.
You can do a Partial Render in WebForms using their AJAX handler without having to write jQuery by enabling a partialRenderingEnabled attribute in the ScriptManager tag. There's no way to do this without scripting or AJAX, the content has to get back to the server to run your code-behind somehow, but at least this way you don't have to write any JavaScript yourself.
For real-time web functionality check out :
SignalR
In a web enviornment, there is no way for the server side to reach out for the client except in the request-response scenario, where the client has to send a request to the server side, the server side handles the request and replies with a response.
EDIT :
SignalR uses the best available technique, websockets when available, if not, AJAX long polling technique in which the client polls(sends a request) to the server, and the server replies only when a certain change to the observed data happens, else SignalR tries repetitve AJAX requests, polling the server over and over until the server replies with the change on the observed data (Worst scenario !).
Also, HTML 5 contains web-sockets, i don't have enough information about it, but it maybe interesting to check out.
With minimal JavaScript you could utilise Server Sent Events for simplex data (from server to client) It doesn't have the overhead of Websockets which has a bit of cost in terms of establishing a connection. However I don't think all browsers are supported but there might be polyfill available for it (library to provide functionality in absence of native support)
Checkout:
http://www.html5rocks.com/en/tutorials/eventsource/basics/
In ASP.NET Threads gets aborted as soon as the page unloads, so this is not possibler you way, sorry.
Your approach could be using AJAX to poll the status from the server on regular intervals. Server on the other hand, will reply according to change in data.
in your ASP.NET Page, add the following script
<head>
<script type="text/javascript" language="javascript">
function poll()
{
var ajax;
if(window.XMLHttpRequest)
ajax = new XMLHttpRequest();
else
ajax = new ActiveXObject("Microsoft.XMLHTTP");
url = "yourPageOrHandler.aspx";
ajax.onreadystatechange = function()
{
if (ajax.readyState == 4 && ajax.status == 200)
{
// success
var receivedText = ajax.responseText;
}
else
{
// error, do the needful
}
}
ajax.open("GET", url, true);
ajax.send();
setTimeout("poll()", 1000); //polls every 1 second, you can change the duration here.
}
</script>
</head>
Start the polling before page finishes
<body>
.
.
.
.
<script type="text/javascript" language="javascript">
poll();
</script>
</body>
Alternatively, you can use the ASP.NET AJAX controls, but that will be heavier compared to JavaScript AJAX.
Glad to help! Please remember to accept the answer if you found it helpful.

Inconsistent LinkButton PageMethod behavior in different browsers

I have a LinkButton on a page that performs a post-back, but also has an onClientClick event. The idea is to set some session variables in the background from client-side data (don't ask).
I've placed a break point in the web method to step through our code, and what we're experiencing is that depending on the browser, the PageMethods may return a success message, failure message, or no message at all. Additionally, the web method may or may not get called, regardless of the PageMethods result.
Here's a handy little chart of our results:
Browser PageMethods WebMethod
-------------- ------------- --------------------
IE 8, 9, 10 Success Called successfully
Safari 5.1.7 Failure *Never called*
Firefox 25.0.1 *Neither* Called successfully
Chrome v31 Failure Called successfully
That's four different browsers, and four different results.
I've tried generating the link button in both server-side and client-side code with the same effect, and without even setting the session variables in the WebMethod, with the same results.
The code can be reproduced with the following simple code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script type="text/javascript">
function doStuff() {
var a = 'a';
var b = 'b';
PageMethods.doStuffWebMethod(a, b, doStuffSuccess, doStuffFail);
}
function doStuffSuccess() {
alert(Success!');
}
function doStuffFail() {
alert(Failure!');
}
</script>
<html>
<body style="background-color:#f3f4f6;" >
<form runat="server" name="mainForm" id="mainForm" action="Test.aspx">
<asp:ScriptManager ID="ScriptManager1" EnablePageMethods="true" runat="server"></asp:ScriptManager>
<asp:LinkButton runat="server" CausesValidation="false" OnClientClick="doStuff();">Do stuff!</asp:LinkButton>
</form>
</body>
</html>
and
protected void Page_Load(object sender, EventArgs e)
{
LinkButton lbAdd = new LinkButton();
lbAdd.Text = "Web method test";
lbAdd.CausesValidation = false;
lbAdd.OnClientClick = "doStuff();";
mainForm.Controls.Add(lbAdd);
}
[WebMethod]
public static void doStuffWebMethod(string a, string b)
{
try
{
//System.Web.HttpContext.Current.Session["a"] = a;
//System.Web.HttpContext.Current.Session["b"] = b;
string x = a + b;
}
catch (Exception ex)
{
//
}
}
The question:
Why is my web method failing in Safari, and giving me one of three different return messages in three other browsers?
How can I change this code to get it to work in the browsers mentioned?
The reason the call to your web method is failing is because
your are not canceling the postback of your LinkButton.
You have to return false from the OnClientClick event to cancel
the postback. The code below should fix it:
function doStuff() {
var a = 'a';
var b = 'b';
PageMethods.doStuffWebMethod(a, b, doStuffSuccess, doStuffFail);
return false; // Cancel postback.
}
function doStuffSuccess() {
alert('Success!');
}
function doStuffFail() {
alert('Failure!');
}
<asp:LinkButton ID="mybutton" runat="server" CausesValidation="false" OnClientClick="return doStuff();">Do stuff!</asp:LinkButton>
For a more sophisticated solution for canceling the default behaviour of a browser (postback) please have a look on the following stackoverflow question and answer.
The reason you are getting those different results for the different browsers is maybe due to different implementations of the browser vendors. But I am not sure about this.
You can also verify this behaviour by creating a network protocol trace
(press F12 in Google Chrome browser and switch to the network tab).
The protocol in case you do not return false from the doStuff() method:
The page method doStuffWebMethod is called. Then you get the JavaScript message box. (HTTP POST)
Your WebForm.aspx is requested. (HTTP POST)
Then WebResource.axd and ScriptResource.axd is requested. (HTTP GET)
Number 2. and 3. shows that a postback is executed after the request of your page method.
The protocol in case you do return false from the doStuff() method:
Only the page method doStuffWebMethod is called. (HTTP POST)
The second trace clearly shows that there is no postback executed.
If you want the postback to happen for your LinkButton then you could manually trigger the postback in the JavaScript success handler using the __doPostBack() method after the page method comes back:
function doStuffSuccess() {
alert('Success!');
__doPostBack('<%= mybutton.UniqueID %>', ''); // trigger the postback.
}

Internet Explorer 8 now running code showing in view source

So I have a server control that is at the bottom of my page:
<%= addPopup() %>
Here is the code for it:
protected String addPopup()
{
if (usedSearch == false)
{
return "";
}
else
{
return "<body id=\"test\" onload=\"popup.show()\" runat=\"server\">";
}
}
Basically, depending on what the user does, usedSearch will be true or false, resulting in the HTML line being added to the page:
<body onload="popup.show()" runat="server">
What this does is show a popup to the user, I do this so that it shows on postback when needed.
This works in all major modern browsers. It does not work in Internet Explorer 8 even though it is showing up in the code behind for every browser including IE8. How do I get IE8 to show the popup when is part of the page source?
You are rendering duplicated body tag, probably that's the reason why IE does not pick it up. You should rather output javascript snippet at the bottom of your page, before </body> tag:
<script type="text/javascript">
if (window.addEventListener) {
window.addEventListener('load', popup.show, false);
}
else if (window.attachEvent) {
window.attachEvent('onload', popup.show );
}
</script>
It would attach your function to onLoad event in all browsers.
P.S. Just make sure that your popup.show function is initialized before this code.

dynamic Javascript in asp.net

well. The problem i'm facing is i have my own custom control and i do a query , get records and dynamically add html controls to the page based on the data.
Now there's the problem of adding some dynamic javascript
I do this with the help of literal controls.
<asp:Literal runat="server" ID="latEventToolTipJqueryScripts"></asp:Literal>
This works like a charm
<script language="javascript" type="text/javascript">
// <![CDATA[
Sys.Application.add_load(WireEvents_<%=this.ID%>); // fix wiring for .NET ajax updatepanel
$(WireEvents_<%=this.ID%>); // handle page load wiring
function WireEvents_<%=this.ID%>() {
<asp:Literal runat="server" ID="latEventToolTipJqueryScripts"></asp:Literal>
}
// ]]>
</script>
I add the literal text dynamically from code behind.
However, when placing the control in an updatepanel, the postbacks don't update the script.
EDIT: The Sys.Application.add_load rewires the necessary functions just fine with the updatepanel. The problem is that the script that needs to be in place of the literal, doesn't update when in an updatepanel.
I've tried the ClientScript.RegisterStartupScript but it has the same effect as with the literal control trick. Any help?
---------------------------SOLVED (tnx to Pranay Rana)----------------------------------
Got rid of the literal in the ascx side. as well as the Sys.Application.add_load
now it's only in the code behind. The thing that was throwing me off was the JQuery thing.
this.strBuilderJS.Append( "<script language='javascript' type='text/javascript'>" +
"$(WireEvents_" + this.ID + ");" +
"function WireEvents_" + this.ID + "(){"+
" alert('stuff');");
this.strBuilderJS.Append( "}</script>");
and then
ScriptManager.RegisterStartupScript(this, this.GetType(), "strBuilderJS", strBuilderJS.ToString(), false);
Make use of ScriptManager.RegisterStartupScript() to register your script...may resolve problem ...
Check this resolve your problem : Add JavaScript programmatically using RegisterStartupScript during an Asynchronous postback
I would recommend on a different approach. To create a dynamic javascript for any language. I would create a reference to a dynamic file. For this example I will use .NET
Create a test.aspx page and add this script:
<script type="text/javscript" src="js/foo.aspx"></script>
Make sure your page response with the right content type. So for this instance I would add this on page load code behind for your foo.aspx:
Response.ContentType = "application/javascript";
On the foo.aspx html view add your javascript code.
alert("<%=DateTime.Now%>");
Browse your test.aspx page and you should see an alert with the current date
You should be able to move forward from here. The goal is to separate the javascript file from the static page.
Happy Coding.
How to change text of ASP Button control on anchor tag click
We have an ASP Button below and we want to change text when WE click on anchor tag
<asp:Button ID="btn_Add" runat="server" CssClass="button2" onclick="btn_Add_Click"/>
We have following two anchor tags
Add New
Add New
Now we add following code in script tag
<script type="text/javascript" language="javascript">
function changeText1() {
document.getElementById("lbl_header").innerText = 'Add New Teacher';
document.getElementById("btn_Add").value = 'Add';
}
</script>
<script type="text/javascript" language="javascript">
function changeText2() {
document.getElementById("lbl_header").innerText = 'Delete Teacher';
document.getElementById("btn_Add").value = 'Delete';
}
</script>

Preventing an ASP.NET Session Timeout when using Silverlight

I'm writing a program which has both an ASP.NET configuration system and a Silverlight application. Most users will remain on the Silverlight page and not visit the ASP.NET site except for logging in, etc.
The problem is, I need the session to remain active for authentication purposes, but the session will timeout even if the user is using the features of the silverlight app.
Any ideas?
On the page hosting the silverlight control, you could setup a javascript timer and do an ajax call to an Http Handler (.ashx) every 5 minutes to keep the session alive. Be sure to have your Handler class implement IRequiresSessionState.
I recommend the Handler because it is easier to control the response text that is returned, and it is more lightweight then an aspx page.
You will also need to set the response cache properly to make sure that the browser makes the ajax call each time.
UPDATE
Here is the sample code for an HttpHandler
public class Ping : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.ContentType = "text/plain";
context.Response.Write("OK");
}
public bool IsReusable
{
get { return true; }
}
}
Then if you use jQuery, you can put this on your host aspx page
setInterval(ping, 5000);
function ping() {
$.get('/Ping.ashx');
}
The interval is in milliseconds, so my sample will ping every 5 seconds, you probably want that to be a larger number. Fiddler is a great tool for debugging ajax calls, if you don't use it, start.
I've actually found a pretty cool hack which essentially embeds an iframe on the same page as the silverlight application. The iframe contains an aspx webpage which refreshes itself every (Session.Timeout - 1) minutes. This keeps the session alive for however long the silverlight app is open.
To do this:
Create an asp.net page called "KeepAlive.aspx". In the head section of that page, add this:
<meta id="MetaRefresh" http-equiv="refresh" content="18000;url=KeepAlive.aspx" runat="server" />
<script language="javascript" type="text/javascript">
window.status = "<%= WindowStatusText%>";
</script>
In the code behind file, add this:
protected string WindowStatusText = "";
protected void Page_Load(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
// Refresh this page 60 seconds before session timeout, effectively resetting the session timeout counter.
MetaRefresh.Attributes["content"] = Convert.ToString((Session.Timeout * 60) - 60) + ";url=KeepAlive.aspx?q=" + DateTime.Now.Ticks;
WindowStatusText = "Last refresh " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
}
}
Now, on the same page as the silverlight app, add this:
<iframe id="KeepAliveFrame" src="KeepAlive.aspx" frameborder="0" width="0" height="0" runat="server" />
Now the asp.net session will remain active while the silverlight app is being used!
The ajax ping / HttpHandler approach is good, but the JQuery $.get function is expecting a json result and throws a javascript parse error.
I modified the Ping HttpHandler to return "{}" instead of "OK" and this worked better.

Categories

Resources