Client Side Events, A Kick @#$% Solution
After reading a recent blog post by Ted Neward, I was once again reminded of why ASP.NET's class library sucks when it comes to client side events. Apparently, the ASP.NET team thinks everyone should code client side scripts in JavaScript. While that is definately one way to do it, that solution is a piece of crap, because no one actually does this. However, 90% of the time, reacting to client-side events on the client-side (what a concept...) can add a ton to your UI. Users should have to wait 3-5 seconds every time they click a button just so that you can refresh the UI a bit.
So, rather than complain about how brain-dead the ASP.NET team tends to be sometimes (and I must emphasize the sometimes part, because overall the do produce some awesome stuff), I offer a solution. Keep in mind, this was all concieved, designed, and implemented in the last hour or two, so it is just a start, but IMO, it is a pretty darn good one and sure beats the hell out of any solution I have seen so far. I will definately be integrating this approach into our apps. Feel free to extend it for yours (it could use richer design time support for starters). I've included the base classes and a sample control that allows you to dynamically the change style of any web control / control in response to client side events (upon postback, the target control will also be automatically updated to reflect the style changes in its properties, so that everything is consistent when the page refreshes).
public class ChangeStyleClientScript : ClientScript
{
public ChangeStyleClientScript()
{
}
string controlToModify;
public string ControlToModify
{
get
{
return controlToModify;
}
set
{
controlToModify = value;
}
}
public string CssClass
{
get
{
return controlStyle.CssClass;
}
set
{
controlStyle.CssClass = value;
}
}
public FontInfo Font
{
get
{
return controlStyle.Font;
}
}
Style controlStyle = new Style();
public Style ControlStyle
{
get
{
return controlStyle;
}
}
public Color BackColor
{
get
{
return ControlStyle.BackColor;
}
set
{
ControlStyle.BackColor = value;
}
}
public Color ForeColor
{
get
{
return ControlStyle.ForeColor;
}
set
{
ControlStyle.ForeColor = value;
}
}
public Unit BorderWidth
{
get
{
return ControlStyle.BorderWidth;
}
set
{
ControlStyle.BorderWidth = value;
}
}
public BorderStyle BorderStyle
{
get
{
return ControlStyle.BorderStyle;
}
set
{
ControlStyle.BorderStyle = value;
}
}
public Color BorderColor
{
get
{
return BorderColor;
}
set
{
BorderColor = value;
}
}
public override string GenerateScript()
{
Control c = Parent.Page.FindControl(ControlToModify);
string ctmID = c.ClientID.Replace(":","_");
StringBuilder script = new StringBuilder();
WebControl wc = c as WebControl;
if(wc == null || ControlStyle.BackColor != wc.BackColor)
{
script.Append(ctmID);
script.Append(".style.backgroundColor='");
script.Append(ColorTranslator.ToHtml(ControlStyle.BackColor));
script.Append("';");
}
if(wc == null || ControlStyle.ForeColor != wc.ForeColor)
{
script.Append(ctmID);
script.Append(".style.color='");
script.Append(ColorTranslator.ToHtml(ControlStyle.ForeColor));
script.Append("';");
}
if(wc == null || ControlStyle.BorderStyle != wc.BorderStyle)
{
script.Append(ctmID);
script.Append(".style.borderStyle='");
script.Append(ControlStyle.BorderStyle.ToString());
script.Append("';");
}
if(wc == null || ControlStyle.BorderWidth != wc.BorderWidth)
{
script.Append(ctmID);
script.Append(".style.borderWidth='");
script.Append(ControlStyle.BorderWidth.ToString());
script.Append("';");
}
if(wc == null || ControlStyle.BorderColor != wc.BorderColor)
{
script.Append(ctmID);
script.Append(".style.borderColor='");
script.Append(ControlStyle.BorderColor.ToString());
script.Append("';");
}
if(wc == null || CssClass != wc.ControlStyle.CssClass)
{
script.Append(ctmID);
script.Append(".className='");
script.Append(ControlStyle.CssClass);
script.Append("';");
}
if(wc == null || wc.Font.Bold != Font.Bold)
{
script.Append(ctmID);
script.Append(".style.fontWeight='");
script.Append(ControlStyle.Font.Bold ? "bold" : "normal");
script.Append("';");
}
if(wc == null || wc.Font.Italic != Font.Italic)
{
script.Append(ctmID);
script.Append(".style.fontStyle ='");
script.Append(ControlStyle.Font.Italic ? "italic" : "normal");
script.Append("';");
}
if(wc == null || wc.Font.Underline != Font.Underline)
{
script.Append(ctmID);
script.Append(".style.textDecoration ='");
script.Append(ControlStyle.Font.Underline ? "underlined" : "none");
script.Append("';");
if(Font.Underline)
{
script.Append(ctmID);
script.Append(".style.textUnderlinePosition='below';");
}
}
if(wc == null || wc.Font.Size != Font.Size)
{
script.Append(ctmID);
script.Append(".style.fontSize ='");
script.Append(ControlStyle.Font.Size);
script.Append("';");
}
if(wc == null || wc.Font.Overline != Font.Overline)
{
script.Append(ctmID);
script.Append(".style.textDecoration='");
script.Append(ControlStyle.Font.Overline ? "underlined" : "none");
script.Append("';");
if(Font.Overline)
{
script.Append(ctmID);
script.Append(".style.textUnderlinePosition='above';");
}
}
if(wc == null || wc.Font.Strikeout != Font.Strikeout)
{
script.Append(ctmID);
script.Append(".style.textDecorationLineThrough='");
script.Append(ControlStyle.Font.Overline ? "true" : "false");
script.Append("';");
}
return script.ToString();
}
protected override void OnExecuted()
{
WebControl wc = Parent.Page.FindControl(ControlToModify) as WebControl;
if(wc != null)
{
wc.ControlStyle.CopyFrom(ControlStyle);
}
}
}
public abstract class ClientScript
{
public event EventHandler Executed;
Control parent;
public Control Parent
{
get
{
return parent;
}
set
{
parent = value;
}
}
ClientEvent clientEvent;
public ClientEvent ClientEvent
{
get
{
return clientEvent;
}
set
{
clientEvent = value;
}
}
public abstract string GenerateScript();
protected virtual void OnExecuted()
{
}
internal void RaiseEvent(object sender)
{
OnExecuted();
if(Executed != null)
{
Executed(sender, EventArgs.Empty);
}
}
}
[Flags]
public enum ClientEvent
{
None = 0, OnClick = 1, OnMouseOver = 2, OnMouseOut = 4
}
public class ClientScriptWebControl : WebControl, IPostBackDataHandler
{
public ClientScriptWebControl()
{
}
ClientScript script;
public ClientScript Script
{
get
{
return script;
}
set
{
script = value;
if(script != null)
{
script.Parent = this;
}
}
}
protected string EventFieldName
{
get
{
return "__events_"+ClientID.Replace(":","_");
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit (e);
Page.RegisterRequiresPostBack(this);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender (e);
Page.RegisterHiddenField(EventFieldName,"");
}
protected void RenderScript(HtmlTextWriter writer)
{
if(script != null)
{
string scriptText = script.GenerateScript()+EventFieldName+".value=1;";
if((script.ClientEvent & ClientEvent.OnClick) != ClientEvent.None)
{
writer.AddAttribute("onClick", scriptText);
}
if((script.ClientEvent & ClientEvent.OnMouseOver) != ClientEvent.None)
{
writer.AddAttribute("onMouseOver", scriptText);
}
if((script.ClientEvent & ClientEvent.OnMouseOut) != ClientEvent.None)
{
writer.AddAttribute("onMouseOut", scriptText);
}
}
}
public void RaisePostDataChangedEvent()
{
script.RaiseEvent(this);
}
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
if(Page.Request[EventFieldName] == "1")
{
RaisePostDataChangedEvent();
}
return false;
}
}
public class ClientScriptLinkButton : ClientScriptWebControl
{
public string Text
{
get
{
return ViewState["Text"] as string;
}
set
{
ViewState["Text"] = value;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender (e);
}
public override void RenderBeginTag(HtmlTextWriter writer)
{
writer.AddAttribute("id", ClientID.Replace(":","_"));
writer.AddAttribute("type", "submit");
ControlStyle.AddAttributesToRender(writer);
RenderScript(writer);
writer.AddAttribute("href","#");
writer.RenderBeginTag("a");
writer.Write(Text);
}
public override void RenderEndTag(HtmlTextWriter writer)
{
writer.WriteEndTag("a");
}
}