ASP.NET 进度条控件






3.77/5 (7投票s)
2005年2月15日
7分钟阅读

119509

3658
一个可重用的 ASP.NET 服务器控件进度条。
引言
本文介绍了一个可重用的进度条的创建过程,该进度条可以在服务器端或客户端进行控制。
背景
在 Web UI 开发中,有时需要显示资源使用信息。进度条的一些应用包括显示队列中的消息数量与队列大小的比例,或者显示当前登录用户数与最多用户数的比例。可能性是无限的。在我第一次尝试创建基于 HTML 的进度条时,我使用了 ASP.NET 服务器页面中的 span 元素来可视化表示进度条。然后,我使用 IFrame
嵌入了该服务器页面,并在需要显示进度条的地方使用它。这种方法存在一个严重缺点,即为了正确显示进度条和处理来自包含页面的样式交互,需要非常复杂的 span 嵌套。在显示进度条的已填充部分时也存在准确性问题。使用嵌入式 IFrame
元素也存在一些问题,其中一些问题并非微不足道。然后,我决定创建一个可重用的 ASP.NET 服务器控件来托管进度条。span 元素存在所有相同的缺点,因此我决定寻找更好的方法,并开始研究 ASP.NET 条形图示例,这些示例与进度条非常相似。我决定采用一种相当健壮的解决方案,使用 HTTP 处理程序和“即时”渲染的图像。这就是本文将要描述的方法。
使用代码
我将在本节中介绍以下主题
- 基本服务器控件创建。
- 进度条服务器控件属性。
- 渲染进度条服务器控件。
- HTTP 处理程序
CustomImageHandler
,用于处理“即时”渲染的图像。 - 在服务器页面中使用进度条。
基本服务器控件创建
创建一个派生自 System.Web.UI.WebControls.WebControl
的新类,并添加如下所示的 using
语句
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
{
}
}
接下来,在类文件顶部(在 using
语句下方)添加以下属性
[assembly: TagPrefix("YourNamespace", "ShortName")]
将 YourNamespace
和 ShortName
替换为适合您项目的有意义的值。此属性控制可视化设计器在将服务器控件从工具箱拖放到服务器页面时添加到 <@Register>
标记中的内容。它允许使用更适合您应用程序的命名方案。我将在“使用进度条”部分更详细地介绍这一点。
在类声明的顶部添加以下代码
/// <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
{
随意添加/替换声明上方的注释代码以作调整。
DefaultProperty
属性将 Type
属性声明为默认属性。这允许您隐式引用该属性。
ProgressBar1 = BarType.Horizontal;
ToolboxData
属性声明了工具箱在将服务器控件拖放到页面上时添加到服务器页面的文本。属性描述中的 {0}
部分是该控件已注册命名空间的替换变量。这由上面描述的 TagPrefix
属性控制。当控件放置在服务器页面上时,可视化设计器将添加以下内容
<ShortName:ProgressBar id="ProgressBar1" runat="server"></ShortName:ProgressBar>
ToolboxBitmapAttribute
属性描述了将在工具箱中显示的位图。如果您只提供一个类类型而不提供名称,可视化设计器将在程序集中搜索同名(16x16)的位图。您可以创建一个 16x16 的位图并将其作为嵌入式资源添加到项目中,以表示您的控件,否则可视化设计器将使用默认位图。
现在您有了一个基本控件,它将显示在工具箱中,并在从工具箱拖放到服务器页面时正确填充您的服务器页面。我将在“使用进度条”部分介绍如何将此控件添加到工具箱。在下一节中,我将介绍如何向进度条控件添加属性。
进度条服务器控件属性
已确定以下属性对于基本进度条渲染是必需的:Type
、Height
、Width
、FillColor
、BackColor
、Border
、BarSize
、FillPercent
和 Format
。
Type
属性是一个枚举,定义如下
public enum BarType
{
Horizontal,
Vertical
}
Height
、Width
和 BarSize
属性都是 Unit
值类型,分别描述进度条的高度、宽度和内部大小。FillPercent
是一个 float
值,指定要显示的进度条的比例。BackColor
和 FillColor
属性是无符号长整型值,分别指定背景颜色和进度条/填充颜色。
Format
属性是一个枚举值,定义如下
public enum BarFormat
{
Gif,
Jpeg,
Png,
Bmp
}
以下是这些属性的代码
#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; }
}
[Browsable(false)]
public string ContentType
{
get { return "image/" + Format.ToString().ToLower(); }
}
最后一个属性 ContentType
使用 Browsable(false)
属性声明为不可浏览。此属性由 HTTP 处理程序 部分中描述的 HTTP 处理程序类 CustomImageHandler
使用。
渲染进度条服务器控件
ASP.NET 服务器控件有两种渲染模式。第一种是设计模式,它提供了一种控件在可视化设计器中表示自己的方法。由于进度条控件是由“即时”渲染的图像组成的,因此无法指定准确的表示,可以使用默认表示
//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) );
}
在运行时渲染控件需要介绍下一个主题 - 使用 HTTP 处理程序来处理“即时”渲染的图像。此处理程序将拦截对某个不存在的网页的请求,但该网页由进度控件生成的 <img>
标记指定。我将在下一节详细介绍此处理程序。生成 <img>
标记的代码如下
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;
}
此代码将生成一个唯一名称,用于标识当前正在渲染输出的进度条类。此唯一名称用作 src
属性,以便 HTTP 处理程序稍后找到该类。ProgressBarRenderStream.ImageNamePrefix
和 ProgressBarRenderStream.ImageHandlerRequestFilename
的值在 HTTP 处理程序类中声明,并将在下一节中介绍。此代码应生成类似以下的 HTML 序列
<img src='image_stream.aspx?id=89f7f4ed-7433-460d-ae97-53c22aa2a232'
border='1' height='8' width='128' alt='25.5%'>
值将根据属性值和生成的 GUID 值而有所不同。
这仍然不足以渲染控件。以下函数将负责绘制进度条并将其渲染到内存流中
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;
}
此代码将由 ProgresBarRenderStream
类调用,该类将在下一节中介绍。
使用 HTTP 处理程序“即时”渲染图像
为了“即时”渲染图像,有必要拦截 HTTP 请求并查找一个不存在的特定网页。此网页名称由进度条服务器控件在渲染期间指定。HTTP 处理程序将在检测到“特殊”网页后,从查询字符串中查找该值。然后,处理程序可以使用该值查找请求的进度条控件以进行“即时”渲染。
提供自定义 HTTP 处理程序需要两个步骤。首先,创建一个派生自 IHttpModule
的类。代码如下
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;
}
}
}
}
}
上面的代码说明了 ProgressBar
控件在渲染 <img>
标记时使用的共享值。这些值随后在 httpApp_BeginRequest
函数中使用,该函数在 Init
函数中注册
httpApp.BeginRequest += new EventHandler(httpApp_BeginRequest);
最后一步是向您的 Web config 文件添加 HTTP 处理程序。必须将其添加到根级别的 Web config 文件中才能正常工作。
<httpModules><add name="ProgressBarRenderStream"
type="YourNamespace.ProgressBarRenderStream,YourAssemblyName" /></httpModules>
在服务器页面中使用进度条
在服务器页面中使用进度条非常简单。首先,在项目文件中添加对该控件的引用。接下来,在服务器页面顶部添加以下指令
<%@ Register TagPrefix="ShortName" Namespace="YourNamespace"
Assembly="YourAssemblyName" %>
最后,将控件添加到您的页面,并根据需要更改参数
<ShortName:ProgressBar id="ProgressBar1"
runat="server"></ShortName:ProgressBar>
结论
我开始研究时,认为有一个简单的解决方案可以提供进程的直接反馈,并用图形表示。深入研究后,我意识到,我试图做的事情对于服务器控件来说非常困难,如果不牺牲站点安全性,则无法独立完成。进一步的研究使我找到了使用 HTTP 处理程序来处理“即时”渲染图像的方法。这种方法功能强大,可以扩展到其他应用程序,仅受您的想象力的限制。