|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article describes the process to create a reusable progress bar that can be controlled at the server or client side. BackgroundIn web UI development, it is sometimes necessary to display resource usage information. Some applications for progress bars include displaying the amount of messages in a queue in relation to the queue size or displaying the number of current logged-in users in relation to the maximum users to date. The possibilities are endless. In my first attempt at creating an HTML based progress bar, I used Using the codeI'll describe the following topics in this section.
Basic server control creationCreate a new class derived from using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.Design;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Design;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.IO;
using System.Math;
namespace YourNamespace
{
public class ProgressBar : System.Web.UI.WebControls.WebControl
{
}
}
Next, add the following attribute to the top of the class file (under the [assembly: TagPrefix("YourNamespace", "ShortName")]
Replace Add the following code to the top of the class declaration: /// <summary>
/// This is the common graphical progress bar.
/// </summary>
/// <remarks>
/// This custom control produces a graphical progress bar.
/// </remarks>
[DefaultProperty("Type")]
[ToolboxData("<{0}:ProgressBar runat="server"></{0}:ProgressBar>")]
[ToolboxBitmapAttribute( typeof(ProgressBar) ) ]
public class ProgressBar : System.Web.UI.WebControls.WebControl
{
Feel free to add/replace the comment code above the declaration to suit. The ProgressBar1 = BarType.Horizontal;
The <ShortName:ProgressBar id="ProgressBar1"
runat="server"></ShortName:ProgressBar>
The You now have a basic control that will be rendered in the toolbox and will populate your server page correctly when dragged from the toolbox. I will cover how to add this control to your toolbox in the Using the progress bar section. In the next section, I will cover adding properties to the progress bar control. Progress bar server control propertiesThe following properties have been identified as necessary for basic progress bar rendering: The public enum BarType
{
Horizontal,
Vertical
}
The properties
The properties The property public enum BarFormat
{
Gif,
Jpeg,
Png,
Bmp
}
Here's the code for those properties: #region Private member variables
BarFormat format;
BarType type;
bool border;
System.Drawing.Color fillColor;
double fillPercent;
Unit barSize;
#endregion
#region Public properties
[DefaultValue(BarFormat.Png), Category("ProgressBar Common"),
Description("Compression format for image.")]
public BarFormat Format
{
get { return format; }
set { format = value; }
}
[DefaultValue(false), Category("ProgressBar Common"),
Description("Flag to show border or not.")]
public bool Border
{
get { return border; }
set { border = value; }
}
[DefaultValue(BarType.Horizontal), Category("ProgressBar Common"),
Description("Type of progress bar to render.")]
public BarType Type
{
get { return type; }
set { type = value; }
}
[Category("ProgressBar Common"),
Description("Background color of progress bar.")]
public System.Drawing.Color FillColor
{
get { return fillColor; }
set { fillColor = value; }
}
[DefaultValue(0.0), Category("ProgressBar Common"),
Description("Fill percentage of progress bar.")]
public double FillPercent
{
get { return fillPercent; }
set { fillPercent = value; }
}
[DefaultValue(0.0), Category("ProgressBar Common"),
Description("Size of progress bar inside frame")]
public Unit BarSize
{
get { return barSize; }
set { barSize = value; }
}
The last property Rendering the progress bar server controlThere are two modes for rendering an ASP.NET server control. The first, //Initial request for ProgressBar object
protected override void Render(HtmlTextWriter output)
{
if (this.Site != null && this.Site.DesignMode )
{
// Be careful to specify only html that
// can be embedded in any tag. This will prevent
// problems when displaying in the Visual Designer.
output.Write(string.Format("<font size='1' " +
"color='SeaGreen' face='arial'> [ProgressBar::{0}]" +
"</font><br>", ID) );
}
Rendering the control at runtime requires an introduction to the next topic - using an HTTP handler to handle displaying the images rendered on the fly. This handler will intercept requests for a certain webpage, which doesn't exist, but is specified within an else
{
string uniqueName = GenerateUniqueName();
Page.Application[ProgressBarRenderStream.ImageNamePrefix + uniqueName] = this;
string toolTip = string.Format( "{0}",
Enabled ? ((ToolTip == null || ToolTip == string.Empty) ?
Round(FillPercent, 2).ToString()+"%" : ToolTip) : "");
//Write relative URL for image stream request
output.Write(
string.Format("<img src='{0}?id={1}' " +
"border='{2}' height='{3}' width='{4}' alt='{5}'>",
ProgressBarRenderStream.ImageHandlerRequestFilename,
uniqueName, (this.Border ? "1" : "0"),
(int)this.Height.Value, (int)this.Width.Value,
toolTip );
}
//Generates a new name for this control & registers
string GenerateUniqueName()
{
string sControlName = System.Guid.NewGuid().ToString();
//Identifies requested ProgressBar image
return sControlName;
}
This code will generate a unique name which will identify the progress bar class currently rendering the output. This unique name is used as the <img src='image_stream.aspx?id=89f7f4ed-7433-460d-ae97-53c22aa2a232'
border='1' height='8' width='128' alt='25.5%'>
This is still not enough to render the control. The following functions will take care of drawing the progress bar and rendering it to a memory stream: public MemoryStream RenderProgressBar()
{
try
{
if( Site != null && Site.DesignMode )
{
string sType = this.Type.ToString();
return null;
}
else
{
return makeProgressBar();
}
}
catch
{
return null;
}
}
private MemoryStream makeProgressBar()
{
// now convert percentage to width
// (subtract 2 from width to adjust for border)
Bitmap bmp = new Bitmap((int)Width.Value,
(int)Height.Value, PixelFormat.Format32bppArgb);
MemoryStream memStream = new MemoryStream();
Brush fillBrush = new SolidBrush( FillColor );
Brush backgroundBrush = new SolidBrush( this.BackColor );
// draw background
System.Drawing.Graphics graphics =
Graphics.FromImage( bmp );
graphics.FillRectangle(backgroundBrush, 0,
0, (int)Width.Value, (int)Height.Value);
double fillAmount;
if( this.Type == BarType.Horizontal )
{
// draw a horizontal bar
// draw only BarSize height, centered vertically
// inside the frame
fillAmount = Width.Value * (FillPercent/100.0);
graphics.FillRectangle(fillBrush, 0,
((int)Height.Value - (int)BarSize.Value)/2,
(int)fillAmount, (int)BarSize.Value);
}
else
{
// draw a vertical bar
// draw only BarSize width, centered horizontally
// inside the frame
fillAmount = Height.Value * (FillPercent/100.0);
graphics.FillRectangle(fillBrush,
((int)Width.Value - (int)BarSize.Value)/2,
(int)Height.Value-(int)fillAmount,
(int)BarSize.Value, (int)Height.Value);
}
graphics.Save();
System.Drawing.Imaging.ImageFormat imgformat =
System.Drawing.Imaging.ImageFormat.Png;
switch( Format )
{
case BarFormat.Bmp:
imgformat = ImageFormat.Bmp;
break;
case BarFormat.Gif:
imgformat = ImageFormat.Gif;
break;
case BarFormat.Jpeg:
imgformat = ImageFormat.Jpeg;
break;
case BarFormat.Png:
imgformat = ImageFormat.Png;
break;
}
// Render BitMap Stream Back To Client
bmp.Save(memStream, imgformat);
return memStream;
}
This code will be called by the Rendering images on the fly using an HTTP handlerIn order to render images on the fly, it is necessary to intercept the HTTP request and look for a specific web page which does not exist. This web page name is specified by the progress bar server control during rendering. The HTTP handler will find that value from the query string after it detects the "special" web page. The handler can then use this value to look up the requested progress bar control for rendering on the fly. There are two steps necessary to provide a custom HTTP handler. First, create a class derived from public class ProgressBarRenderStream : IHttpModule
{
public const string ImageHandlerRequestFilename="image_stream.aspx";
public const string ImageNamePrefix="i_m_g";
public ProgressBarRenderStream()
{
}
public virtual void Init( HttpApplication httpApp )
{
httpApp.BeginRequest += new EventHandler(httpApp_BeginRequest);
}
public virtual void Dispose()
{
}
private void httpApp_BeginRequest(object sender, EventArgs e)
{
HttpApplication httpApp = (HttpApplication)sender;
ProgressBar pb = null;
if( httpApp.Request.Path.ToLower().IndexOf(
ImageHandlerRequestFilename) != -1 )
{
pb = (ProgressBar)httpApp.Application[ImageNamePrefix +
(string)httpApp.Request.QueryString["id"]];
if( pb == null )
{
return; // 404 will be returned
}
else
{
try
{
System.IO.MemoryStream memStream = pb.RenderProgressBar();
memStream.WriteTo(httpApp.Context.Response.OutputStream);
memStream.Close();
httpApp.Context.ClearError();
httpApp.Context.Response.ContentType = pb.ContentType;
httpApp.Response.StatusCode = 200;
httpApp.Application.Remove(ImageNamePrefix +
(string)httpApp.Request.QueryString["id"]);
httpApp.Response.End();
}
catch(Exception ex)
{
ex = ex;
}
}
}
}
}
The code above illustrates the shared values that the httpApp.BeginRequest += new EventHandler(httpApp_BeginRequest);
The final step required is to add the HTTP handler to your web config file. This must be added to your root level web config file in order to work properly. <httpModules>
<add name="ProgressBarRenderStream"
type="YourNamespace.ProgressBarRenderStream,YourAssemblyName" />
</httpModules>
Using the progress bar on a server pageUsing the progress bar on a server page is easy. First, add a reference to the control in your project file. Next, add the following directive to the top of your server page: <%@ Register TagPrefix="ShortName"
Namespace="YourNamespace" Assembly="YourAssemblyName" %>
Finally, add the control to your page and change the parameters to suit: <ShortName:ProgressBar id="ProgressBar1" runat="server"></ShortName:ProgressBar>
ConclusionI started my research thinking there was an easy solution to my problem - provide direct feedback of a process using a graphical representation. Digging further, I realized that what I was trying to do was very difficult for a server control to do by itself without compromising security on the site. Further research led me to the HTTP handler method of dealing with images rendered on the fly. This method is powerful and can be extended to other applications, limited only by your imagination.
|
||||||||||||||||||||||