动态内容渲染的另一个视角,侧重于图像和 JScript
关于使用 ASP.NET 渲染动态内容的信息和示例。
引言
已经有几篇文章讨论了动态图像渲染的主题。本文讨论了一种更通用的实现此概念的方法。本文讨论的一些技术基于专注于动态内容渲染特定领域(如图形、图像文本等)的类似文章的概念。
即,以下文章对本文的撰写起到了至关重要的作用:
- Graphics on the Fly Directly from ASP.NET Server Control without .ASPX page - Andrew S Traub
- Roll your own ASP.NET Chart - blong
本文试图将动态渲染的概念通用化,以便程序员可以实现自己的动态内容。这可以作为创建自定义 ASP.NET Web 控件的基础,这些控件可以提供各种不同类型的动态内容。我们将研究动态生成或嵌入在程序集 DLL 中的动态内容的渲染方式。这有助于消除对单独文件(如通常需要与 Web 控件程序集 DLL 一起单独包含的 JavaScript 或图像)的需求。
动态渲染的一些优点是:
- 能够将所有静态文件嵌入到程序集 DLL 中,以便于分发。
- 隐藏脚本源代码。
- 能够在服务器上创建动态图像,而无需将图像文件保存到磁盘。
背景
当我阅读关于动态图像渲染的文章和示例代码时,我决定把我学到的东西拿来尝试改进。当我深入研究动态图像渲染时,我很快意识到其他内容也可以被动态渲染,例如脚本、样式表等。我决定基于我阅读的动态图像内容文章来实现通用的动态内容类。我还选择将图像绘制纳入用户控制的绘制,而不是针对特定绘制类型的特定类。结果就是本文所描绘的源代码。
使用代码
用于提供动态内容的主要类是 CustomHttpModule
类。这是一个模块处理程序类,用于拦截 aspx URL 请求。此类在 Web.Config 文件的 <System.web>
部分下注册,如下所示:
<httpModules>
<add name="CustomModule" type="Custom.Web.CustomHttpModule, CustomWeb"/>
</httpModules>
请注意,CustomWeb 项目生成的 CustomWeb.dll 必须可以被实现它的 ASP.NET 页面访问。这通常涉及将生成的 CustomWeb.DLL 文件复制到 ASP.NET 页面的 bin 目录中。
为了了解如何使用 CustomHttpModule
类,我提供了两个实现了它的类。一个是 CustomImage
类,另一个是 EmbeddedJScript
类。请注意,EmbeddedJScript
类实现了更通用的 EmbeddedWebContent
类,该类封装了 CustomHttpModule
类,而 CustomImage
类直接实现了 CustomHttpModule
类。
让我们先看看 CustomImage
类。这个类是一个派生自 Image
控件的自定义控件。它可以使用 Visual Studio .NET 拖放到 Web 窗体上。
第一个感兴趣的区域是 Render()
方法。正是在此方法中,CustomImage
控件调用了一个名为 OnImageRender
的事件处理程序。此事件处理程序由控件对象的父页面实现,用于绘制到 EventArgs
中提供的 GDI+ Graphics
对象上。然后,此动态绘制的图像将使用 CustomHttpModule
类动态地流式传输到网页。
protected override void Render(HtmlTextWriter writer)
{
if (Site != null && Site.DesignMode)
{
Label DesignerLabel = new Label();
DesignerLabel.Width = this.Width;
DesignerLabel.Height = this.Height;
DesignerLabel.Text = "CUSTOM IMAGE";
DesignerLabel.BorderStyle = this.BorderStyle;
DesignerLabel.BorderWidth = this.BorderWidth;
DesignerLabel.BorderColor = this.BorderColor;
DesignerLabel.CssClass = this.CssClass;
DesignerLabel.RenderControl(writer);
}
else
{
String SaveUrl = null;
if (OnImageRender != null)
{
Bitmap Bmp = new Bitmap((int) this.Width.Value,(int) this.Height.Value);
Graphics Graph = Graphics.FromImage(Bmp);
MemoryStream MemStream = new MemoryStream();
Graph.FillRectangle(new SolidBrush(this.BackColor),0,0,Bmp.Width,Bmp.Height);
OnImageRender(this,new RenderImageEventArgs(Graph));
Bmp.Save(MemStream,RenderImageFormat);
ImageData.Data = MemStream.ToArray();
SaveUrl = ImageUrl;
ImageUrl = CustomHttpModule.RegisterHandler(Page.Application,
ID,
new HttpModuleHandler(ModuleHandler),
this.ImageData);
Graph.Dispose();
Bmp.Dispose();
MemStream.Close();
}
base.Render(writer);
if (SaveUrl != null)
{
ImageUrl = SaveUrl;
}
}
CustomHttpModule
在 CustomImage
类的两个不同区域实现。这里是调用 CustomHttpModule.RegisterHandler()
的静态调用。
ImageUrl = CustomHttpModule.RegisterHandler(Page.Application,
ID,
new HttpModuleHandler(ModuleHandler),
this.ImageData);
上述调用基本上将控件注册为自定义图像处理程序控件。CustomHttpModule.RegisterHandler()
的返回值是一个 URL,用于向 CustomHttpModule
类发出信号,以便在 Web 页面请求动态内容时调用 HttpModuleHandler
委托(ModuleHandler
)。RegisterHandler
的最后一个参数是可选数据,将在调用委托时传递给它。在这种情况下,它是事件处理程序中的代码绘制的图像原始数据字节。
当 Image
控件渲染时,它将使用来自 RegisterHandler
的 URL 作为 <Img>
标签的 SRC
属性。当浏览器尝试查找图像时,模块处理程序就会接管。它会找到适当的已注册调用者,然后调用 ModuleHandler
委托。然后,委托会将图像内容流式传输到请求的 URL。
static private void ModuleHandler(CustomHttpModule Module,
CustomHttpModuleEventData e)
{
HttpContext context = e.HttpApp.Context;
CustomImage.ImageDataType ImgData = e.ModuleObj as ImageDataType;
if (ImgData != null)
{
MemoryStream MemStream = new MemoryStream();
MemStream.Write(ImgData.Data,0,ImgData.Data.Length);
MemStream.WriteTo(context.Response.OutputStream);
MemStream.Close();
context.Response.Cache.SetNoServerCaching();
context.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
context.Response.Cache.SetNoStore();
context.Response.Cache.SetExpires(new DateTime(1900,01,01,00,00,00,00));
context.ClearError();
context.Response.ContentType = "image/" + ImgData.RenderImageFormat.ToString();
context.Response.StatusCode = 200;
context.Response.Flush();
}
}
EmbeddedJScript
类使用类似的技术;但是,它封装了 EmbeddedWebContent
类,该类提供了一个通用的 ModuleHandler
委托函数,用于流式传输数据。使用 EmbeddedWebContent
类的主要原因是为了允许将资源嵌入数据流式传输为动态内容。因此,您可以创建一个 JavaScript .JS 文件,并将其作为嵌入资源添加到 VS.NET 项目中。只需在 Visual Studio 中将 JavaScript .JS 文件的生成操作设置为“嵌入资源”。这可以通过文件属性访问(右键单击 JS 文件并选择“属性”)。
当 JScript 文件被嵌入时,它就成为程序集 DLL 的一部分,在分发 Web 控件时无需将其包含为单独的文件。JScript 源从资源加载,并使用 HttpModuleHandler
类直接作为动态内容流式传输到 Web 页面,如 CustomImage
类所示。请注意,对于资源嵌入的图像、样式表等,也可以使用类似的方法。
总之,这里的首要目标是展示各种形式的动态内容的使用,并说明一个中心控件类(HttpModuleHandler
)以一种有条理的方式处理该过程。谁知道呢,也许其他人可以像我从我读过的文章中学到的那样,在这些代码的基础上进行扩展。我希望这有帮助。