今天看到一篇比较好的文章,特此翻译出来,原文地址:http://www.codedigest.com/Articles/ASPNET/232_Picasa_Style_Photo_Album_Using_ListView_Control_in_ASPNet_35.aspx
源码下载: http://www.codedigest.com/Articles/ArticleFiles/ZIPS/232.zip
ListView 是ASP.Net 3.5中新发布的数据绑定控件. 我在 ListView Control in ASP.Net 3.5 一文中初步介绍了其用法.ListView是数据绑定控件中最灵活的控件,可以用任何自定义格式显示数据。此文将用ListView创建Google’s Picasa 类型相册. 但是 picasa 功能强大,我们只是实现显示相册和图片。
不需要介绍Google’s Picasa 了,在相片共享方面它已得到广泛应用。Picasa 是基于web 的 相册,用户可以再此创建相册、上传相片,与亲朋好友共享。阅读此片文章之前,最好对它的工作方式做一些了解,至少要知道我们用ListView要实现的功能。Picasa 显示相册的列表,以其中一种相片做为封面(图1)。点击相册,将会进入显示上传相片缩略图的列表界面(图2)。点击缩略图,可以看到原始相片。在相片全图下,可以向前后导航(图3). 我不善于UI设计; 对如此差的UI表示抱歉。
接下来,我们将用ListView实现Picasa类似的相册。如前所说,Picasa提供了很多功能,我们仅仅实现上面所说的功能,来帮助理解ListView的灵活性。
进入实现之前,帮助理解,我们将看看存储相册信息的数据库如何设计。参考下图,数据库包含2张表:
1. Album(相册) – 存储相册信息。 DefaultPhotID字段存储封面相片。
2. PhotAlbum(相片) –包含相册的所有信息。Photo列存储相片路径。根据需要你可以改变字段大小。
接下来,进入实现部分。
相册页面,我们将每行平铺显示3个相册,例如,水平重复显示相片,参考图1。要建立新相册,在ListView的最下面有个“Create New Album” 链接。点击此链接,将移动到ListView的最后一列,下面我们来看看如何实现。
要在ListView中显示数据,首先,我们必须要定义好如下模版,Layout Template 和 Item Template,可在ListView Control in ASP.Net 3.5 了解更多。
另外,我们用Group Template 来平铺分组。 用GroupItemCount属性来限制每行显示项的数量。
要显示“Create New Album” 链接, 要用到ListView 的另一个模版InsertItemTemplate。InsertItemTemplate中的内容将被显示为新增项。 设置InsertItemPosition属性 决定InsertItemTemplate的显示位置。此文中是LastItem。ListView 最终代码如下:
<asp:ListView ID="lvAlbums" runat="server"
DataSourceID="SqlDataSource1" GroupItemCount="3"
InsertItemPosition="LastItem">
<LayoutTemplate>
<table border="1">
<tr ID="groupPlaceholder" runat="server">
</tr>
</table>
</LayoutTemplate>
<GroupTemplate>
<tr>
<td ID="itemPlaceholder" runat="server">
</td>
</tr>
</GroupTemplate>
<ItemTemplate>
<td id="Td3" width="150px" height="150px" align="center" style="background-color: #e8e8e8;color: #333333;">
<asp:HiddenField ID="hfPhotoID" runat="server" Value='<%# Eval("DefaultPhotID") %>' />
<a href='<%# "Photos.aspx?AlbumID="+Eval("AlbumID") %>'>
<asp:Image CssClass="Timg" runat="server" ID="imPhoto" ImageUrl='<%# "ThumbNail.ashx?ImURL="+Eval("Photo") %>' />
</a>
<br />
<b><asp:Label ID="lblAlbumName" runat="server" Text='<%# Eval("AlbumName") %>'></asp:Label> </b>
</td>
</ItemTemplate>
<InsertItemTemplate>
<td id="Td3" width="150px" height="150px" runat="server" align="center" style="background-color: #e8e8e8;color: #333333;">
<a href="CreateAlbum.aspx">
Create New Album
</a>
</td>
</InsertItemTemplate>
</asp:ListView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
SelectCommand="SELECT Album.AlbumID, Album.DefaultPhotID, Album.AlbumName, PhotAlbum.Photo FROM Album INNER JOIN PhotAlbum
ON Album.DefaultPhotID = PhotAlbum.PhotoID">
</asp:SqlDataSource>
在上面代码中, 我设置GroupItemCount 为 3。增加这个值可以增加相册每行显示的数量。数据绑定用的是SqlDataSource。
既然,相册封面为相册中的相片(Album表DefaultPhotID列), 要将缩略图做为相册封面. 要这样做, 就要用HttpHandler[ThumbNail.ashx] 将原始相片转换为缩略图.
下面就是HttpHanlder 实现从原始图生成(100x100)缩略图.
<%@ WebHandler Language="C#" Class="ThumbNail" %> using System; using System.Web; using System.Drawing; using System.IO; public class ThumbNail : IHttpHandler { public void ProcessRequest (HttpContext context) { string imageurl = context.Request.QueryString["ImURL"]; string str = context.Server.MapPath(".") + imageurl; Bitmap bmp = new Bitmap(str); System.Drawing.Image img = bmp.GetThumbnailImage(100, 100, null, IntPtr.Zero); MemoryStream ms = new MemoryStream(); img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); byte[] bmpBytes = ms.GetBuffer(); img.Dispose(); ms.Close(); context.Response.BinaryWrite(bmpBytes); context.Response.End(); } public bool IsReusable { get { return false; } } }
下一步, 点击 “Create New Album” 链接到CreateAlbum.aspx 页面创建新相册. 一旦相册建立之后, 将显示(ImageUpload.aspx) 页面进行相片上传. 参考下图:
CreateAlbum.aspx
ImageUpload.aspx
我不打算讨论这些页面的实现方法,不在此文范畴。你可以下载代码查看。
最后,当用户点击相册,将跳到 (photos.aspx)页面,在此显示相片缩略图列表。
在photos.aspx 页面, 将从QueryString中获取AlbumId。同样, 用GroupTemplatee每行显示3个.参考图2. 在图2,相片缩略图列表(右边内容)又ListView显示,而左边的显示相册信息的内容是分开处理的.
要显示每幅照片的缩略图, 同样用HttpHandler [ThumbNail.ashx]. 可以考虑在上传时就存储缩略图,可以改进性能。本方法中,用户每次查看相册都会生成缩略图,这可以通过在上传时就存储缩略图才避免。可以参考此处 Upload image to file system and create thumbnail image on the fly in C# and ASP.Net
最终, ListView 类似如,
<asp:ListView ID="lvPhotos" runat="server" DataKeyNames="AlbumID" DataSourceID="SqlDataSource1" GroupItemCount="3"> <LayoutTemplate> <table ID="groupPlaceholderContainer" runat="server" border="0" cellpadding="0" cellspacing="0"> <tr ID="groupPlaceholder" runat="server"> </tr> </table> </LayoutTemplate> <GroupTemplate> <tr ID="itemPlaceholderContainer" runat="server"> <td ID="itemPlaceholder" runat="server"> </td> </tr> </GroupTemplate> <ItemTemplate> <td runat="server" align="center" style="background-color: #e8e8e8;color: #333333;"> <a href='<%# "PhotoViewer.aspx?PhotoID="+Eval("PhotoID")+"&AlbumID="+ Eval("AlbumID") %>'> <asp:Image CssClass="Timg" runat="server" ID="imPhoto" ImageUrl='<%# "ThumbNail.ashx?ImURL="+Eval("Photo") %>' /> </a> </td> </ItemTemplate> </asp:ListView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>" SelectCommand="SELECT [PhotoID], [Photo], [PhotoName], [AlbumID] FROM [PhotAlbum] WHERE ([AlbumID] = @AlbumID) ORDER By [PhotoID] ASC" onselected="SqlDataSource1_Selected"> <SelectParameters> <asp:QueryStringParameter DefaultValue="1" Name="AlbumID" QueryStringField="AlbumID" Type="Int32" /> </SelectParameters> </asp:SqlDataSource>
同样用SqlDataSource做为数据绑定源。
要显示全图,用户点击缩略图将加载全图,带有next和previous按钮. 下节我们会实现这个功能。
点击缩量图将加载ListView中的全图,(参考PhotoViewer.aspx) 以AlbumId 和PhotoID 做为参数。参考图 3. 同时还显示next 和previous 按钮来做相片导航。不同于其他两个页面,我们用代码进行数据绑定。
在这种情况下,我们每次在ListView中显示一张相片,如图3所示。同样用GroupTemplate 在ListView显示相片。
也可以不用GroupTemplate. 用GroupTemplate,我们可以用GroupItemCount 属性来增加每次显示的相片的数量 .
Next 和 Previous 按钮在DataPager中显示. 阅读我的文章- Paging in ListView in ASP.Net 3.5 ,了解更多用DataPager分页ListView.
所以, ListView应该像这样,
<table> <tr> <td> <asp:ListView ID="lvPhotoViewer" runat="server" GroupItemCount="1"> <LayoutTemplate> <table ID="groupPlaceholderContainer" runat="server" border="1"> <tr ID="groupPlaceholder" runat="server"> </tr> </table> </LayoutTemplate> <ItemTemplate> <td id="Td4" align="center" style="background-color: #eeeeee;"> <asp:Image runat="server" ID="imPhoto" Height="450px" Width="450px" ImageUrl='<%# "~"+Eval("Photo") %>' /> <br /> <asp:Label ID="DefaultPhotIDLabel" runat="server" Text='<%# Eval("PhotoName") %>' /> </td> </ItemTemplate> <GroupTemplate> <tr ID="itemPlaceholderContainer" runat="server"> <td ID="itemPlaceholder" runat="server"> </td> </tr> </GroupTemplate> </asp:ListView> </td> </tr> <tr> <td align="center"> <asp:DataPager ID="DataPager1" runat="server" PagedControlID="lvPhotoViewer" PageSize="1" onprerender="DataPager1_PreRender"> <Fields> <asp:NextPreviousPagerField ButtonType="Link" PreviousPageText="<< " NextPageText=" >>" /> </Fields> </asp:DataPager> </td> </tr> </table>
CS代码:
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { string photoID = Request.QueryString["PhotoID"]; string albumID = Request.QueryString["AlbumID"]; ViewState["hfAlbumID"] = albumID; //Get Page number by passing photo id int index = GetPageNumber(int.Parse(photoID), int.Parse(albumID)); DataPager1.SetPageProperties(index, 1, true); } } /// <summary> /// Since the pagesize is 1 the row number can be taken as page number /// </summary> /// <param name="PhotoID"></param> /// <param name="AlbumID"></param> /// <returns></returns> public int GetPageNumber(int PhotoID,int AlbumID) { SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString); con.Open(); SqlCommand com = new SqlCommand("SELECT PageNumber FROM (SELECT row_number() Over (order by photoid asc) AS PageNumber,photoid,Albumid "+ " FROM PhotAlbum where Albumid=" + AlbumID.ToString() + ") As Photos where photoid=" + PhotoID.ToString() + " and Albumid=" + AlbumID.ToString(), con); SqlDataReader dr = com.ExecuteReader(); int pageno = 1; if (dr.Read()) { pageno = int.Parse(dr["PageNumber"].ToString()); } dr.Close(); con.Close(); return (pageno - 1); } public DataTable GetPhoto(string query) { SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString); SqlDataAdapter ada = new SqlDataAdapter(query, con); DataTable dtEmp = new DataTable(); ada.Fill(dtEmp); return dtEmp; } protected void DataPager1_PreRender(object sender, EventArgs e) { lvPhotoViewer.DataSource = GetPhoto("Select * from PhotAlbum where AlbumID = " + ViewState["hfAlbumID"].ToString()); lvPhotoViewer.DataBind(); }
既然我们每次显示一张相片,例如 PageSize 为1,从SqlServer 2005中获取的page number 和 Row number 是一样的. 所以, GetPageNumber() 方法将返回缩略图的Row_Number() ,用来显示ListView的特定页.
Row_Number() 只适用于Sql Server 2005及以上版本. 我用行内查询绑定到数据库. 你可以用存储过程来提高性能,以及避免sql注入攻击。
本文代码下载:Download Source