彩票走势图

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

原创|其它|编辑:郝浩|2009-12-28 10:14:29.000|阅读 637 次

概述:在我主持过的一个Asp.Net论坛上,一个用户询问如何在一个页面上去通过hyperlink列出一个路径下的很多文件名,能使用户能点击它们。我认为这是可能是经常执行的动作,所以决定建立一个服务器控件来封装它。

# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>

    控件代码: 

    在我主持过的一个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
    {
 &nbsp;   &nbsp;  [Bindable(
true)]
    ;    [Category(
"Files List")]
   &nbsp;     [Description(
"The Title of the list of files")]
        
public string FilesTitle {getset;}

 &nbsp;      [Bindable(
true)]
&nbsp;       [Category(
"Files List")]     
        [Description(
"The directory ;of the files to list:  (~/Files/)")]
&nbsp;       
// these two built in editors were lacking:
        
//[EditorAttribute(typeof(System.Web.UI.Design.UrlEditor), typeof(UITypeEditor))]
   &nbsp;    
//[EditorAttribute(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(UITypeEditor))]
&nbsp;       [EditorAttribute(typeof(EndWell.DualModeFolderEditor), typeof(UITypeEditor))]  
  &nbsp;     
public string FilesDirectory { getset; }

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

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

       &nbsp;
// ---- Private vars --------------------------

    &nbsp;   
private String[] m_FilesArray;  // cached for performance

       &nbsp;
//&nbsp;---- 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 -------------------------- 

&nbsp;       
public HyperlinkFileList()
        {
  &nbsp;         
// set defaults&nbsp;for our properties
   &nbsp;        FilesDirectory = DEF_FILES_DIR;
            FilesFilter 
= DEF_FILES_FILT;
            FilesTitle 
= DEF_FILES_TITLE;
  &nbsp;         NoFilesText 
= DEF_NOFILES_TEXT;

 &nbsp;          
// Set defaults for panel properties
      &nbsp;     
// I don't like the default width to be full screen
            
// And a border ;looks better
        &nbsp;   Width = new Unit("300px");
   ;  &nbsp;      BorderStyle 
= BorderStyle.Solid;
            BorderWidth 
= 1;
     &nbsp;    ;  BorderColor 
= Color.Black;

           ; 
// If height is set, force scroll bars to keep list
        &nbsp;   
// from spilling over the panel/div boundaries.
&nbsp;           if ((Height != null&& (ScrollBars == ScrollBars.None))
  &nbsp;             ScrollBars 
= ScrollBars.Auto;
           
      &nbsp;     
// Allow multiple controls to be placed horizontally
          &nbsp; 
// (normally each div get's its own line)           
   &nbsp;        Style["display"= "inline-block";

           &nbsp;
// add spacing outside the control
       ;     Style["margin"= "0.5em";

     &nbsp;      
// add&nbsp;space inside the control           
         ;   Style["padding-left"= "0.5em";
             Style[
"padding-right"= "0.5em";
     &nbsp;      Style[
"padding-bottom"= "0.5em";
        &nbsp; &nbsp; 
// top space usually comes&nbsp;from the title...
            if (String.IsNullOrEmpty(FilesTitle) == true)
    &nbsp;           Style[
"padding-top"= "0.5em";
        }

&nbsp;       
// ---- RenderContents ----------------------------
 &nbsp;      
//
        
// Spit out the HTML
 
  &nbsp;     
protected override void RenderContents(HtmlTextWriter Output)
        {
  &nbsp;         
// output the ;title if one was set
  &nbsp;         if (String.IsNullOrEmpty(FilesTitle) == false)
            {
          &nbsp;     Output.Write(
"<h3>  ");  // cosmetic spacing
                Output.Write(FilesTitle);
         &nbsp;      Output.Write(
"</h3>");
            }

            GetFilesArray();
  
  &nbsp;         
if (m_FilesArray.Length == 0)
            {
                Output.Write(HttpUtility.HtmlEncode(NoFilesText));              
            }
      &nbsp;  ;   
else
            {
       &nbsp;        
foreach (String OneFile in m_FilesArray)
                {
   &nbsp;              &nbsp; HyperLink Link 
= new HyperLink();
  &nbsp;                 Link.NavigateUrl 
= Path.Combine(FilesDirectory, Path.GetFileName(OneFile));
                    Link.Text 
= Path.GetFileNameWithoutExtension(OneFile);                  
                    Link.RenderControl(Output);
                    Output.WriteBreak();
                }
            }
        }

   &nbsp;    
// ---- GetFilesArray -------------------------
 &nbsp;      
//
   &nbsp;    
// Fill the m_FilesArray with a list of files
        
//&nbsp;either from disk&nbsp;or the cache

        
private void GetFilesArray()
        {
 &nbsp;          
// see if the file list is in the cache.
   &nbsp;        
//&nbsp;use directory&nbsp;and filter as unique key
&nbsp;          &nbsp;m_FilesArray = Page.Cache[FilesDirectory + FilesFilter] as String[];

            
if (m_FilesArray != null)
    &nbsp;            
return;

       &nbsp;    
// if no files filter set, use&nbsp;the default&nbsp;one.
            ;if (String.IsNullOrEmpty(FilesFilter))
   &nbsp;            FilesFilter 
= DEF_FILES_FILT;

            ;
// if no files directory set, use the default one.
      ;      if (String.IsNullOrEmpty(FilesDirectory))
&nbsp;&nbsp;              FilesDirectory 
= DEF_FILES_DIR;

  &nbsp;         
// if a virtual path is detected, map to full path
            String FullPath;
&nbsp;&nbsp;          
if (FilesDirectory.StartsWith("~"))
             &nbsp;  FullPath 
= Context.Server.MapPath(FilesDirectory);
            
else
          ;      FullPath 
= FilesDirectory;

         &nbsp;  ;
// 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&nbsp;dependency on the directory being read from for auto refreshing
            Page.Cache.Insert(FilesDirectory + FilesFilter,   // unique key
    &nbsp;                         m_FilesArray,                   // list of files to store
            &nbsp;            &nbsp;    new CacheDependency(FullPath)); // dependency on directory
           
        }
    }
}

 注意:

控件名字:

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

    我称这个控件为: HyperlinkFileList.

溢出问题:

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

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

  if ((Height != null&& (ScrollBars == ScrollBars.None))
   &nbsp;  ScrollBars 
= ScrollBars.Auto;

CSS 布局:

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

    Style["display"= "inline-block";

CSS框模型:

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

     // add spacing&nbsp;outside the control
    Style["margin"= "0.5em";
    
// add space inside the control 
    Style["padding-left"= "0.5em"

状态管理:

    在最初的测试的时候, 我发现每次控件都能运行,它都将重新读取的文件目录。文件输入输出代价是很昂贵的。我想去结合的服务器控件的“State&rdquo;。但它使用的是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,   &nbsp;               // list of files to store
                ;  new CacheDependency(FullPath)); // dependency on directory

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

Property Editor:

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

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

代码
    <EW:HyperlinkFileList ID="HyperlinkFileList5"  runat="server" BackColor="#FFFF66"
 &nbsp;      Height
="200px">
    
    
<EW:HyperlinkFileList ID="HyperlinkFileList6"  runat="server" FilesTitle="The Same XML Files"
&nbsp;       Height
="200px">
    
<br >
    
<EW:HyperlinkFileList ID="HyperlinkFileList7"  runat="server" BackColor="#66FFFF"
    &nbsp;   BorderColor
="#FF3300" BorderWidth="3px" FilesDirectory="C:/Peachw/EndSofi/BAK/"
   &nbsp;     FilesFilter
="*.Zip" FilesTitle="Whole lotta files!" ForeColor="#3333CC" Width="293px"
&nbsp;    &nbsp;  Height
="156px">
    
    
<EW:HyperlinkFileList ID="HyperlinkFileList8"  runat="server" BackColor="#66CCFF"
       &nbsp;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))]

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

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

 &nbsp;  我没有使用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
    {
     &nbsp;  
// ---- GetEditStyle --------------------------------
        
//
   &nbsp;    
// tell designer what kind of UI we are (Dropdown or Modal DialogBox)

  &nbsp;     
public override UITypeEditorEditStyle 
           GetEditStyle(ITypeDescriptorContext context)
        {
 &nbsp;      &nbsp;    
return UITypeEditorEditStyle.Modal;
        }

&nbsp;       
// ---- EditValue ----------------------------------------
   ;     
//
        ;
// Called by IDE designer when user clicks the ... button
         
// A DialogBox is launched

        
public override object EditValue(ITypeDescriptorContext Context,
                                         IServiceProvider Provider,
    &nbsp;                         &nbsp;          
object Value)
        {           
            IWindowsFormsEditorService EditorService 
= null;
         &nbsp;  
if (Provider != null)
            {
    &nbsp;   &nbsp;       EditorService 
= (IWindowsFormsEditorService)
                   Provider.GetService(
typeof(IWindowsFormsEditorService));
            }
   ;     &nbsp;   
if (EditorService != null)
            {
    ;            
// launch the dialog box
  &nbsp;             DuaModeFolderEditorForm Editor = 
             &nbsp;     
new DuaModeFolderEditorForm(Value.ToString(), Context);
                
                EditorService.ShowDialog(Editor);
              &nbsp; 
return Editor.m_Value;
            }
           ; 
else
            {
                 
return Value;
            }
        }
    }
}

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

 

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

目录分隔符(斜线):

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

服务器控件开发提示:

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

调试编辑器:

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

System.Diagnostics.Debugger.Break();

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

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

可能的改进:

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

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

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

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

欢迎讨论,谢谢!

参考原文:


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn

文章转载自:博客园

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP