控件代码: /Files/zhuqil/HyperlinkFileList.zip
在我主持过的一个Asp.Net论坛上,一个用户询问如何在一个页面上去通过hyperlink列出一个路径下的很多文件名,能使用户能点击它们。我认为这是可能是经常执行的动作,所以决定建立一个服务器控件来封装它。
起初,我尝试了Web用户控件,但我想能允许设置边框,字体,背景颜色等。使用Web用户控件,我要为每一个属性手动设置。俗话说:“你写的代码越少,你的错误越少了”,所以我要研究一个更好的方法。
我决定创建自己首个自定义服务器控件。我试着去继承一个label控件,但label控件不支持滚动条。 所以,我选择继承Panel控件。最终的控件具有Panel控件的所有属性(颜色,边框,滚动支持等),再加上一些我添加自定义属性。使用Panel控件将会尽最小的努力。
第一部分:自定义服务器控件
初始化的服务器控制相对容易。下面是最终的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;
[assembly: TagPrefix("EndWell", "EW")]
namespace EndWell
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:HyperlinkFileList runat="server">")]
[ToolboxBitmap("HyperlinkFileList.ico")]
public class HyperlinkFileList : Panel
{
[Bindable(true)]
[Category("Files List")]
[Description("The Title of the list of files")]
public string FilesTitle {get; set;}
[Bindable(true)]
[Category("Files List")]
[Description("The directory of the files to list: (~/Files/)")]
// these two built in editors were lacking:
//[EditorAttribute(typeof(System.Web.UI.Design.UrlEditor), typeof(UITypeEditor))]
//[EditorAttribute(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(UITypeEditor))]
[EditorAttribute(typeof(EndWell.DualModeFolderEditor), typeof(UITypeEditor))]
public string FilesDirectory { get; set; }
[Bindable(true)]
[Category("Files List")]
[Description("The filter for the files to show: (*.*)")]
public string FilesFilter { get; set; }
[Bindable(true)]
[Category("Files List")]
[Description("Text to show when there are no files")]
public string NoFilesText { get; set; }
// ---- Private vars --------------------------
private String[] m_FilesArray; // cached for performance
// ---- Default constants-------------------
const String DEF_FILES_DIR = "~/xml/";
const String DEF_FILES_FILT = "*.xml";
const String DEF_FILES_TITLE = "XML Files:";
const String DEF_NOFILES_TEXT = "";
// ---- Constructor --------------------------
public HyperlinkFileList()
{
// set defaults for our properties
FilesDirectory = DEF_FILES_DIR;
FilesFilter = DEF_FILES_FILT;
FilesTitle = DEF_FILES_TITLE;
NoFilesText = DEF_NOFILES_TEXT;
// Set defaults for panel properties
// I don't like the default width to be full screen
// And a border looks better
Width = new Unit("300px");
BorderStyle = BorderStyle.Solid;
BorderWidth = 1;
BorderColor = Color.Black;
// If height is set, force scroll bars to keep list
// from spilling over the panel/div boundaries.
if ((Height != null) && (ScrollBars == ScrollBars.None))
ScrollBars = ScrollBars.Auto;
// Allow multiple controls to be placed horizontally
// (normally each div get's its own line)
Style["display"] = "inline-block";
// add spacing outside the control
Style["margin"] = "0.5em";
// add space inside the control
Style["padding-left"] = "0.5em";
Style["padding-right"] = "0.5em";
Style["padding-bottom"] = "0.5em";
// top space usually comes from the title...
if (String.IsNullOrEmpty(FilesTitle) == true)
Style["padding-top"] = "0.5em";
}
// ---- RenderContents ----------------------------
//
// Spit out the HTML
protected override void RenderContents(HtmlTextWriter Output)
{
// output the title if one was set
if (String.IsNullOrEmpty(FilesTitle) == false)
{
Output.Write("<h3> "); // cosmetic spacing
Output.Write(FilesTitle);
Output.Write("</h3>");
}
GetFilesArray();
if (m_FilesArray.Length == 0)
{
Output.Write(HttpUtility.HtmlEncode(NoFilesText));
}
else
{
foreach (String OneFile in m_FilesArray)
{
HyperLink Link = new HyperLink();
Link.NavigateUrl = Path.Combine(FilesDirectory, Path.GetFileName(OneFile));
Link.Text = Path.GetFileNameWithoutExtension(OneFile);
Link.RenderControl(Output);
Output.WriteBreak();
}
}
}
// ---- GetFilesArray -------------------------
//
// Fill the m_FilesArray with a list of files
// either from disk or the cache
private void GetFilesArray()
{
// see if the file list is in the cache.
// use directory and filter as unique key
m_FilesArray = Page.Cache[FilesDirectory + FilesFilter] as String[];
if (m_FilesArray != null)
return;
// if no files filter set, use the default one.
if (String.IsNullOrEmpty(FilesFilter))
FilesFilter = DEF_FILES_FILT;
// if no files directory set, use the default one.
if (String.IsNullOrEmpty(FilesDirectory))
FilesDirectory = DEF_FILES_DIR;
// if a virtual path is detected, map to full path
String FullPath;
if (FilesDirectory.StartsWith("~"))
FullPath = Context.Server.MapPath(FilesDirectory);
else
FullPath = FilesDirectory;
// get the files
m_FilesArray = Directory.GetFiles(FullPath, FilesFilter, SearchOption.TopDirectoryOnly);
// put the list in the cache so we don't have to read the disk again
// use a dependency on the directory being read from for auto refreshing
Page.Cache.Insert(FilesDirectory + FilesFilter, // unique key
m_FilesArray, // list of files to store
new CacheDependency(FullPath)); // dependency on directory
}
}
}