65.9K
CodeProject 正在变化。 阅读更多。
Home

OverLibWrapper -- overLIB DHTML 弹出 JavaScript 库的 C# 包装器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (11投票s)

2005年2月15日

CPOL

12分钟阅读

viewsIcon

117620

downloadIcon

1103

一篇关于使用 OverlibPageControl 和 OverlibPopupAnchor 来扩展 overLIB 弹出窗口 JavaScript 库操作的文章。

Shows the OverlibPageControl in action-PageControlDesign.jpg

Overlib objects in action

引言

市面上有很多弹出窗口控件;只需在 Google 上搜索,就会弹出成千上万个(无意冒犯)。大多数情况下,这些实现要么是纯 JavaScript,要么是简化的 Web 控件扩展。虽然这些实现都很好,但我正在寻找一个能够为我编写 JavaScript 并提供良好设计时支持的控件。最终,我需要的是一个更健壮、能够从页面上的中心位置处理多个预先确定和即时生成的弹出窗口的控件。此外,通过传统的工具提示提供大量信息是不可能的。在对 DHTML 实现进行一些研究后,我偶然发现了 overLIB DHTML 库,并对其在 JavaScript DHTML 环境中安排更多信息的方式印象深刻。这个库最大的限制是开发者必须手动编写所有命令。尽管市面上有一些网站可以为您编写适当的代码块,但我仍然需要一个拥有一切本地化功能的控件。

本文的目的是讨论我开发的包装器控件的功能,以满足显示格式化信息的需要,无论是来自数据库、数据集还是其他信息存储库。源代码提供了关于这些控件如何运行以及包装 overLIB 功能所需的详细注释。两个主要的控件是 OverlibPageControl,它位于任何特定的网页上,并且是用于在设计时将弹出工具提示绑定到所有 Web 控件(例如 LabelCheckBox 等)的中央存储库;另一个是 OverlibPopupAnchor,它是一个简化的超链接,用于为任何特定 Web 表单上的单词或单词组添加弹出功能。我将讨论如何在设计时和运行时创建和修改弹出窗口。本文的目标是展示此控件如何节省大量信息(如果按原样显示在网页上会使网页难以阅读或在切换到较低屏幕分辨率时导致严重问题)的设计和测试时间。

背景

我以前的一个项目需要能够通过工具提示显示有关特定关键字或 Web 控件的额外数据。因此,我的搜索引导我找到了 Code Project 以及 Tomas Petricek 和 Code Project 领域内其他开发者的弹出窗口功能。

然而,我们所需要的东西是这些项目似乎都无法做到的。我们真正需要的是一种方法,能够从页面上的中心位置处理大量弹出窗口,并动态定义新的弹出窗口。因此,创建了 OverLibWrapper C# namespace。有关 overLIB 命令的更完整定义,请访问 overLIB 文档站点。有很多命令,在 BosrupBoughner 页面 上对这些定义有更好的解释。

使用代码

首先,您需要将 OverLibWrapper.dll 文件添加到您的工具箱。我不会详细介绍安装过程,因为许多使用此控件的人应该都熟悉将可视化和非可视化控件添加到 IDE 工具箱的添加/删除方法。OverLibWrapper namespace 包含可视化控件:OverlibPageControlOverlibPopupAnchorColorComboBoxColorComboBox 暂且不论,它是一个控件,是由于 OverlibCommand 的设计时开发的一些 UI 要求而开发的。有关快速了解,请下载演示版来测试控件的操作。

最重要的两个控件是 OverlibPageControlOverlibPopupAnchorOverlibPopupAnchor 包装了基本的 <a> HTML 控件,并附带一些附加好处。对于那些不想要或没有大量工具提示的开发者,或者只是想像 Robert BoughnerErik Bosrup 的页面上所示的那样进行尝试,您会想要使用此项。它的概念就像在您的页面上添加一个新的超链接一样,但您也可以更改一些参数,使其显示您想要的任何内容。它包含了 OverlibPageControl 的所有其他设计器,所以如果您不想为了一个微不足道的锚点而放置一个页面控件,您不必这样做。

在文章开头的截图中,您可以看到 OverlibPageControl 在设计时是什么样的。下面的截图显示了 HTML 视图中的内容

<asp:Label id="Label1" style="Z-INDEX: 101; LEFT: 56px; 
   POSITION: absolute; TOP: 112px" runat="server">Label</asp:Label>
<asp:CheckBox id="CheckBox1" style="Z-INDEX: 102; LEFT: 32px; 
   POSITION: absolute; TOP: 160px" runat="server" Text="CheckBoxName">
</asp:CheckBox>
<olwc:OverlibPageControl id="OverlibPageControl1" runat="server" 
   PageDefaults="STICKY,FGCOLOR,'#D2691E',CAPCOLOR,'#D4D0C8',WRAP">
   <OverlibPopup ControlLink="Label1" 
   Text="This is the Tip Text shows when the control is clicked"
     Commands="STICKY,CENTER,CAPTION,'Clicked caption'" EventType="OnClick" />
   <OverlibPopup ControlLink="Label1" 
   Text="This shows when the user passes the mouse over" 
           Commands="STICKY,CENTER" 
     EventType="OnMouseOver" />
</olwc:OverlibPageControl>
OverlibPageControl 设计器

OverlibPageControl 具有两个属性,使其能够在 Web 表单中运行。它们是 PageDefaultsTips

Properties page

PageDefaults 属性连接到一个自定义 UITypeEditor,该编辑器允许开发人员构建将用于每一次调用底层 overlib 函数调用的适当命令。这些命令与通常需要手动输入到 overLIB JavaScript 库中的命令相同。

Command Editor

Tips 属性打开一个 CollectionEditor,允许设计器为当前页面上存在的特定控件创建、删除和修改弹出窗口。这个控件的一个优点是它允许开发人员将多个弹出窗口链接到特定控件,并具有不同的操作。目前只有两种事件类型:OnMouseOverOnClick。这些是 Web 开发中最常用的事件。在本文中,它们将是最常引用的事件。如下图所示,有两个弹出窗口控制着 Label1。当您将鼠标悬停在 Label1 上时,将出现一个弹出窗口;当您单击 Label1 时,将出现另一个弹出窗口。从技术上讲,每个控件的弹出窗口数量仅限于特定控件可以处理的事件数量。

Collection Editor

CollectionEditor 内部,Commands 属性打开 OverlibCommandEditor,用于添加将在此特定弹出窗口上运行的命令。Text 属性打开一个文本编辑器,它还允许直接输入 ToolTip 的文本内容。

代码

不仅可以在设计器中构建弹出窗口,还可以在运行时构建。这为需要动态弹出窗口提供了更大的灵活性。

// We want to add another popup -- so we build one from scratch
    OverLibWrapper.OverlibPopup pop = new OverLibWrapper.OverlibPopup();
    pop.Text = "This is a color test";
    //this is a little hex color utility that was built 
    //by http://www.tonesco.com
    string colorStr = 
          HexColor.HexColorUtil.ColorToHex(System.Drawing.Color.Beige);
    string colorStr2 = 
          HexColor.HexColorUtil.ColorToHex(System.Drawing.Color.IndianRed);
    //add the commands that we want for this call to the overlib function
    pop.AddCommand(OverLibWrapper.OverlibCommand.BackgroundColor,colorStr2);
    pop.AddCommand(OverLibWrapper.OverlibCommand.ForegroundColor,colorStr);
    pop.AddCommand(OverLibWrapper.OverlibCommand.Caption,"This is a color Test");
    //add out control to link to -- this is the control.ID value
    //Note: if the ControlLink property is not set. 
    ///During the pre render of the control an exception will be issued 
    pop.ControlLink = "Label1";
    //add out event type: the default is onmouseover
    //Note: that if two or more controls have the same control 
    //link and have the same
    //ControlEventType, the first event will be captured, but the second
    // will be discarded even though it will remain within the code or 
    //within the design collection
    pop.EventType = 
        OverLibWrapper.ControlEventType.OnClick;
    OverlibPageControl1.Tips.Add(pop);

运行时要使用的另一个类是 OverlibPopupTextBuilder。此类提供与文本编辑器相同的功能,但允许开发人员在运行时指定信息的确切布局。如果您想创建一个表来组织工具提示中显示的数据,那么它很有用。

OverLibWrapper.OverlibPopupAnchor anchor = 
            new OverLibWrapper.OverlibPopupAnchor();
OverLibWrapper.Design.OverlibPopupTextBuilder optb = 
            new OverLibWrapper.Design.OverlibPopupTextBuilder();
    optb.AddTable();
    optb.AddTableAttribute("border","1");
    int rowIndex = optb.AddTableRow();
    optb.AddRowCell(rowIndex,"this is some information in a cell");
    optb.AddRowCell(rowIndex,"This is an cell with information");
    anchor.Title = "Inner popup";
    anchor.PopupText = "This is text inside the popup";
    anchor.IsInnerPopupAnchor = true;
    anchor.OverlibCommands = 
            "STICKY,CAPTION,'This is an inner popup caption',WRAP";
    rowIndex = optb.AddTableRow();
    optb.AddRowCell(rowIndex,"This is another cell");
    optb.AddRowCell(rowIndex,anchor.HyperlinkString());
    string popupInformation = optb.PopupText();
//popupInformation will read:
//<table border=\'1\'>
//<tr><td>this is some information in a cell </td> 
//<td>This is an cell with
//information</td></tr><tr><td>
//This is another cell</td> 
//<td><a href="javascript:void(0);" 
//onmouseover=
//  "overlib2('This is text inside the popup',STICKY,CAPTION,'This is an
// inner popup caption',WRAP)" onmouseout="nd()2;">Inner popup</a>
//</td></tr> </table>
//this is then added to another anchor or popup and the 
//entire object is written to the rendered html

要进一步了解 OverlibPopupTextBuilder 的工作原理,请下载源代码并查看其内部细节。

OverlibPopupAnchor

OverlibPopupAnchor 包含与 OverlibPageControl 相同的功能,只是它在开发者面前的表示形式更紧凑。此控件显示一个文本超链接,该链接已设置为显示弹出窗口。使用锚点的一个限制是,它在设计时不能添加多个事件类型。

OverlibCommand 和 OverlibCommandCollection

OverlibCommandOverlibCommandCollection 是构建对 overlib 函数调用的命令结构的两个类。OverlibCommand 包含 overLIB 库的核心和官方插件的 100 多个 overLIB 命令的整个列表。OverlibCommandCollection 是一个强类型集合,允许进行正常的集合方法(即 AddRemoveIndexOf 等)。OverlibCommandCollection 中一个更有趣的方法是 ToString() 方法。调用 ToString() 时,返回值是一个逗号分隔的字符串,包含所有插入到集合中的命令。这正是 overLIB JavaScript 库期望的命令格式。OverlibCommandEditorDialog 包含一个 OverlibCommandCollection,它将解析传入的命令字符串,并将它们转换为有效的 OverlibCommand 进行编辑。最好不要直接创建 OverlibCommand,而是使用 OverlibPopup 来为弹出窗口添加带有值的特定命令。

OverlibPopup

这个类是 OverLibWrapper namespace 中其他重要项之一。OverlibPopup 类由 OverlibPageControl 解析和生成。OverlibPopupCollection 在设计和渲染期间包含弹出窗口。在设计和运行时添加新的 OverlibPopup 时,必须将 ControlLink 属性设置为当前在 Web 表单设计视图中的 WebControl。通过设置 ControlLink 属性,您指示该特定弹出窗口将显示在该特定 WebControl 上。如果未设置该属性,则在 Web 页面执行时将抛出异常。

上述命令类型的每个示例是

OverlibCommand.Sticky; //flag type command
    OverlibCommand.Caption; //quoted text command
//a text command; this is required for those commands that
//are looking for objects on the page using JavaScript    
OverlibCommand.Frame;  
// a color command in the form of '#FFCCAA'
    OverlibCommand.ForegroundColor;

以下是创建弹出窗口并添加各种命令及其关联值的示例

OverLIBWrapper.OverlibPopup pop = new OverLIBWrapper.OverlibPopup();
    pop.Text ="A new popup";
     //this is a plugin command (unofficial)
    pop.AddCommand(OverLIBWrapper.OverlibCommand.Draggable);
    //this is a core command
    pop.AddCommand(OverLIBWrapper.OverlibCommand.Center); 
    //core command with a quoted text value the single quotes are 
    //optional because the logic behind the OverlibCommand is 
    //that it will automatically determine whether or 
    //the text has to be quoted.  Another item is that it will automatically 
    //escape the single quotes that internal to the string value.
    pop.AddCommand(OverLIBWrapper.OverlibCommand.Caption,"This is a test");  

    //if the controlLink is not set there will be exceptions thrown 
    //during pre-render of this control
    pop.ControlLink = "Label1";  //this is equivalent to Lable1.ID
    //this adds the command to the current page control's popup collection
    OverlibPageControl1.Tips.Add(pop);

关注点

您可能会觉得有趣的一点是,试图查找页面上特定控件的问题。Tomas(前面提到的)通过他查找通过当前上下文实例查找控件的 UITypeEditor,帮助我走了一半的路。但是,当您创建 CollectionEditor 的新实例并添加新对象时,在查找提供包含页面上所有控件的引用的当前服务时存在一个问题。以下代码片段可以进行一些小的修改,用于查找您希望的页面上的任何内容

public override object EditValue(ITypeDescriptorContext context,
                        IServiceProvider provider,object value){
  if (context!=null && context.Instance!=null && provider!=null) {
      edSvc=(System.Windows.Forms.Design.IWindowsFormsEditorService)
   provider.GetService(typeof(
       System.Windows.Forms.Design.IWindowsFormsEditorService));
        if (edSvc!=null) {                    
            lb=new System.Windows.Forms.ListBox();
            lb.BorderStyle=System.Windows.Forms.BorderStyle.None;
            lb.SelectedIndexChanged+=
                new EventHandler(lb_SelectedIndexChanged);
            ArrayList items = new ArrayList();
            System.Web.UI.Control parentControl = null;
            // gets all controls from the form
            //this grabs the service for all the references that the provider has
            //this is key if you are calling this editor 
            //from inside another editor
            //,i.e., a CollectionEditor
            IReferenceService service = 
              (IReferenceService)provider.GetService(typeof(IReferenceService));
            object[] references = 
                service.GetReferences(typeof(System.Web.UI.Control));
            //grabs the parent control based on the references.
            foreach(Control control in references){
                if(control.GetType() == typeof(System.Web.UI.UserControl) ||
                   control.GetType() == typeof(System.Web.UI.Page)){
                    parentControl = control;
                    break;
                }
            }
            if (parentControl == null) {
                return "No controls found";
            }
            //recursively grabs all the controls 
            //within each control's Controls container
            //otherwise the only level we will see 
            //is the Page level controls, i.e. those
            //controls that put on the actual page.
            //All other controls that are placed 
            //inside panels,
            //or other containers will not be shown 
            //and thus if you wish to get a 
            //control that is two or three levels deep or if there is a 
            //master page context
            //you will be out of luck.
            //This is especially the case when wanting to create a UserControl.
            //and adds them to the item list
            foreach(Control ctrl in parentControl.Controls){
                GetControls(ctrl, items);
            }
            items.Sort();
            lb.Items.AddRange(items.ToArray());
            edSvc.DropDownControl(lb);
            if (lb.SelectedIndex==-1) return value;
            return lb.SelectedItem;
        }
    }

    return value; 
}

另一个要注意的是,试图在不重写的情况下合并已经有效的代码可能是一个大问题。由于在使用 overLIB JavaScript 文件时出现了这种情况,将它们编译为资源然后将资源文件插入到调用 Web 项目中是一个不错的想法,但也充满了挑战。OverLibWrapper namespace 中的另外两个类有助于操作 web.config 文件并将适当的文件夹和文件插入到当前的 Web 项目中。这些类足够通用,可以插入和操作您希望插入到特定 Web 项目中的几乎任何嵌入式资源。OverLibWrapper.Design.ProjectController 处理从嵌入式资源进行文件插入。OverlibWrapper.Design.AppConfig 处理和操作配置文件。对于那些坚持要注释的人来说,您甚至可以根据您刚刚插入的键来插入一个注释到特定的配置文件中。

OverlibPageControlOverlibPopupAnchorOnPreRender() 期间,将生成必需的 overLIB JavaScript 元素并写入页面。两个控件都足够智能,如果 overLIB 脚本位置有问题,或者预期的显示文本有问题,则不会将任何内容注入页面,这样在尝试对 Web 表单源的其他元素进行故障排除时就不会有杂乱。

技巧、窍门和故障排除

OverlibPageControl

  • 将多个页面控件拖放到 Web 表单上将导致异常。别担心。这是设计使然。由于 OverlibPageControl 持有一个 OverlibPopup 的集合,因此每页实际上只需要一个控件。
  • overLIB 库的 JavaScript 文件嵌入到 .dll 中。这意味着用户永远不必担心 JavaScript 文件的最终位置。它会自动加载到当前的 Web 项目中。

  • 拖放控件的操作将自动将适当的文件加载到 Web 项目中,并将适当的 JavaScript 文件位置设置为根级别插入到 web.config 中。
      <appSettings>
        <add key="overLIBLocation" value="/OverlibDemo/overLIBScripts" />
      </appSettings>
  • 如果您想摆脱页面控件,请不要先删除 overLIBScripts 文件夹或 appSettings 键。如果发生这种情况,然后删除控件,在其刷新和初始化周期中,将检查键的位置和实际物理文件夹。如果这两个项都不存在,它将恢复键和所有文件。别担心,这是设计使然。过去,当其他组件开发人员开发高速低损耗控件时,下层开发人员需要花费大量精力来确保外部文件文件夹在开发和部署过程中存在。此控件不是这样。除了核爆炸,它拥有在您的页面上生存所需的一切。这里的关键是先删除页面上的控件,然后删除 web.config 键,最后删除包含 overLIB JavaScript 文件的目录。
  • 此控件的一个优点是,当您认为 overLIB JavaScript 有更新时,只需将文件复制到本地文件文件夹,并且在没有意外功能或关键字添加的情况下,代码将按原样工作。
  • 包装 overLIB 库的目的是保持 JavaScript 不变,只让控件完成所有工作。我认为这已经完成了。
  • OverlibPageControl 的任务是方便设计师使用。它会告诉您它在哪里看到相应的文件、<appSettings></appSettings> 键,以及控件包含的总弹出窗口数量。
  • 此控件具有丰富的 UI,允许开发人员在设计时完成大部分工作。

OverlibPopupAnchor

  • 将控件拖放到 Web 表单上将执行与 OverlibPageControl 相同的操作。与 OverlibPageControl 一样,将此控件添加到您的页面将在您的 Web 项目中插入 <appSettings> 引用并放置 overLIBScripts 文件夹。
  • 注意:删除 overLIBScripts 目录、将其排除在项目之外或从 <appSettings> 中删除 overLIBLocation 的操作不会被 OverlibPopupAnchor 自动重新添加。这是因为 OverlibPopupAnchor 在创建控件期间仅查找 appSettings 键和文件文件夹位置。
  • OverlibPopupAnchor 实际上仅设计用于页面上的小型实现,这些实现主要包含大量文本。对于更健壮的实现,其中包含控件,OverlibPageControl 更适合开发。

谢谢

我最深切的感谢献给 Code Project 的作者们,他们提供了节省时间的宝贵步骤,并帮助我解决了实现过程中一些更令人困扰的部分。其他感谢和致敬献给 Erik Bosrup 和他的助手们,他们提出了一个广受欢迎且使用广泛的 JavaScript,并允许我包装他们的代码。还要感谢 Rob Battle 在代码的一些看似困难的部分,他几次严厉地指出我正确的方向。

法律

所有 overLIB JavaScript 代码均为 Erik Bosrup 和 Robert Boughner 的版权所有。

所有其他代码均归各自开发者所有。

在开发此代码期间,从未进行过故意侵权。

历史

2005-02-16 -- 本文的初始撰写。

© . All rights reserved.