Asp.net Custom user control button. How to stop multiple clicks by user.
- by Laurence Burke
I am trying to modify an open source Forum called YetAnotherForum.net in the project they have a custom user control called Yaf:ThemeButton. Now its rendered as an anchor with an onclick method in this code
ThemeButton.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace YAF.Controls
{
/// <summary>
/// The theme button.
/// </summary>
public class ThemeButton : BaseControl, IPostBackEventHandler
{
/// <summary>
/// The _click event.
/// </summary>
protected static object _clickEvent = new object();
/// <summary>
/// The _command event.
/// </summary>
protected static object _commandEvent = new object();
/// <summary>
/// The _attribute collection.
/// </summary>
protected AttributeCollection _attributeCollection;
/// <summary>
/// The _localized label.
/// </summary>
protected LocalizedLabel _localizedLabel = new LocalizedLabel();
/// <summary>
/// The _theme image.
/// </summary>
protected ThemeImage _themeImage = new ThemeImage();
/// <summary>
/// Initializes a new instance of the <see cref="ThemeButton"/> class.
/// </summary>
public ThemeButton()
: base()
{
Load += new EventHandler(ThemeButton_Load);
this._attributeCollection = new AttributeCollection(ViewState);
}
/// <summary>
/// ThemePage for the optional button image
/// </summary>
public string ImageThemePage
{
get
{
return this._themeImage.ThemePage;
}
set
{
this._themeImage.ThemePage = value;
}
}
/// <summary>
/// ThemeTag for the optional button image
/// </summary>
public string ImageThemeTag
{
get
{
return this._themeImage.ThemeTag;
}
set
{
this._themeImage.ThemeTag = value;
}
}
/// <summary>
/// Localized Page for the optional button text
/// </summary>
public string TextLocalizedPage
{
get
{
return this._localizedLabel.LocalizedPage;
}
set
{
this._localizedLabel.LocalizedPage = value;
}
}
/// <summary>
/// Localized Tag for the optional button text
/// </summary>
public string TextLocalizedTag
{
get
{
return this._localizedLabel.LocalizedTag;
}
set
{
this._localizedLabel.LocalizedTag = value;
}
}
/// <summary>
/// Defaults to "yafcssbutton"
/// </summary>
public string CssClass
{
get
{
return (ViewState["CssClass"] != null) ? ViewState["CssClass"] as string : "yafcssbutton";
}
set
{
ViewState["CssClass"] = value;
}
}
/// <summary>
/// Setting the link property will make this control non-postback.
/// </summary>
public string NavigateUrl
{
get
{
return (ViewState["NavigateUrl"] != null) ? ViewState["NavigateUrl"] as string : string.Empty;
}
set
{
ViewState["NavigateUrl"] = value;
}
}
/// <summary>
/// Localized Page for the optional link description (title)
/// </summary>
public string TitleLocalizedPage
{
get
{
return (ViewState["TitleLocalizedPage"] != null) ? ViewState["TitleLocalizedPage"] as string : "BUTTON";
}
set
{
ViewState["TitleLocalizedPage"] = value;
}
}
/// <summary>
/// Localized Tag for the optional link description (title)
/// </summary>
public string TitleLocalizedTag
{
get
{
return (ViewState["TitleLocalizedTag"] != null) ? ViewState["TitleLocalizedTag"] as string : string.Empty;
}
set
{
ViewState["TitleLocalizedTag"] = value;
}
}
/// <summary>
/// Non-localized Title for optional link description
/// </summary>
public string TitleNonLocalized
{
get
{
return (ViewState["TitleNonLocalized"] != null) ? ViewState["TitleNonLocalized"] as string : string.Empty;
}
set
{
ViewState["TitleNonLocalized"] = value;
}
}
/// <summary>
/// Gets Attributes.
/// </summary>
public AttributeCollection Attributes
{
get
{
return this._attributeCollection;
}
}
/// <summary>
/// Gets or sets CommandName.
/// </summary>
public string CommandName
{
get
{
if (ViewState["commandName"] != null)
{
return ViewState["commandName"].ToString();
}
return null;
}
set
{
ViewState["commandName"] = value;
}
}
/// <summary>
/// Gets or sets CommandArgument.
/// </summary>
public string CommandArgument
{
get
{
if (ViewState["commandArgument"] != null)
{
return ViewState["commandArgument"].ToString();
}
return null;
}
set
{
ViewState["commandArgument"] = value;
}
}
#region IPostBackEventHandler Members
/// <summary>
/// The i post back event handler. raise post back event.
/// </summary>
/// <param name="eventArgument">
/// The event argument.
/// </param>
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
OnCommand(new CommandEventArgs(CommandName, CommandArgument));
OnClick(EventArgs.Empty);
}
#endregion
/// <summary>
/// Setup the controls before render
/// </summary>
/// <param name="sender">
/// </param>
/// <param name="e">
/// </param>
private void ThemeButton_Load(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(this._themeImage.ThemeTag))
{
// add the theme image...
Controls.Add(this._themeImage);
}
// render the text if available
if (!String.IsNullOrEmpty(this._localizedLabel.LocalizedTag))
{
Controls.Add(this._localizedLabel);
}
}
/// <summary>
/// The render.
/// </summary>
/// <param name="output">
/// The output.
/// </param>
protected override void Render(HtmlTextWriter output)
{
// get the title...
string title = GetLocalizedTitle();
output.BeginRender();
output.WriteBeginTag("a");
output.WriteAttribute("id", ClientID);
if (!String.IsNullOrEmpty(CssClass))
{
output.WriteAttribute("class", CssClass);
}
if (!String.IsNullOrEmpty(title))
{
output.WriteAttribute("title", title);
}
else if (!String.IsNullOrEmpty(TitleNonLocalized))
{
output.WriteAttribute("title", TitleNonLocalized);
}
if (!String.IsNullOrEmpty(NavigateUrl))
{
output.WriteAttribute("href", NavigateUrl.Replace("&", "&"));
}
else
{
// string.Format("javascript:__doPostBack('{0}','{1}')",this.ClientID,""));
output.WriteAttribute("href", Page.ClientScript.GetPostBackClientHyperlink(this, string.Empty));
}
bool wroteOnClick = false;
// handle additional attributes (if any)
if (this._attributeCollection.Count > 0)
{
// add attributes...
foreach (string key in this._attributeCollection.Keys)
{
// get the attribute and write it...
if (key.ToLower() == "onclick")
{
// special handling... add to it...
output.WriteAttribute(key, string.Format("{0};{1}", this._attributeCollection[key], "this.blur();this.display='none';"));
wroteOnClick = true;
}
else if (key.ToLower().StartsWith("on") || key.ToLower() == "rel" || key.ToLower() == "target")
{
// only write javascript attributes -- and a few other attributes...
output.WriteAttribute(key, this._attributeCollection[key]);
}
}
}
// IE fix
if (!wroteOnClick)
{
output.WriteAttribute("onclick", "this.blur();this.style.display='none';");
}
output.Write(HtmlTextWriter.TagRightChar);
output.WriteBeginTag("span");
output.Write(HtmlTextWriter.TagRightChar);
// render the optional controls (if any)
base.Render(output);
output.WriteEndTag("span");
output.WriteEndTag("a");
output.EndRender();
}
/// <summary>
/// The get localized title.
/// </summary>
/// <returns>
/// The get localized title.
/// </returns>
protected string GetLocalizedTitle()
{
if (Site != null && Site.DesignMode == true && !String.IsNullOrEmpty(TitleLocalizedTag))
{
return String.Format("[TITLE:{0}]", TitleLocalizedTag);
}
else if (!String.IsNullOrEmpty(TitleLocalizedPage) && !String.IsNullOrEmpty(TitleLocalizedTag))
{
return PageContext.Localization.GetText(TitleLocalizedPage, TitleLocalizedTag);
}
else if (!String.IsNullOrEmpty(TitleLocalizedTag))
{
return PageContext.Localization.GetText(TitleLocalizedTag);
}
return null;
}
/// <summary>
/// The on click.
/// </summary>
/// <param name="e">
/// The e.
/// </param>
protected virtual void OnClick(EventArgs e)
{
var handler = (EventHandler) Events[_clickEvent];
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// The on command.
/// </summary>
/// <param name="e">
/// The e.
/// </param>
protected virtual void OnCommand(CommandEventArgs e)
{
var handler = (CommandEventHandler) Events[_commandEvent];
if (handler != null)
{
handler(this, e);
}
RaiseBubbleEvent(this, e);
}
/// <summary>
/// The click.
/// </summary>
public event EventHandler Click
{
add
{
Events.AddHandler(_clickEvent, value);
}
remove
{
Events.RemoveHandler(_clickEvent, value);
}
}
/// <summary>
/// The command.
/// </summary>
public event CommandEventHandler Command
{
add
{
Events.AddHandler(_commandEvent, value);
}
remove
{
Events.RemoveHandler(_commandEvent, value);
}
}
}
}
now that is just cs file its handled like this in the .ascx page of the actual website
<YAF:ThemeButton ID="Save" runat="server" CssClass="yafcssbigbutton leftItem" TextLocalizedTag="SAVE"
OnClick="Save_Click" />
now it is given an OnClick codebehind function that does some serverside function like this
protected void Save_Click(object sender, EventArgs e)
{
//some serverside code here
}
now I have a problem with the user being able to click multiple times and firing that serverside function multiple times. I have added in the code as of right now an extra onclick="this.style.display='none'" in the .cs code but that is a ugly fix I was wondering if anyone would have a better idea of disabling the ThemeButton clientside?? pls any feedback if I need to give more examples or further explain the question thanks.