| A couple of interesting things happened
to me at about the same time recently, and that's why you are reading
this article. First, I needed to have an efficient way to control the
timed reloading or postback on a page that monitors some Windows Services
and shows their status. It was conceivable that the user would need to
keep the page up in their browser and watch it over a period of time while
monitoring the services in order to see if any of them had stopped or
started. Second, I finished reading and reviewing "Professional
ASP.NET Server Controls" which has an add-on downloadable chapter
about encapsulating client-side Javascript in .NET Server Controls.
At first I reasoned that the easy way to handle this
would have been to simply insert a Meta Refresh tag, <meta
http-equiv="refresh" content="5"> which
would automatically reload the page every 5 seconds.
However, all the cool stuff I read in the Wrox book convinced
me that if I took a little time and exercised my noodle, I could come
up with a much more useful and extensible .NET way to do this. One which
might have a programmable event that could be engineered on the server-side.
The result is my "PAB.PgTimer" ServerControl
which is available free for all to download at the bottom of this page.
One of the coolest things about the ServerControl architecture
is some methods that allow you to emit client-side Javascript. This can
be used for a number of things, and you can even author a 100% client
- side DHTML control whose Javascript, css and even images are compiled
and encapsulated into the ServerControl DLL Assembly as resources.
A common scenario would be for the control to perform
a postback, and if the control you are writing doesn't derive from an
existing WebControl that already performs this action, you will have to
implement it manually.
All we would need is code that creates the Javascript
to do a postback in an ASP.NET page which looks like this:
<script
language="javascript">
<!--
setTimeout( "__doPostBack('PgTimer1','')", 5000);
// -->
</script>
Fortunately, there is a built - in method , GetPostBackEventReference,
that receives a control and a string argument and returns a string with
the proper client - side call to perform the postback, which looks like
this:
<input
type="hidden" name="__EVENTTARGET" value=""
/>
<input type="hidden" name="__EVENTARGUMENT" value=""
/>
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document.Default;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
The rest is simply practicing how
to author ServerControls and wire them up. In this particular case, it's
a pretty simple control because it has no visible UI - it's sole purpose
in life is to provide an "elapsed" event which can be programmed
against to postback the default form on the ASP.NET page. The cool thing
about having this in a ServerControl is that the event handler (in this
case, ControlID_Elapsed ) is generated in your ASP.NET
page in the Designer view by simply double - clicking on the control.
This means you can go ahead and "fill it in" to do whatever
you want - go get stock prices, etc. and refresh the info on elements
on your page, or simply let the postback occur and whatever happens in
your Page_Load simply reoccurs.
PgTimer has two classes, PgTimerDesigner,
which derives from ControlDesigner, and PgTimer,
whcih derives from Control and also inherits the IPostBackEventHandler
interface.
Let's take a look at the code in
each class:
using
System;
using System.IO;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Text;
namespace PAB.PgTimer
{
[ToolboxDataAttribute("<{0}:PgTimer runat=server></{0}:PgTimer>")]
[DefaultEventAttribute("Elapsed")]
[ToolboxBitmapAttribute(typeof(PgTimer), "PAB.PgTimer.PgTimer.bmp")]
[DesignerAttribute("PAB.PgTimer.PgTimerDesigner", "PAB.PgTimer")]
public class PgTimer : Control, IPostBackEventHandler
{
private int intInterval = 5000;
private bool blnEnabled = true;
private bool blnEnableViewState = true;
[CategoryAttribute("PgTimer")]
[BrowsableAttribute(true)]
[DescriptionAttribute("Number of milliseconds PgTimer waits
until firing a postback.")]
[BindableAttribute(true)]
public int Interval
{
get
{
return intInterval;
}
set
{
intInterval = value;
}
}
[BindableAttribute(true)]
[CategoryAttribute("PgTimer")]
[DescriptionAttribute("Whether PgTimer Control is enabled.")]
[BrowsableAttribute(true)]
public bool Enabled
{
get
{
return blnEnabled;
}
set
{
blnEnabled = value;
}
}
[BindableAttribute(false)]
[BrowsableAttribute(false)]
public override bool EnableViewState
{
get
{
return blnEnableViewState;
}
set
{
blnEnableViewState = value;
}
}
public event EventHandler Elapsed;
protected override void OnInit(EventArgs
e)
{
}
protected override void Render(HtmlTextWriter
output)
{
if (blnEnabled)
{
/* Create the Javascript postback "setTimeout"
Page Timer code:
<script language="javascript">
<!--
setTimeout( "__doPostBack('PgTimer1','')", 5000);
// -->
</script>
*/
StringBuilder sb = new StringBuilder();
sb.Append("\n<script language=\"javascript\">
\n <!-- \n setTimeout( \"");
sb.Append(base.Page.GetPostBackEventReference(this));
sb.Append("\", ");
sb.Append(intInterval.ToString());
sb.Append("); \n // --> \n </script>");
output.Write(sb.ToString());
}
}
public void RaisePostBackEvent(string eventArgument)
{
OnElapsed(new EventArgs());
}
public
void OnElapsed(EventArgs ea)
{
if (Elapsed != null)
{
Elapsed(this, ea);
}
}
}
}
|
The attributes that decorate the
class control the designer and default event:
[ToolboxDataAttribute("<{0}:PgTimer
runat=server></{0}:PgTimer>")] -- sets
the tag when you drop an instance of the control from your toolbox onto
your page.
[DefaultEventAttribute("Elapsed")] --
sets the default event for the control (what we can program against)
[ToolboxBitmapAttribute(typeof(PgTimer), "PAB.PgTimer.PgTimer.bmp")]
-- sets the icon for the toolbox - bmp must be the same name as the class,
and be set as Build Action=Embedded Resource
[DesignerAttribute("PAB.PgTimer.PgTimerDesigner", "PAB.PgTimer")]
-- sets the designer class for the control
Aside from the above, the only
other really important feature is that we override the Render method of
the control to output our custom - generated Javascript.
Now here is the code for the Designer
Class, which derives from ControlDesigner:
using
System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Reflection;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Web.UI.Design;
namespace PAB.PgTimer
{
class PgTimerDesigner : ControlDesigner
{
private PgTimer pgtimer ;
public override string GetDesignTimeHtml()
{
return "<label id=\'Lbl1\' style=\'align: center;
COLOR:Blue; valign: middle; background-color:FFCC66; border-width:2px;\'
>PgTimer</label>";
}
public override void Initialize(IComponent component)
{
pgtimer = (PgTimer)component;
base.Initialize(component);
return;
}
}
}
|
As can be seen, in this simple
control which really has no usable UI, all we need to do is override the
GetDesignTimeHtml method to show something on the Designer so we can find
it when we have dragged an instance from the Toolbox to our page. Everything
else is "default" Designer class implementation.
Once this is compiled, all we need
to do is choose "Customize Toolbox" by right - clicking on the
Toolbox panel when our design view for our web page is active, choose
the ".NET Framework Components" tab, and browse to the physical
control DLL and bring it in. If you start out with the defaults in the
downloaded solution, you'll see our exceptionally innovative "Fried
Egg" Eggheadcafe.com icon on your Toolbox as a selectable control
item. To add a PgTimer control to a page, just grab the icon from the
Toolbox panel and drag - n - drop it on to your page in Design mode! You
can then set the only two properties besides the control's ID, which are
Enabled and Interval (the number of
milliseconds between postback events).
When you double-click on the control,
it will expose the Elapsed event handler:
private void PgTimer1_Elapsed(object sender, System.EventArgs
e)
{
// do whatever you need to do here (get stock prices, etc.)
}
Note: When you unzip the sample
code in the download, there are two folders, PgTimer (for the control
project and the solution) and PgTimerTest (for the web project that uses
the control). If you unzip this in wwwroot, all you'll need to do is right
click on the PgTimerTest folder and choose Web Sharing to make it a virtual
directory. I would add in closing that this is the simplest possible implementation,
you can add a lot more functionality to this type of control. Enjoy!
Download
the code that accompanies this article:
| |
| Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here. --><-- NOTE: Post QUESTIONS on FORUMS! |  |
|
|