65.9K
CodeProject 正在变化。 阅读更多。
Home

“胶片盒”或目录视图相册示例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (50投票s)

2005年10月20日

CPOL

10分钟阅读

viewsIcon

346795

downloadIcon

7033

一种照片图像画廊浏览模板 - 目录视图风格。

Sample Image

引言

本文演示了一个照片画廊模板,类似于 Windows XP 幻灯片文件夹视图,具有幻灯片播放功能和运行时“即时”生成缩略图图像的功能。作为一个选项,还演示了如何使用基本的 XMLHttpRequest 通过 HttpHandler 从服务器获取所需数据。演示位于此处

背景

网络上有很多照片画廊,但令我惊讶的是,我无法找到一个小型项目所需的确切功能。大多数照片相册都显示类似于“图标”视图的缩略图画廊,当图像数量较多时,这通常会占用大量屏幕空间。无论如何,我的客户要求以缩略图“条带”的形式显示图像,并可滚动浏览,同时在下方显示一些带有详细信息的大图像,这给了我开发此功能的理由。在我看来,这种类型的画廊非常适合,例如,展示物品目录等。

这里的主要思想是在一行中显示画廊中的小图像,并可以来回滚动,并在下方放置一个大图像,类似于 WinXP 文件夹的“幻灯片视图”,或类似于目录视图。当用户点击小图像时,他会得到一个大图像和一些关于产品、物品或照片的详细信息,这些信息是从数据库或数据文件中提取的。(将数据与图像链接是另一个问题。)

使用代码

页面包含以下控件:一个带有 HyperLink 项的 Datalist,用于显示文件夹列表并在画廊之间导航。它绑定一个包含照片/图像的文件夹数组。主照片文件夹的名称在 Web.config 文件中设置。

<add key="photofolder" value="photo"/>

页面加载时,Datalist 将填充主照片文件夹中的子目录名称,每个 DataItem 都包含指向相应文件夹的链接。子文件夹名称作为参数添加到 TargetUrl。例如,将我们的页面切换到所需文件夹的 URL 看起来像 PhotoGallery.aspx?G="foldername"。如果没有参数,页面将使用 GetPhotos() 方法加载第一个画廊和其中的第一张图像。

所以,我们有主照片目录中的文件夹列表。接下来是什么?我们只想显示部分内容,例如 10 张图像,并表明还有更多内容可供显示。我使用 Repeater 控件,因为它是一个简单的列表控件,我只需要在其中列出图像。主要的挑战是为 Repeater 控件创建“页面”,但在网上搜索后,我找到了Harrison Enholm 的一篇文章。主要思想是首先将我的图像地址数组绑定到 PagedDataSource,设置 PageSizePageNumber 等,然后将 Repeater 绑定到它。简单但有效。

现在,我们可以绑定数据并使用 ViewState 来跟踪页面等。如果只有一个页面,导航控件可以根据客户端偏好隐藏或禁用。我在这里放置了一些导航控件,如“转到页面:#”、“转到最后一页/第一页”等。

GetPhotos() 是根据页面参数从目录获取所需图像的主要方法。

此方法将遍历所选目录(取决于参数 ?G=""),并使用 System.IO.Directory.GetFiles 方法获取图像名称的 string 数组。有了名称,我们可以将它们用作图像控件的源、大图像的链接地址等。然后,我们将此数组用作 Repeater 控件的数据源。同时,我们将用页码填充 DropDownList dlPages,我们将使用它直接跳转到任何页面。例如,当您有超过 10 页时,此选项很好。

Repeater 中,每个项目都是一个带有 src 属性的 <img>

<a><img id=iPhoto runat=server 
  src='<%# "ThumbGen.ashx?path="+Container.DataItem%>' 
  title='<%# Container.DataItem %>' 
  width="69" height="90" style="cursor:hand" 
  class=photoover>
</a>
private void GetPhotos()
{
    string photodir = 
      System.Configuration.ConfigurationSettings.AppSettings["photofolder"];
      //or get from config file

    string curGallery="";
    // use convention G is parameters to switch between galleries
    if (Request["G"]!=null)
    {
        curGallery=Request["G"]; //find where we are
    }
    else
    {
        curGallery=
          System.IO.Directory.GetDirectories(Server.MapPath(photodir))[0];
        //if no parameters show first directory

        string parent=(string) ViewState["parent"];
        curGallery=curGallery.Substring(parent.Length);
    }
    // try get all files in folder
    try
    {
        //get thumbnails and photos arrays in current directory
        string[] photos = System.IO.Directory.GetFiles(
                 Server.MapPath(photodir+"/"+curGallery),"*.jpg");
        // this array used if we have separate files as
        // thumbnail images. Convention here to add _thumb to file name
        
        //string[] thumb=System.IO.Directory.GetFiles(
        //   Server.MapPath("photo/"+curGallery),"*_thumb*");
        //get directory info itself
        int i=System.IO.Directory.GetParent(
              Server.MapPath(photodir)).FullName.Length;
        //
        //get files names/ relative path
        //just get actual files names for thumbnails and photos
        //just removing extra path, leaving only catalog and file name
        for (int ix=0; ix<photos.Length; ix++)
        {
            photos[ix]=photos[ix].Substring(i+1);
        }
        //ArrayList gallery=new ArrayList(photos);
        
        //find how many photos and how many pages to create pages
        int num=photos.Length;//it is number of photos
        //int num=thumb.Length; old usage
        lblPage.InnerText=num.ToString();
        if (num>9) imgbNext.Visible=true;
    
        //here we are using paged datasource
        PagedDataSource objPds = new PagedDataSource();
        //
        objPds.DataSource = photos; //we will show thumbnails
        
        // Indicate that the data should be paged
        objPds.AllowPaging = true;

        // Set the number of items you wish to display per page
        objPds.PageSize = 10;
        //fixed page size, other option 
        //to control through drop down box.

        objPds.CurrentPageIndex = CurrentPage;
        ViewState["Pages"]=objPds.PageCount;
        //fill pages values for dropdown list
        //do it only first time
        if (!Page.IsPostBack)
        {
            dlPages.Items.Clear();
            for (int ip=1;ip<=objPds.PageCount;ip++)
            {
                dlPages.Items.Add(ip.ToString());
            }
        }
        else
        {
            //list already there, so just set the right page
            //dlPages.SelectedIndex=CurrentPage;
        }

        lblPage.InnerText=" Page "+(CurrentPage+1).ToString() + 
                          " of "+objPds.PageCount.ToString();
        //control visibility of next/prev buttons
        imgbPrev.Enabled = !objPds.IsFirstPage;
        //imgbPrev.Visible=!objPds.IsFirstPage;
        imgbNext.Enabled = !objPds.IsLastPage;
        //imgbNext.Visible = !objPds.IsLastPage;

        repPhotoG.DataSource=objPds;
        //
        //DIV1.InnerText=thumb.
        //gallery.Add()
        repPhotoG.DataBind();
        //we also will use item data bind events 
        //to add some links to thumbnails

    }
    catch (Exception ex)
    {
        //do something with error
    }

    //imgLarge.Attributes["onfilterchange"]="fade()";
}

最初,我创建了具有一些独特名称的独立缩略图图像,然后解析文件名以仅选择目录中的缩略图而不是大图像,并将列表绑定到我的 Repeater 模板。然而,从逻辑上讲,即时生成它们似乎更好,可以方便用户。因此,我使用了 System.Drawing 类和 getThumbnail 方法来获取小图像。现在图像的 src 属性看起来像 “src=<% ''ThumbGen.ashx?path="+Container.DataItem %>”。ThumbGen.ashx 是一个 HttpHandler,用于处理即时创建图像。它是一个相当简单的处理程序,其任务是创建一个小图像并使用图像路径作为输入参数返回字节流。一些详细信息如下所示。

public class ThumbGen : IHttpHandler
{
    public void ProcessRequest (HttpContext context)
    {    
        if (context.Request["path"]!=null)
        //we have a pth try process image
        {
        
            string img=(string)context.Request["path"];
            //here is path for application dir
            string path1=context.Server.MapPath(context.Request.ApplicationPath);
            path1=context.Server.MapPath(img);
            //img=path1+"\\"+img; //full path to image
            
            img=path1;
            System.Drawing.Image.GetThumbnailImageAbort myCallback =
            new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
            
            //get bitmap from file
            Bitmap bitmap = new Bitmap(img);
            
            // process image, create thumbnail.
            // Try use to reduce size, instead of GetThubmnail
            ImageFormat imgformat=bitmap.RawFormat;
            //we will use fixed size for picture, in our case 69x90
            //but easy to add parameters such as width and calculate height
            /////////////////this code borrowed from Rick Strahl,
            //                        http://west-wind.com/weblog
            //works better than NET method 
            //GetThumbnail() when need transparent gif
            //Bitmap bmpOut=new Bitmap(70,90);
            //Graphics g=Graphics.FromImage(bmpOut);
            //g.InterpolationMode=System.Drawing.Drawing2D.
            //        InterpolationMode.HighQualityBicubic;
            //g.FillRectangle(Brushes.White,0,0,69,90);
            //g.DrawImage(bitmap,0,0,69,90);
            ///////////////////////////////////////////////////

            // ALternatively you can use 
            System.Drawing.Image imgOut=
              bitmap.GetThumbnailImage(70, 90, myCallback, IntPtr.Zero);
            bitmap.Dispose();
            
            //but seems creating Black background for transparent GIF's
            //////////////////return our image back to stream
            context.Response.ContentType="image/jpeg";
            imgOut.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            //bmpOut.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            //bmpOut.Dispose();
            imgOut.Dispose();
        }

我保留了注释掉的代码,我也用过,所以你可以尝试不同的方法来生成缩略图。我使用了 .NET 框架中可用的标准方法。另一种方法是首先创建白色背景,然后在其上加载位图。同时,我使用了硬编码的缩略图图像大小,但同样,它也可以作为可选参数传递给处理程序,或者根据图像方向(横向或纵向)进行计算。例如,可以添加更多参数,如缩略图图像的大小、宽度或高度等。在我的情况下,横向图像会看起来扭曲。最后,每个小图像都将“即时”生成并返回到页面。

此外,还有一些用于缩略图和大照片的微软滤镜。所有缩略图照片都显示为灰色,并在鼠标悬停时变为彩色。大图像在更改时具有淡入淡出效果。为了避免“闪烁”,页面过渡效果应用于整个页面,但更适合只应用于缩略图。(可以尝试的地方。)

获得缩略图画廊后,我们仍然需要决定如何显示大图像。为此,我使用 ItemDataBound 事件。在这里,我们可以在 Repeater 中找到图像,然后使用字符串操作,将绝对路径转换为相对路径(例如, вместо c:\\foldername\filename 只需 /<virtualfolder>/<filename>),并为每个图像添加一个 JavaScript 函数 OnClick 来显示大图像。相对图像路径名被传递给 JavaScript 函数。

img.Attributes["onclick"]="showImg('"+IMGpath(img.Src)+"')";

这是JavaScript函数

function showImg(imgName) { 
  imgOn = ("" + imgName);
  document.imgLarge.src = imgOn;
  hdr.innerHTML=imgName; document.imgLarge.filters[0].Apply();
  document.imgLarge.filters[0].Play();
}

因此,当用户点击小图像时,大图像 imgLargesrc 属性将改变,并显示全尺寸照片。

同时,我决定检查第一张图片的大小,并利用这些信息来改变我的大图片的大小,以获得一个比例协调的图片。最初,我所有文件夹中的所有图片都是相同大小的,这对我来说很好,但在现实生活中,图片可能具有不同的大小和方向。在我的情况下,它们应该在一个文件夹中保持相同的大小。也许这不是目前最好的方法,最好在将其分配给大图片之前在客户端调整每张图片的大小。

为此(找出图像大小),我使用从第一张照片创建的 Bitmap 来获取宽度与高度的比例,并按比例改变大图像的大小。

private void repPhotoG_ItemDataBound(object sender, 
        System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
    //finding repeater item Image
    System.Web.UI.HtmlControls.HtmlImage img= 
       (System.Web.UI.HtmlControls.HtmlImage)e.Item.FindControl("iPhoto");
    //create a relative path for image and add onclick function
    img.Src=img.Src.Replace('\\', '/');
    img.Attributes["onclick"]="showImg('"+IMGpath(img.Src)+"')";
    
    //some visual effects using MS filters
    img.Attributes["onmouseover"]="this.className='thumb'";
    img.Attributes["title"]=img.Src;
    img.Attributes["onmouseout"]="this.className='photoover'";
    //here we are trying to get size of images 
    //within directiry to set a more proportional
    //thumbnail. The rule to have all images within folder the same size.
    if (e.Item.ItemIndex==0)
    {
        imgLarge.ImageUrl=IMGpath(img.Src); //show first photo in folder
        string path=Server.MapPath(imgLarge.ImageUrl);
        Bitmap newimg = new Bitmap(path);
        double ratio= (double)newimg.Width/(double)newimg.Height;
        lblName.InnerText="size:"+newimg.Width.ToString() 
                          +"-"+newimg.Height.ToString();
        lblName.InnerText=lblName.InnerText+"Resolution:"+
                          newimg.VerticalResolution.ToString();
        imgLarge.Style["Width"]=(imgLarge.Width.Value*ratio).ToString();
        newimg.Dispose();
    }
}

就是这样。要在实际生活中使用此画廊,只需指定主文件夹名称,将 PhotGallery.aspx 放在 Web 服务器上,复制 bin 文件夹,并创建一个包含图像的子文件夹,并且不要忘记修改 Web.config 文件(添加配置键和 HttpHandler 部分)。应用程序将处理其他事项。

更新 (2005年10月24日)

增加了幻灯片播放功能。同样,创建幻灯片播放可能有几种不同的方法。我的意图是不使用任何回发,并在客户端完成所有操作。幻灯片播放背后的想法是在客户端有一个图像列表,然后根据当前选择动态更改我们的 image.src 属性。

为此,我在页面上放置了一个隐藏的 HtmlListBox,以保留一个文件夹中所有图像信息的列表。这非常简单。当我们正在服务器端绑定 Repeater 时,我们将 ListBox 与图像路径绑定。当页面返回到客户端时,我们将拥有将在幻灯片播放中播放的完整图像列表。之后,我们只需要添加一些脚本来控制播放/停止和幻灯片播放的外观。有一组不同的 Java 函数负责幻灯片播放。所有幻灯片播放功能都基于 JavaScript/DHTML(文件 gallery.js)。

工作原理:点击“启动幻灯片播放”将激活幻灯片播放,并在图像顶部显示“幻灯片控制框”。 (上一张/下一张/暂停/全尺寸/停止)对于“全尺寸”功能,我们使用 DHTML,因此我们可以更改元素的布局和位置。因此,当用户点击“全尺寸”按钮时,他将获得图像在中间(位置将使用客户端屏幕尺寸计算)。

为了简化起见,停止幻灯片播放,我只使用了常规的 ASP.NET 按钮“回发”,它将刷新页面并重新加载画廊。否则,我们需要一个与“全尺寸”相反的函数,并将所有内容恢复。另一种选择可能是一个单独的“全屏”窗口。

////////////////
//start slideshow, using list of images from list box
function SlideShow()
{
    document.imgLarge.filters[0].Duration=2.5;
    document.imgLarge.filters[0].Apply();
    //alert(document.getElementById("listImg").options[i].text);
    document.getElementById('hdr').innerText=
       document.getElementById("listImg").options[i].text;
    document.imgLarge.src=document.getElementById("listImg").options[i].text;
    document.imgLarge.filters[0].Play();
    if (i>=((document.getElementById("listImg").length)-1))
    {
        i=0;
    }
    else
    {
        i=i+1;
    }
}

“AJAX”方式创建幻灯片

为了探索AJAX技术,在HttmlHandler中添加了新选项。如果我们使用参数“Dir'”(将显示“ThumbGen.asxh?Dir=___”),这意味着我们要运行“幻灯片”的目录名称,处理程序将向响应流返回一个包含所有图像名称的XML文档。我们将使用它通过XtmlHTTP请求从客户端进行远程回调,并获取所需的图像列表。我们得到的结果是一个数组,但请注意,我们将转换返回流以获取数据的XML表示。这将为您提供更多选项来处理客户端文档,尤其是当您有来自数据库的数据时。例如,如果我们有一个DataSet,我们可以使用方法.GetXML()来获取XML格式。否则,我们只需调用ThumbGen.ashx并附带所需的参数,然后返回一个包含照片名称的“列表”。然后我们在客户端填充Arrays。就是这样,我们有了幻灯片播放的源,并将使用与之前相同的脚本来运行它。

我尝试这种方式只是为了看看我们如何使用 XmlHtml 对象和 HtmlHandler。以类似的方式,我们可以从数据库请求数据。在页面上,您还可以找到一个隐藏的下拉列表框,仅作为示例,尝试如何使用请求填充它。我没有将它用于任何其他目的。提示:例如,如果我们不指定图像的宽度,它将自动按比例缩放高度。

我相信还有很大的改进空间,但这只是一个构建画廊的模板,以及Javascript/DHTML和远程回调技术的不同示例。

function ProcessResponse()
{
    if(req.readyState == 4)
    {
        if(req.status == 200) //analog "OK",
        {
        //here we are processing result,////////////////////////
        //
        var p=req.responseXML.documentElement;
        
        ///alert(req.responseXML); -testing purpose
        var ddlTest = document.getElementById("test");
        //var limg=document.getElementById("listImg");
        
        for (var count = ddlTest.options.length-1;count >-1; count--)
        {
            ddlTest.options[count] = null;
        }
        var photos = p.getElementsByTagName('photos');
        var text;
        //here we are trying to create array in memory

        //this example just to show how we can 
        //fill selection box from callback result.
        //After callback we got XMl document-table 
        //and we can use it to fill table/select/list etc.
        var listItem;
        var l=photos.length;
        for (var count = 0; count < photos.length; count++)
        {
            text = (photos[count].textContent ||
            photos[count].innerText || photos[count].text);
            listItem = new Option(text, text, false, false);
            ddlTest.options[ddlTest.length] = listItem;
        }
    }
    else
    {
        alert("Error retrieving data!" );
    }

    ////// this script responsible to fill Album array
    // by data returned from client in our case
    // it is a images pathes.
    
    for (i = 0; i<l; i++)
    {
        text2 = (photos[i].textContent ||
        photos[i].innerText || photos[i].text);
        Album[i]= new Image();
        Album[i].src = text2;
    }
    //now we have array with images. call slide show
    slide=0;
    //call slide show and show menu on top of image
    AjaxSlideShow();
    SlideMenu();

}
function AjaxSlideShow()
{
    StopSlide();

    if (slide>=Album.length)
    {
        slide=0;
    }

    document.imgLarge.filters[0].Duration=2.5;
    document.imgLarge.filters[0].Apply();
    document.imgLarge.src=Album[slide].src;
    document.imgLarge.filters[0].Play();
    var tm=document.getElementById("slidetime").value*1000
    oInterval=window.setTimeout("AjaxSlideShow()",tm);
    
    //nr=nr+1;
    slide++;
}

关注点

我正在考虑通过添加一些方法来扩展处理程序,以从数据库获取图像并将图像与数据文件或数据库绑定。我仍在寻找改进缩略图加载的方法,以避免“闪烁”并获得更流畅的外观。我还留下了一个指向“幻灯片播放”功能的链接:播放目录中的所有文件——这有待实现。我还注意到,当页面刚加载时,滤镜最初不工作,但在下一次点击后才开始工作。我还想尝试将其转换为具有一些外部属性的用户控件。总有办法添加东西,改进。这就是我喜欢编程的原因,它是无止境的。

历史

  • 2005年8月 - 基本项目。
  • 2005年10月 - 为可用性重写。
  • 2005年10月24日 - 添加了幻灯片播放功能。
© . All rights reserved.