您的位置:首页 > 编程语言 > ASP

一个"简单"的ASP.NET的服务器控件

2012-03-09 00:00 423 查看
控件代码: /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
==

)
{
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

}
}
}

注意:

控件名字:

去烦恼一个控件的名称是非常愚蠢做法,真的是这样吗?错误的命名一个控件(或里任何变量)就像结婚。由于你将要与它走很长一段时间,在将来改变它可能会非常痛苦。所以首先要用好你的命名。

我称这个控件为: HyperlinkFileList.

溢出问题:

如果控件的高度一旦设置,文件的列表超出了控件的高度。就会使文件“溢出”控件的边界。

为了解决这个问题,我将下面代码放到控件的构造函数之中:



if
((Height
!=

null
)
&&
(ScrollBars
==
ScrollBars.None))
ScrollBars
=
ScrollBars.Auto;

CSS 布局:

因为控件是基本是一个div(panel渲染成一个div),然后,设置"display"属性为"inline-block",允许多个控件并排在一起。


Style[
"
display
"
]
=

"
inline-block
"
;

CSS框模型:

我不喜欢文本卡在控件的左侧,所以我添加一些CSS来填充。我也在控件的周围使用一些css样式。使它不会与其它控件贴的很死。

//
add spacing outside the control

Style[
"
margin
"
]
=

"
0.5em
"
;

//
add space inside the control

Style[
"
padding-left
"
]
=

"
0.5em
"
;

状态管理:

在最初的测试的时候, 我发现每次控件都能运行,它都将重新读取的文件目录。文件输入输出代价是很昂贵的。我想去结合的服务器控件的“State”。但它使用的是View State 类型,两次发送文件列表到客户端是效率非常低的 。一次作为HTML列表,一次在ViewState 。

所以我想使用 Session State, Application State 和Cache。

我决定将文件列表放在一个缓存对象之中。使列表能在session之间共享。如果内存在溢出,缓存中的列表将会丢失。

我将文件的目录和文件filter连接在一起,作为缓存中索引键。这样允许多个控件同时使用和共享文件列表。

开始,我添加了使开发人员可以强制重新读取需要的文件的功能。但是,缓存对象可以使用依赖关系:任何从属目录更改会导致缓存过期。最后的代码是非常的简单:





代码


//
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

侧注:当然,这只是一个代码行,但做了几个小时的研究,以决定这是最好的方法去处理状态的管理的问题。有时需要很长的时间编写很少的代码。

Property Editor:

我将所有的自定义属性分组到标题 "Files List"下面。他们都在一个地方与Panel的属性分离开来。



下面是四个控件在一个页面上的标签





代码



<
EW:HyperlinkFileList
ID
="HyperlinkFileList5"
runat
="server"
BackColor
="#FFFF66"

Height
="200px"
>

<
EW:HyperlinkFileList
ID
="HyperlinkFileList6"
runat
="server"
FilesTitle
="The Same XML Files"

Height
="200px"
>

<
br
>

<
EW:HyperlinkFileList
ID
="HyperlinkFileList7"
runat
="server"
BackColor
="#66FFFF"

BorderColor
="#FF3300"
BorderWidth
="3px"
FilesDirectory
="C:/Peachw/EndSofi/BAK/"

FilesFilter
="*.Zip"
FilesTitle
="Whole lotta files!"
ForeColor
="#3333CC"
Width
="293px"

Height
="156px"
>

<
EW:HyperlinkFileList
ID
="HyperlinkFileList8"
runat
="server"
BackColor
="#66CCFF"

Height
="156px"
Width
="198px"
FilesDirectory
="~/Images/"
FilesFilter
="*.jpg"

FilesTitle
="Pretty Pictures"
>

下面是他们渲染之后的样子:



第二部分:自定义服务器控件编辑器

选择文件目录:

我认为粘贴文件的目录路径是业余开发人员使用的法子,所以我决定添加一个目录浏览器。

开发服务器控件编辑器所用时间比开发实际控件用的时间更长。

我认为控件的文件的目录,应在两个方面可设置:

Absolute Path: C:\PublicData\ImageFiles\
Virtual Path: ~\xmlFiles\
我试着设计两个内置浏览来设置FilesDirectory属性。


[EditorAttribute(typeof(System.Web.UI.Design.UrlEditor), typeof(UITypeEditor))]

我没有使用UrlEditor,因为它不允许浏览外部站点的主目录。


[EditorAttribute(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(UITypeEditor))]

我没有使用FolderNameEditor,因为它没有提供虚拟路径的选择。此外,它迫使用户选择一个我不想选择文件。

要创建自定义服务器控件编辑器,就要创建一个类自UITypeEditor继承和覆盖两个功能...其中一个启动一个DialogBox。

下面是代码:





代码


using
System;

using
System.Collections.Generic;

using
System.Drawing.Design;

using
System.ComponentModel;

using
System.Windows.Forms.Design;

using
System.Text;

namespace
EndWell
{

class
DualModeFolderEditor : UITypeEditor
{

//
---- GetEditStyle --------------------------------

//

//
tell designer what kind of UI we are (Dropdown or Modal DialogBox)

public

override
UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context)
{

return
UITypeEditorEditStyle.Modal;
}

//
---- EditValue ----------------------------------------

//

//
Called by IDE designer when user clicks the ... button

//
A DialogBox is launched

public

override

object
EditValue(ITypeDescriptorContext Context,
IServiceProvider Provider,

object
Value)
{
IWindowsFormsEditorService EditorService
=

null
;

if
(Provider
!=

null
)
{
EditorService
=
(IWindowsFormsEditorService)
Provider.GetService(
typeof
(IWindowsFormsEditorService));
}

if
(EditorService
!=

null
)
{

//
launch the dialog box

DuaModeFolderEditorForm Editor
=

new
DuaModeFolderEditorForm(Value.ToString(), Context);

EditorService.ShowDialog(Editor);

return
Editor.m_Value;
}

else

{

return
Value;
}
}
}
}

以下是编辑的DialogBox,如下所示:



我将不会显示DialogBox代码,因为这是它比较长,涉及广。由于缺乏文档,在开发过程中进行了反复的试错。但也有一些利益的东西...

目录分隔符(斜线):

反斜杠像 "\~"这样被使用的时候,GetProjectItemFromUrl 函数将不能正常使用。它正斜杠:"/~"能正常工作。因此,我确保所有的目录分隔符使用正斜杠。但是,从目录浏览器返回的目录使用的是反斜杠。所以我们要确保一致性...虽然它使代码有点凌乱,但真的没有我更喜欢其他选择。

服务器控件开发提示:

一旦控件放置到页面上,你能自动的更新bin文件路径下面的DLL,通过右击右击控件,选择“Refresh”。我不得不删除从bin目录下的控件,然后重新添加到网页上,以获取最新版本到该项目中。

调试编辑器:

调试控件是非常容易的。调试控件的编辑器却非常的难。因为它运行在Visual Studio中。我在不同的地方添加下面代码:

System.Diagnostics.Debugger.Break();

当运行到断点,你得到下面这个令人爽快的画面:



点击 "Debug the program",将创建一个新的 Visual Studio 实例。你就能够调试控件的编辑器了。原来的运行Visual Studio将锁定的(至少在我这里是这样的),你不得不去终止。由于欠缺自定义服务器控件编辑器的文档,这是非常宝贵的去寻找和了解什么被传递了,发生了什么。

可能的改进:

•标题字体可设置(大小,颜色,背景颜色...)

•将名称放在一个固定的div中,文件列表在另一可调整大小或有scrollable的div中。

•添加一个布尔字段来选择显示在链接的文件扩展名。

希望这篇文章能够帮助你。

欢迎讨论,谢谢!

参考原文:http://www.codeproject.com/Articles/46264/A-Simple-ASP-NET-Custom-Server-Control.aspx

原文链接:
http://www.cnblogs.com/zhuqil/archive/2009/12/28/HyperlinkFileList.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: