创建图像裁剪控件

分享于 

15分钟阅读

Web开发

  繁體

Anders

介绍

创建自定义 ASP.NET web控件有很多不同的方面。 控件的用户只需将控件放置在页面上,并且该控件应该自动保持。 换句话说,register 客户端脚本。客户端CSS和客户端图像。

本文将指导你创建使用用于脚本。CSS和图像的网络资源的自定义控件,并向你展示如何为页面上的每个控件初始化JavaScript代码。 我们将介绍使用实现 IHttpHandler 接口的类的基础知识。

使用代码

我们首先要做的是创建所有的网络资源。 我们将所有的资源放在不同的文件夹( css css css,。) 中。 选择每个文件夹中的内容并右键单击/属性。 将生成操作设置为所有这些文件的嵌入资源。

接下来是 register 中的资源:

//Cropping logic[assembly: WebResource("Anders.Web.Controls.js.cropper.js", "text/javascript")]//Script lib[assembly: WebResource("Anders.Web.Controls.js.lib.builder.js", "text/javascript")]
[assembly: WebResource("Anders.Web.Controls.js.lib.dragdrop.js", "text/javascript")]
[assembly: WebResource("Anders.Web.Controls.js.lib.effects.js", "text/javascript")]
[assembly: WebResource("Anders.Web.Controls.js.lib.prototype.js", "text/javascript")]
[assembly: WebResource("Anders.Web.Controls.css.cropper.css", 
 "text/css", PerformSubstitution = true)]
[assembly: WebResource("Anders.Web.Controls.img.marqueeHoriz.gif", "image/gif")]
[assembly: WebResource("Anders.Web.Controls.img.marqueeVert.gif", "image/gif")]

请注意,我们已经为CSS资源使用了 PerformSubstitution 属性。 ASP.NET 引擎用于为CSS中定义的background 图像呈现正确的路径。

background: transparent url('<%=WebResource(
 "Anders.Web.Controls.img.marqueeHoriz.gif")%>') repeat-x 0 0;

下一步是创建cropper控件类,并让它的从中运行。 这是一个强大的抽象类,允许你在自定义控件中重用. NET 控件。

我还从 IPostBackDataHandler 中获得 inherit ;这样我就可以将控件作为 postback 处理程序进行 register。

让我们添加所有将成为我们控制的控件。

private Image image = new Image();private HiddenField cropCords = new HiddenField();private CropAreaCordinates cropArea = new CropAreaCordinates();protectedoverridevoid CreateChildControls()
{
 EnsureChildControls();
 CheckForHandler();
 image.ID = "cropImage";
 cropCords.ID = "cords";
 Controls.Add(cropCords);
 Controls.Add(image);
 base.CreateChildControls();
}

图像将是to的主用户界面,隐藏字段将存储所有的剪裁坐标,使用户可以将它们 postback。

我们还需要设置子控件的id,因为这些控件不会自动生成。 ID将把父标识作为前缀。

我们的控件实现 IPostBackDataHandler 接口,它强制我们实现 LoadPostData 方法。

publicbool LoadPostData(string postDataKey, 
 System.Collections.Specialized.NameValueCollection postCollection)
{
 string v = postCollection[postDataKey + "$cords"];
 if (!string.IsNullOrEmpty(v))
 {
 string[] values = v.Split(';');
 cropArea.X = int.Parse(values[0]);
 cropArea.Y = int.Parse(values[1]);
 cropArea.Width = int.Parse(values[2]);
 cropArea.Height = int.Parse(values[3]);
 //This values are not saved in client hiddenfield, //we retrive them from viewstate instead cropArea.MinWidth = MinWidth;
 cropArea.MinHeight = MinHeight;
 returntrue;
 }
 elsereturnfalse;
}

我们只解析将在后面使用的隐藏字段值。

现在是初始化客户端脚本和属性的时候了。 这是在 OnPrerender 方法中完成的。

protectedoverridevoid OnPreRender(EventArgs e)
{
 if (CropEnabled)
 {
 InitClientCrop();
 InitImages();
 float h = (float)CroppedImageHeight/(float)CroppedImageWidth;
 IFormatProvider culture = new CultureInfo("en-US", true);
 string height = h.ToString(culture);
 image.Attributes["onload"] = string.Format("InitCrop(this.id, 
 {0},{1},{2},{3},{4},{5}, '{6}', {7}, {8}, {9});",
 AllowQualityLoss? 0 : cropArea.MinWidth,
 AllowQualityLoss? 0 : cropArea.MinHeight,
 cropArea.X,
 cropArea.Y,
 cropArea.Width,
 cropArea.Height,
 cropCords.ClientID,
 CaptureKeys.ToString().ToLower(),
 MaintainAspectRatio? 1 : 0,
 MaintainAspectRatio? height : "0" ); 
 image.ImageUrl = string.Format("{0}?cropCacheId={1}", httpHandlerPath, CacheKey);
 cropCords.Value = string.Format("{0};{1};{2};{3}", cropArea.X, 
 cropArea.Y, cropArea.Width, cropArea.Height);
 Page.RegisterRequiresPostBack(this);
 }
 else image.Attributes.Remove("onload");
 base.OnPreRender(e);
}

首先,我们在 InitClientCrop 方法( 我将更详细地介绍) 中初始化客户端脚本,然后在 InitImages 方法中初始化将显示的图像。

我们希望这里控件在同一页上的多个实例中使用。 为此,我们需要为每个控件触发初始化脚本。 这是使用图像元素的onload 客户端事件完成的。 web浏览器完成图像加载后,onload 事件将激发。

init脚本将实例化cropper类并给它所有的属性。

注意,我们还设置了图像的路径,并且提供了路径中的缓存,我们将返回这个路径。

记住类实现 IPostBackDataHandler,但是我们还需要让页面知道我们对 postback 数据有兴趣。 这是使用 Page.RegisterRequiresPostBack 方法完成的。

脚本资源 register 进程如下所示: 首先,我们将获取资源的relative 路径,如下所示:

string protoTypePath = this.Page.ClientScript.GetWebResourceUrl(typeof(ImageCropper),
 "Anders.Web.Controls.js.lib.prototype.js");

然后,使用 ClientScriptManager 类的RegisterClientScriptInclude 方法在该页上对它的进行 register 处理:

this.Page.ClientScript.RegisterClientScriptInclude("prototype.js", protoTypePath);

我们还需要对页面上的CSS资源进行 register。 这里没有内置的方法,所以它是一些管道。

if (Page.Header.FindControl("cropCss") == null)
{
 HtmlGenericControl cssMetaData = new HtmlGenericControl("link");
 cssMetaData.ID = "cropCss";
 cssMetaData.Attributes.Add("rel", "stylesheet");
 cssMetaData.Attributes.Add("href",
 Page.ClientScript.GetWebResourceUrl(typeof(ImageCropper),
 "Anders.Web.Controls.css.cropper.css"));
 cssMetaData.Attributes.Add("type", "text/css");
 cssMetaData.Attributes.Add("media", "screen");
 Page.Header.Controls.Add(cssMetaData);
}

由于可能有更多的控件,我们需要检查是否有其他控件已经注册了 CSS。 这是使用 FindControl 方法完成的。 然后我们将CSS控件添加到页面的head控件中,如果 FindControl 方法返回

注册脚本后,我们需要呈现将呈现给用户的客户端图像。

privatevoid CreateImage()
{
 ImageManager imageManager = new ImageManager();
 CropData cropData = imageManager.GetImageCropperDisplayImage(CroppedImageWidth, 
 CroppedImageHeight, SourceImage, JpegQuality);
 cropArea = cropData.CropAreaCordinates;
 //Min width and min height is not sent to client's hiddenfield //so we need to save this in viewstate. MinWidth = cropArea.MinWidth;
 MinHeight = cropArea.MinHeight;
 //Saves buffer to cache SetBuffer(string.Empty, cropData.Buffer);
}

我不会介绍如何处理图像,如果你感兴趣的话,可以在应用程序层( 或者业务逻辑层,如果你愿意更好地) 中检查 ImageManager。 上面 方法首先要求图像管理器创建一个图像以便为用户显示。 方法将控制的一些属性作为参数,CroppedImageWidth/height 告诉逻辑所需的结果宽度/高度应该是什么。 SourceImage 包含控件的用户设置的字节缓冲区源图像。 JpegQuality 属性定义JPEG应该在哪个质量中呈现。 这里方法还计算裁剪区域的默认值。

最后但并非最不重要的是,我们将缓冲区保存到缓存。 缓存代码是这个控件中相当大的一部分,但是我不会详细讨论。 你需要知道的是,我将GUID存储在控制状态视图中,并且使用这里GUID作为缓存键。

应用层还负责裁剪图像的逻辑。

我们已经注册了所有的脚本和资源,我们也创建了服务器端映像。 但是,我们如何将映像呈现给客户端? 控件的用户可以创建一个ASPX页,该页将返回一个图像。 但是,记住这个控件的想法是,我们只希望用户将一个控件拖到页面上,它应该完全。 强制用户创建ASPX页并不是 MATCH的配置文件。

因此,解决方案是 IHttpHandler 接口。

你可以 register 任何HTTPHandler和参考它的路径,在 web.config。

<addpath="CropImage.axd"verb="*"type="Anders.Web.Controls.ImageCropperHttpHandler"validate="false"/>

当 ASP.NET 获取特定路径的请求时,在本例中," CropImage.axd",它将实例化在 type 属性中定义的类。

HTTPHandler类的惟一要求是它实现了 IHttpHandler 接口。

publicclass ImageCropperHttpHandler : IHttpHandler
{
 publicbool IsReusable
 {
 get { returntrue; }
 }
 publicvoid ProcessRequest(HttpContext context)
 {
 string cacheId = context.Request.QueryString["cropCacheId"];
 byte[] buffer = context.Cache[cacheId] as byte[];
 context.Response.ContentType = "image/jpeg";
 context.Response.OutputStream.Write(buffer, 0, buffer.Length);
 }
}

属性告诉ASP引擎是否可以重用处理程序的实例,或者是否需要为类的每个请求重新创建实例。 在这种情况下,完全可以重用它,因为它是无状态的。

当ASP引擎获取请求时调用 ProcessRequest 方法。 它提供当前的HTTP上下文,这是你获取当前 ASP.NET 范围所需的全部内容。

所有方法都是解析 cropCacheId 查询字符串,并使用输出流输出图像。

使用HTTPHandlers的唯一缺点是,它们要求你在 web.config 中对数据进行 register 处理。 因此我们添加一个方法来验证控件的用户是否已经完成了。

privatevoid CheckForHandler()
{
 if (httpHandlerPath == null)
 {
 HttpHandlersSection handlerSection = 
 WebConfigurationManager.GetWebApplicationSection("system.web/httpHandlers") 
 as HttpHandlersSection;
 bool foundHandler = false;
 Type type = typeof(ImageCropperHttpHandler);
 string handlerName = type.ToString();
 string fullHandlerName = type.AssemblyQualifiedName;
 foreach (HttpHandlerAction action in handlerSection.Handlers)
 if (action.Type == handlerName || action.Type == fullHandlerName)
 {
 foundHandler = true;
 httpHandlerPath = action.Path.StartsWith("~")? 
 string.Empty : "~/" + action.Path;
 break;
 }
 if (!foundHandler)
 thrownew ApplicationException(string.Format("The HttpHandler {0} is" + 
 " not registered in the web.config", handlerName));
 }
}

这里方法从 CreateChildControls 方法调用,并且在每个应用程序范围中只调用一次。 如果它包含正确的类型,它将检查路径并使用该路径作为图像路径。 如果它不存在,我们抛出一个 ApplicationException

最后一个说明,这里控件的所有属性都使用视图状态。 我看到很多人使用标准属性的文章。 如果每次加载页面时设置属性,这可能工作。 但是,这不是标准. NET 控件工作的方式。 你应该始终使用视图状态实现属性,不决定控件的用户是否喜欢视图状态。 如果他不想使用视图状态,那就只是把它关掉。

历史记录

  • 版本 1.
  • 版本 1.1 ( 添加了 IPostBackDataHandler 接口,并增加了 HttpHandler web.config 寄存器的验证)。
  • 固定 Bug ( 新图像上传没有刷新控件)。

控制  图像  CROP  
相关文章