zoukankan      html  css  js  c++  java
  • 图形、GDI + 和图表(在网页上嵌入动态图形)

           使用 Image.Save()方法将一个图像保存到一个响应流时,会覆盖所有 ASP.NET 要用到的控件。这有一个解决方案,可以使用 HTML 的 <img> 标签或者 Image Web 控件来链接到一个生成动态图像的 .aspx 文件。

           创建 GDI+ 图像通常比提供一个静态图像慢一个数量级,因此,使用 GDI+ 多次重复绘制图形按钮或其他元素绝不是一个好主意。(如果真要这么做,要考虑缓存或者保存生成的图像文件以提高性能。)

    使用 PNG 格式

           PNG 是一种通用格式,这种格式将 GIF 的无损压缩和 JPEG 的丰富色彩结合起来以支持高质量图像。

           动态生成的 PNG 图像有一个问题,即不能使用 BitMap.Save()方法。Response.OutputStream 是一个线性流,你只能从头到尾顺序写入数据。要创建一个 PNG 文件,.NET 需要能够在一个文件里来回前后的定位,需要一个可定位的流。

           解决方案也很简单,可以创建一个 System.IO.MemoryStream 对象(内存里的一个缓冲区),将图像保存到这个对象后,就能很容易的从 MemoryStream 复制数据到 Response.OutputStream 了。

    Response.ContentType = "image/png";
     
    MemoryStream mem = new MemoryStream();
    image.Save(mem,ImageFormat.Png);
     
    mem.WriteTo(Response.OutputStream);
     
    g.Dispose();
    image.Dispose();

    传递信息给动态图像

           当使用这个技术在网页里动态生成图形时,可以在网页中向动态生成图形的代码传递信息。

           下面的例子,使用这一技巧创建一个数据绑定列表,显示一个给定目录中每个位图的缩略图。

           这个页面需要设计为两部分:包含 GridView 的页面(多次调用缩略图页面来填充列表)和动态呈现一个单一缩略图的页面(多次被调用)。

           首先,设计创建缩略图的页面,为了尽可能通用,不应该硬编码任何关于要使用的目录和缩略图大小的信息,应从调用者处获取这些信息:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(Request.QueryString["X"]) ||
            string.IsNullOrEmpty(Request.QueryString["Y"]) ||
            string.IsNullOrEmpty(Request.QueryString["FilePath"]))
        {
            // There is missing data, so don't display anything.
            // Or return an image with some static error text.
        }
        else
        {
            int x = Int32.Parse(Request.QueryString["X"]);
            int y = Int32.Parse(Request.QueryString["Y"]);
            string file = Server.UrlDecode((Request.QueryString["FilePath"]));
            ...

           一旦获得了这些基本数据,就可以创建 BitMap 和 Graphics 对象了。本例中,BitMap 大小应该与缩略图大小相对应:

    Bitmap image = new Bitmap(x, y);
    Graphics g = Graphics.FromImage(image);

           Graphics 类会自动伸缩你的图像来适应这些宽窄,使用反锯齿技术来创建一个高质量的缩略图

    Bitmap image = new Bitmap(x, y);
    Graphics g = Graphics.FromImage(image);
     
    System.Drawing.Image thumbnail = System.Drawing.Image.FromFile(file);
    g.DrawImage(thumbnail, 0, 0, x, y);
     
    image.Save(Response.OutputStream, ImageFormat.Jpeg);
    g.Dispose();
    image.Dispose();
        }
    }

           接着,是在包含 GridView 的页面中使用这个页面。基本思路是用户输入目录路径并单击“提交”按钮,此时,你的代码可以使用 System.IO 类来做点事

    protected void btnShow_Click(object sender, EventArgs e)
    {
        DirectoryInfo dir = new DirectoryInfo(txtDir.Text);
        gridThumbs.DataSource = dir.GetFiles();
        gridThumbs.DataBind();
    }

           绑定了 FileInfo 数组之后,如何显示完全由 GridView 模板决定。本例中,需要显示两段信息,即文件的短名和对应的缩略图。短名只需要绑定到 FileInfo.Name 属性即可。缩略图需要使用 <img> 标签来调用动态图片页面,不过,构建正确的 URL 有一点点麻烦,因此将该工作交给网页类中的 GetImageUrl()方法来完成。

    protected string GetImageUrl(object path)
    {
        return "ThumbnailViewer.aspx?x=50&y=50&FilePath="
            + Server.UrlEncode((string)path);
    }
    <div>
        Directory:<asp:TextBox runat="server" ID="txtDir"></asp:TextBox>
        <br />
        <br />
        <asp:Button ID="btnShow" runat="server" OnClick="btnShow_Click" Text="Show Thumbnails" />
        <br />
        <br />
        <asp:GridView ID="gridThumbs" runat="server" AutoGenerateColumns="False" Font-Names="Verdana"
            Font-Size="X-Small" GridLines="None">
            <Columns>
                <asp:TemplateField>
                    <ItemTemplate>
                        <img alt="" src='<%# GetImageUrl(Eval("FullName")) %>' />
                        <%
    # Eval("Name") 
    %>
                        <hr />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </div>

    image

          文件路径以 URL 形式编码,这是因为文件名通常包含 URL 禁止的字符,比如空格。因此这一小小改变非常重要。

    使用 GDI+ 的自定义控件

           你可能已经想急于使用 GDI+ 来创建封装的很好的自定义控件,遗憾的是,ASP.NET 并没有为你在网页里嵌入 GDI+ 图像变得简单。

           如你所见,使用 GDI+ 需要创建一个独立的网页输出图像,然后使用 <img> 标签将这个页面的内容嵌入进来。因此,你不能简单的直接在一个网页上拖放一个使用了 GDI+ 的自定义控件。

           你可以做的是创建一个包装了 <img> 标签的自定义控件。这个控件提供一个方便的编程接口、包括属性、方法和事件。不过,该控件实际上并不能生成图像,它可以从它的属性获取数据,构建 URL 的查询字符串部分,然后将自己呈现为一个 <img> 标签,该标签指向真正完成图像生成的页面。

           这个自定义控件提供了更高级别的包装,这个包装抽象了传递信息到你的 GDI+ 页面的过程!

           也可以考虑使用 HTTP 处理程序来生成图像。你的图像生成器可以具有一个自定义的扩展名。

    1. 自定义控件类

           与任何自定义控件类一样,可以放在网站的 App_Code 文件夹中,或是放在一个单独的类库项目中。

           本例中的自定义控件类从 Control 类派生,而不从 WebControl 类派生。因为它不能支持富样式属性集,因为它仅仅呈现动态图形,而不是一个 HTML 标签

    public class GradientLabel : Control
    {
        public string Text
        {
            get { return (string)ViewState["Text"]; }
            set { ViewState["Text"] = value; }
        }
     
        public int TextSize
        {
            get { return (int)ViewState["TextSize"]; }
            set { ViewState["TextSize"] = value; }
        }
     
        /// <summary>
        /// 渐变的起始颜色
        /// </summary>
        public Color GradientColorStart
        {
            get { return (Color)ViewState["GradientColorStart"]; }
            set { ViewState["GradientColorStart"] = value; }
        }
     
        /// <summary>
        /// 渐变的结束颜色
        /// </summary>
        public Color GradientColorEnd
        {
            get { return (Color)ViewState["GradientColorEnd"]; }
            set { ViewState["GradientColorEnd"] = value; }
        }
     
        public Color TextColor
        {
            get { return (Color)ViewState["TextColor"]; }
            set { ViewState["TextColor"] = value; }
        }
     
        // 构造函数将属性设置成一些合理的默认值
        public GradientLabel()
        {
            Text = "";
            TextColor = Color.White;
            GradientColorStart = Color.Blue;
            GradientColorEnd = Color.DarkBlue;
            TextSize = 14;
        }
     
        protected override void Render(HtmlTextWriter writer)
        {
            HttpContext context = HttpContext.Current;
            writer.Write(string.Format(@"<img src='GradientLabel.aspx?Text={0}&TextSize={1}
                &TextColor={2}&GradientColorStart={3}&GradientColorEnd={4}' />",
                context.Server.UrlEncode(Text), TextSize.ToString(), TextColor.ToArgb(), 
                GradientColorStart.ToArgb(), GradientColorEnd.ToArgb()));
        }
    }

    2. GDI+ 图像呈现页面

           这个呈现页面有一个有趣的挑战:即文本和字体大小是动态提供的!因此,不可能使用一个固定大小的位图,太小会截断文本,太大会浪费过多的内存且传递页面太费时。

           解决方案是创建所需的 Font 对象,然后调用 Graphics.MeasureString 参数来判断要显示你的文本需要多少像素。唯一需要注意的是,要避免位图过大。为避免这个风险,呈现代码强制 800 像素高和宽的限制。(还可使用 DrawString()方法的另一个版本,即接受一个放置文本的矩形,有多行空间的话,这个方法会自动包装文本,允许显示超过几行的大量文本。)

    protected void Page_Load(object sender, EventArgs e)
    {
        string text = Server.UrlDecode(Request.QueryString["Text"]);
        int textSize = int.Parse(Request.QueryString["TextSize"]);
        Color textColor = Color.FromArgb(int.Parse(Request.QueryString["TextColor"]));
        Color gradientColorStart = Color.FromArgb(int.Parse(Request.QueryString["GradientColorStart"]));
        Color gradientColorEnd = Color.FromArgb(int.Parse(Request.QueryString["GradientColorEnd"]));
     
        // Define the font.
        Font font = new Font("Tahoma", textSize, FontStyle.Bold);
     
        // Use a test image to measure the text.
        Bitmap image = new Bitmap(1, 1);
        Graphics g = Graphics.FromImage(image);
        SizeF size = g.MeasureString(text, font);
        g.Dispose();
        image.Dispose();
     
        // Using these measurements, try to choose a reasonable bitmap size.
        // If the text is large, cap the size at some maximum to
        // prevent causing a serious server slowdown.
        int width = (int)Math.Min(size.Width + 20, 800);
        int height = (int)Math.Min(size.Height + 20, 800);
        image = new Bitmap(width, height);
        g = Graphics.FromImage(image);
     
        // 使用线性渐变封装 System.Drawing.Brush
        LinearGradientBrush brush = new LinearGradientBrush(
            new Rectangle(new Point(0, 0), image.Size), gradientColorStart, 
            gradientColorEnd, LinearGradientMode.ForwardDiagonal);
     
        // Draw the gradient background.
        g.FillRectangle(brush, 0, 0, width, height);
     
        // Draw the label text.
        g.DrawString(text, font, new SolidBrush(textColor), 10, 10);
     
        // Render the image to the output stream.
        image.Save(Response.OutputStream,ImageFormat.Jpeg);
     
        g.Dispose();
        image.Dispose();
    }

           除了文本的大小外,每边都增加了 20 像素,这允许每一位度都增加了 10 个像素。

    3. 测试效果

    <cc1:GradientLabel ID="GradientLabel1" runat="server" Text="Test String"
     GradientColorStart="MediumSpringGreen" GradientColorEnd="RoyalBlue">
    </cc1:GradientLabel>

    image 

           把信息从一个页面传送到另一个页面是 GDI+ 另一个不错的应用,但是对查询字符串大小的限制意味着它能处理的数据量较有限。对于较大的数据量,可以采用 Session 传递,Session 能传递任何可序列化的数据,不过也需要顾及到服务器的开销。

  • 相关阅读:
    JAVA中线程同步的方法(4种)汇总
    java
    指定的元素已经是另一个元素的逻辑子元素。请先将其断开连接。(解决问题总结)
    无法将类型为“System.Windows.Controls.SelectedItemCollection”的对象强制转换为类型“System.Collections.Generic.IList`1
    foreach---集合已修改;可能无法执行枚举操作。
    WPF_View中控件使用单例ViewModel
    判断s2是否能够被通过s1做循环移位(rotate)得到的字符串是否包含
    多列转1列 SqlServer 实现oracle10g的 wmsys.wm_concat()--for xml path('')
    异步对象(XMLHttpRequest)的帮助脚本
    在vs2010使用EF出现CS0012: 类型“System.Data.Objects.DataClasses.EntityObject”在未被引用的程序集中定义
  • 原文地址:https://www.cnblogs.com/SkySoot/p/2813368.html
Copyright © 2011-2022 走看看