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


        控件代码: /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 {getset;}

            [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 { getset; }

            [Bindable(
    true)]
            [Category(
    "Files List")]
            [Description(
    "The filter for the files to show:  (*.*)")]
            
    public string FilesFilter { getset; }

            [Bindable(
    true)]
            [Category(
    "Files List")]
            [Description(
    "Text to show when there are no files")]
            
    public string NoFilesText { getset; }

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

     注意:

    控件名字:

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

        我称这个控件为: 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



    (全文完)


    以下为广告部分

    您部署的HTTPS网站安全吗?

    如果您想看下您的网站HTTPS部署的是否安全,花1分钟时间来 myssl.com 检测以下吧。让您的HTTPS网站变得更安全!

    SSL检测评估

    快速了解HTTPS网站安全情况。

    安全评级(A+、A、A-...)、行业合规检测、证书信息查看、证书链信息以及补完、服务器套件信息、证书兼容性检测等。

    SSL证书工具

    安装部署SSL证书变得更方便。

    SSL证书内容查看、SSL证书格式转换、CSR在线生成、SSL私钥加解密、CAA检测等。

    SSL漏洞检测

    让服务器远离SSL证书漏洞侵扰

    TLS ROBOT漏洞检测、心血漏洞检测、FREAK Attack漏洞检测、SSL Poodle漏洞检测、CCS注入漏洞检测。

  • 相关阅读:
    GRUB引导——menu.lst的写法
    条形码类型及常见条形码介绍
    Tmux:终端复用器
    find+*的问题
    find命令之exec
    Linux core 文件介绍
    C语言中返回字符串函数的四种实现方法
    C语言中的volatile
    Stars
    Game with Pearls
  • 原文地址:https://www.cnblogs.com/zhuqil/p/HyperlinkFileList.html
Copyright © 2020-2023  润新知