ScriptManager.RegisterClientScriptBlock registering scripts twice - asp.net

On the OnPreRender of my custom server control, I am registering an alert to popup everytime the page is loaded after a partial postback (add_pageLoaded(function() { alert('Hi')})). I only want this to be called once, but its gets called as many times as there has been partial postbacks. For example, in the code below, if you open the page and click "Click me" you get one alert, click again and you will get two alerts, three --> three alerts and so forth. My understanding was that ScriptManager.RegisterClientScriptBlock, using the scriptKey parameter was suppose to check if the script has already been registered, and if so, dont register it again.
I cannot register this script on !IsPostBack because my custom control will be inserted during Postbacks into pages, so using !IsPostBack will not include my script.
Here is a simple example of this problem:
ASPX:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="RegisterClientScriptExperimentation._Default" %>
<%# Register Assembly="RegisterClientScriptExperimentation" Namespace="RegisterClientScriptExperimentation" TagPrefix="cc" %>
<!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>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"/>
<asp:UpdatePanel runat="server">
<ContentTemplate>
<asp:Panel ID="panel" runat="server"/>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
namespace RegisterClientScriptExperimentation
{
public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
panel.Controls.Add(new CustomControl());
}
}
public class CustomControl : CompositeControl
{
private Button button;
public CustomControl()
{
button = new Button();
button.Text = "Click me";
}
protected override void CreateChildControls()
{
Controls.Clear();
Controls.Add(button);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
StringBuilder _text = new StringBuilder();
_text.Append("var prm = Sys.WebForms.PageRequestManager.getInstance();");
_text.Append("prm.add_pageLoaded(function() { alert('Page Loaded'); });");
if (null != System.Web.UI.ScriptManager.GetCurrent(Page) && System.Web.UI.ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
System.Web.UI.ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "customControlScript", _text.ToString(), true);
else
Page.ClientScript.RegisterStartupScript(this.GetType(), "customControlScript", _text.ToString(), true);
}
}
}

if(!Page.ClientScript.IsClientScriptBlockRegistered("customControlScript") && !Page.IsPostBack)
{
if (null != System.Web.UI.ScriptManager.GetCurrent(Page) && System.Web.UI.ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
System.Web.UI.ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "customControlScript", _text.ToString(), true);
else
Page.ClientScript.RegisterStartupScript(this.GetType(), "customControlScript", _text.ToString(), true);
}

I found the solution to this.
In your page events, after you execute whatever you need to do, you need to remove the function from the event which you hooked into.
For example:
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_pageLoaded(DoSomething);
function DoSomething() {
alert('Hello');
Sys.WebForms.PageRequestManager.getInstance().remove_pageLoaded(DoSomething);
}

Related

Using Nemiro OAuth library for Google authentication in Webforms

I am trying to use Nemiro library to authenticate against Google in a Webforms asp.net project. This library documentation is at Nemiro GoogleClient Documenation
I have a simple aspx page called ExternalLogin.aspx, whose markup and code-behind are as given below.
Question
With code that I have, when Login using Google button is clicked, then user does not get directed to Google's authorization page. What is missing in my code that is causing this?
Markup
<%# Page Language="C#" AutoEventWireup="true" CodeFile="ExternalLogin.aspx.cs" Inherits="ExternalLogin" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="btnGoogle" runat="server" Text="Login using Google" OnClick="btnGoogle_Click" />
</div>
</form>
</body>
</html>
Code-behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Nemiro;
using Nemiro.OAuth.Clients;
using Nemiro.OAuth;
public partial class ExternalLogin : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnGoogle_Click(object sender, EventArgs e)
{
var result = OAuthWeb.VerifyAuthorization();
if (result.IsSuccessfully)
{
var user = result.UserInfo;
Response.Write(String.Format("User ID: {0}<br />", user.UserId));
Response.Write(String.Format("Name: {0}<br />", user.DisplayName));
Response.Write(String.Format("Email: {0}", user.Email));
}
}
}
I have also defined the keys for Google OAuth in Application_Start event as below.
void Application_Start(object sender, EventArgs e)
{
Nemiro.OAuth.OAuthManager.RegisterClient(
new Nemiro.OAuth.Clients.GoogleClient(
"some-value-1",
"some-value-2"
));
}
I think you should be looking at OAuthWeb.RedirectToAuthorization method. Here's the API doc for your reference. So just call this method in your btnGoogle_Click, and then verify your authorization in Page_Load event handler.
Here's the sample code:
protected void btnGoogle_Click(object sender, EventArgs e)
{
OAuthWeb.RedirectToAuthorization("Google", new Uri(Request.Url, "ExternalLogin.aspx").AbsoluteUri);
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostback)
{
var result = OAuthWeb.VerifyAuthorization();
if (result.IsSuccessfully)
{
var user = result.UserInfo;
Response.Write(String.Format("User ID: {0}<br />", user.UserId));
Response.Write(String.Format("Name: {0}<br />", user.DisplayName));
Response.Write(String.Format("Email: {0}", user.Email));
}
}
}
Also, if you'd like to verify the authorization results on a different page, just change the page name in the URI constructor, and put verification code in the Page_Load event of your new page.
Hope it helps.

UseSubmitBehavior=False not working with OnClientClick when webform is based on a Master Page

I'm writing an admin page where I have a textbox for user name and a refresh button which shows user details when clicked. Among other controls, I have a 'remove user' button to delete the user. I have javascript code to get confirmation before attempting this.
Because I do not want this Remove User button to have submit behavior, I have set UseSubmitBehavior=False. However, this caused the server side event to not get fired. So I wrote a small client side function to explicitly call __doPostBack.
The final code works, but only on pages that are not based off of a master page or nested master pages. Sadly all my web pages are based off of master pages.
Wondering if I'm missing something or if there is a way around? I've just started asp.net so please excuse any obvious mistakes.
Many thanks in advance.
Code Sample:
Following code works (webform with no master page)
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm_with_NoMasterPage.aspx.cs" Inherits="WebApplication1.WebForm_with_NoMasterPage" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<script type="text/javascript">
function GetConfirmation(btn, msg) {
if (confirm(msg)) {
__doPostBack(btn.id, '');
return true;
}
else {
return false;
}
}
</script>
<asp:Button ID="btnRemoveUser" runat="server" Text="Remove User" OnClick="btnRemoveUser_Click"
OnClientClick="return GetConfirmation(btnRemoveUser, 'Really remove?');"
UseSubmitBehavior="false"
Height="37px" Width="230px" />
</div>
</form>
</body>
</html>
code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class WebForm_with_NoMasterPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnRemoveUser_Click(object sender, EventArgs e)
{
// put a breakpoint here and see if it is hit.
int x = 0;
}
}
}
Following does not work (same fragment but in a Webform based on masterpage):
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="WebForm_WITH_MasterPage.aspx.cs" Inherits="WebApplication1.WebForm_WITH_MasterPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript">
function GetConfirmation(btn, msg) {
if (confirm(msg)) {
__doPostBack(btn.id, '');
return true;
}
else {
return false;
}
}
</script>
<asp:Button ID="btnRemoveUser" runat="server" Text="Remove User" OnClick="btnRemoveUser_Click"
OnClientClick="return GetConfirmation(btnRemoveUser, 'Really remove?');"
UseSubmitBehavior="false"
Height="37px" Width="230px" />
</asp:Content>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class WebForm_WITH_MasterPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnRemoveUser_Click(object sender, EventArgs e)
{
// put a breakpoint here and see if it is hit
int z = 0;
}
}
}

Inconsistent behaviour for captcha

One of my forms has a captcha.
I am forced to use this captcha since there's a lot of code already written before me.
This way is simple:
There is an ashx file. An image will be created using that file. At the same time, that file will create a session with the same value from the image.
Upon submission, the code will check whether they are the same. If so, continue. if not, return.
Upon page refresh, the captcha will update.
Then I need to add in ajax update panel as the requirement.
Then the captcha is still working fine in chrome and safari but it is not refreshing when the page loads again in IE and firefox.
I created a simple page just for captcha
aspx
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default7.aspx.cs" Inherits="Default7" %>
<!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>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<img height="30" alt="" src="Handler.ashx" width="80"><br>
<asp:Button ID="btnSubmit" runat="server" Text="Submit"
onclick="btnSubmit_Click"/>
</ContentTemplate>
</asp:UpdatePanel>
<div>
</div>
</form>
</body>
</html>
aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Default7 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
}
}
handler.ashx
<%# WebHandler Language="C#" Class="Handler" %>
using System;
using System.Web;
using System.Web.SessionState;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
public class Handler : IHttpHandler,System.Web.SessionState.IRequiresSessionState {
public void ProcessRequest(HttpContext context)
{
Bitmap objBMP = new System.Drawing.Bitmap(60, 20);
Graphics objGraphics = System.Drawing.Graphics.FromImage(objBMP);
//objGraphics.Clear(Color.Blue);
objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
//' Configure font to use for text
Font objFont = new Font("Arial", 8, FontStyle.Bold);
string randomStr = "";
int[] myIntArray = new int[5];
int x;
//That is to create the random # and add it to our string
Random autoRand = new Random();
for (x = 0; x < 5; x++)
{
myIntArray[x] = System.Convert.ToInt32(autoRand.Next(0, 9));
randomStr += (myIntArray[x].ToString());
}
randomStr = GetRandomString();
//This is to add the string to session cookie, to be compared later
context.Session.Add("randomStr", randomStr);
//' Write out the text
objGraphics.DrawString(randomStr, objFont, Brushes.White, 3, 3);
//' Set the content type and return the image
context.Response.ContentType = "image/GIF";
objBMP.Save(context.Response.OutputStream, ImageFormat.Gif);
objFont.Dispose();
objGraphics.Dispose();
objBMP.Dispose();
}
public bool IsReusable
{
get
{
return false;
}
}
private string GetRandomString()
{
string[] arrStr = "A,B,C,D,1,2,3,4,5,6,7,8,9,0".Split(",".ToCharArray());
string strDraw = string.Empty;
Random r = new Random();
for (int i = 0; i < 5; i++)
{
strDraw += arrStr[r.Next(0, arrStr.Length - 1)];
}
return strDraw;
}
}
Any idea?
I got the answer now.
change image control to server control.
and in code behind change the image source with current date time
<img height="30" alt="" src="/Handler.ashx" width="80" runat="server" id="imgCaptcha">
imgCaptcha.Src = "Handler.ashx?dt=" + DateTime.Now.ToString();
change image control to server control.
and in code behind change the image source with current date time
<img height="30" alt="" src="/Handler.ashx" width="80" runat="server" id="imgCaptcha">
imgCaptcha.Src = "Handler.ashx?dt=" + DateTime.Now.ToString();

How to use control state in asp.net

Below is my simple code to use control state in a custom control,
[DefaultProperty("Text")]
[ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]
public class WebCustomControl1 : WebControl
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get { return text; }
set { text = value; }
}
private string text;
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
protected override void OnInit(System.EventArgs e)
{
base.OnInit(e);
Page.RequiresControlState(this);
}
protected override object SaveControlState()
{
object baseSate = base.SaveControlState();
return new Pair(baseSate, Text);
}
protected override void LoadControlState(object savedState)
{
Pair value = savedState as Pair;
text = value.Second;
}
}
But it doesn't seem to work.. The SaveControlState and LoadControlState are not firing. can someone help me..?
Below is the aspx Code. Here is where i use the custom control.
<%# Page Language="C#" AutoEventWireup="true" EnableViewState="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
<%# Register Assembly="WebApplication1" Namespace="WebApplication1" TagPrefix="cc1" %>
<!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>Untitled Page</title>
</head>`enter code here`
<body>
<form id="form1" runat="server">
<div>
<cc1:WebCustomControl1 ID="WebCustomControl1_1" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Button" /></div>
</form>
</body>
</html>
You've called RequiresControlState
Determines whether the specified Control object is registered to participate in control state management.`
But you should call RegisterRequiresControlState
Registers a control as one whose control state must be persisted.

FindControl for nested controls in UserControl returns null

I have a very weird issue. I have a UserControl that has some controls inside. I want to refer those controls after, in another postback. But when I try to get them the Controls property of my controls returns null.
I'm working on vs2008.
Here is the sample code:
public partial class MyUserControl : System.Web.UI.UserControl, INamingContainer
{
protected void Page_Load(object sender, EventArgs e)
{
foreach (Control control in this.Controls)
{
Response.Write(control.ClientID);
}
}
private void MyTable()
{
Table table = new Table();
TableRow row = new TableRow();
TableCell cell = new TableCell();
CheckBox check = new CheckBox();
check.ID = "theId";
check.Text = "My Check";
check.AutoPostBack = true;
cell.Controls.Add(check);
row.Cells.Add(cell);
check = new CheckBox();
check.ID = "theOther";
check.AutoPostBack = true;
check.Text = "My Other Check";
cell = new TableCell();
cell.Controls.Add(check);
row.Cells.Add(cell);
table.Rows.Add(row);
this.Controls.Add(table);
}
protected override void Render(HtmlTextWriter writer)
{
MyTable();
base.Render(writer);
}
}
and the Default.aspx page is something like:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.cs" Inherits="Tester.Default" %>
<%# Register TagPrefix="uc1" TagName="MyControl" Src="~/MyUserControl.ascx" %>
<!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>Unbenannte Seite</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:MyControl ID="MyControlInstance" runat="server" />
</div>
</form>
</body>
</html>
I don't know if I'm lost in some part of the ASP.NET life cycle. But this situation is making me crazy. Any help would be very grateful.
Create your child controls (MyTable) in either CreateChildControls or OnInit:
protected override void CreateChildControls()
{
MyTable();
base.CreateChildControls();
}
Or
protected override void OnInit(object sender, EventArgs e)
{
MyTable();
base.OnInit(e);
}
You shouldn't/cannot create controls in Render as it occurs after Page_Load. See the ASP.Net Page Lifecycle here.
I believe it is because the Render event occurs after Page_Load, so when you are trying to iterate your control collection, it hasn't been set up yet. Most common solution is to override CreateChildControls to get the proper timing down.

Resources