程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> 一個“簡單”的ASP.NET的服務器控件

一個“簡單”的ASP.NET的服務器控件

編輯:.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 {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
           
        }
    }
}

注意:

控件名字:

去煩惱一個控件的名稱是非常愚蠢做法,真的是這樣嗎?錯誤的命名一個控件(或裡任何變量)就像結婚。由於你將要與它走很長一段時間,在將來改變它可能會非常痛苦。所以首先要用好你的命名。

我稱這個控件為: 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://zhuqil.cnblogs.com

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved