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

VML Web 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.61/5 (12投票s)

2005年12月15日

5分钟阅读

viewsIcon

73292

downloadIcon

1909

用于 ASP.NET Web Forms 的 VML 绘图控件。

VML Drawing Controls

引言

我作为地理学家/GIS 专家,背景经常涉及开发用于各种主题的交互式地图网站。我们的一些要求包括能够通过客户端创建矢量图形,用于缩放框、测量工具、形状(点、线、多边形)以及桌面地理信息系统中常见的其他功能。我们的许多客户都有严格的准则,包括开发不需要插件和/或下载的应用程序。我发现 VML 是满足此要求的完美解决方案,并开发了 VML 绘图控件,以实现拖放工具,用于创建客户端的线、折线、多边形、矩形和圆角矩形图形,以自动化此任务。在本文中,我们将以 VMLControl 基类以及 VMLPolygon Web 控件为例,介绍这些 VML 绘图控件。

* 请注意,虽然不需要下载或插件,但需要 Internet Explorer 浏览器。

背景

有关 VML 的更多背景信息,请参阅 Simon Stewart 的文章 Introduction to VML 和 Microsoft 的 VML Reference。我还要特别感谢 Sreenivas Vemulapalli,他的关于 Using the Property Grid 的文章帮助这些控件成型。

VML 命名空间参考:VML 的使用需要在 HTML 文档中包含以下标签用法

<HTML xmlns:v="urn:schemas-microsoft-com:vml">
    <HEAD>
        <style>v\:* { BEHAVIOR: url(#default#VML) }
        </style>
    </HEAD>
    ....

使用代码

这些控件应像其他任何 .NET Web 控件一样使用,可以通过 Visual Studio IDE 的“工具”菜单下的“添加/删除工具箱项...”添加到您的项目中。

VMLDrawingControls 由五个派生自 VMLControl 基类的形状控件类组成。

  1. VMLLine
  2. VMLPolyline
  3. VMLPolygon
  4. VMLRectangle
  5. VMLRoundRectangle(继承自 VMLRectangle

VML Designer Controls

VMLControl

MustInherit VMLControl 基类为控件提供了框架,并包含以下属性:

  • ID
  • ButtonType [Button | Image]
  • ButtonText
  • ImageSrc, ImageSrcMouseDown, ImageSourceMouseOver
  • CursorStyle, CustomCursor, CustomCursorEnabled
  • BoundingDrawCanvas

BoundingDrawCanvas 是一个**必需**的属性,它保存将用作矢量图形边界绘图画布的图像控件的 ID。我使用了 System.Web.UI.WebControls.WebControlAddAttributesToRender 覆盖方法来提醒用户此要求。一旦在控件的属性中满足了此要求,警告将消失。

Protected Overrides Sub AddAttributesToRender(ByVal _
                    output As System.Web.UI.HtmlTextWriter)

    'Warn User Of Control Requirements
    If Me.BoundingDrawCanvas = "" Or _
          Me.BoundingDrawCanvas = "(none)" Then
        output.Write("<BR>")
        output.Write("<Font color='Red'>" & _ 
               "<li>BoundingDrawCanvas Property Is Required</Font>")
    Else
        If Page.FindControl(Me.BoundingDrawCanvas) Is Nothing Then
            output.Write("<BR>")
            output.Write("<Font color='Red'>" & _ 
                   "<li>BoundingDrawCanvas Control ID" & _ 
                   " Cannot Be Found</Font>")
        End If
    End If

    MyBase.AddAttributesToRender(output)

End Sub

* 源代码:VMLControl.vb

BoundingDrawCanvas Warning

以下是 VMLControl 基类中的 BoundingDrawCanvas 属性,它在所有派生的 VML 形状类中使用。

<Category("Appearance"), _
Description("The Control ID Of The Image Control" & _ 
            " Which Defines The Bounding Draw Canvas"), _
TypeConverter(GetType(BoundingControlsConverter)), _
PersistenceMode(PersistenceMode.Attribute)> _
Public Property BoundingDrawCanvas() As String
    Get
        _BoundingDrawCanvas = _
           CType(ViewState("VMLBoundingDrawCanvas"), String)
        If _BoundingDrawCanvas Is Nothing Then
            Return "(none)"
        Else
            Return _BoundingDrawCanvas
        End If
    End Get
    Set(ByVal Value As String)
        ViewState("VMLBoundingDrawCanvas") = Value
        _BoundingDrawCanvas = Value
    End Set
End Property

*源代码:VMLControl.vb

我为此属性创建了一个 BoundingControlsConverter,用于列出 Web Forms 上当前的图像控件。根据我工作的性质,我使用图像 Web 控件(其中包含我的地图图像)作为绘图画布。从基类 System.ComponentModel.TypeConverter 中覆盖的 GetStandardValues 方法允许我检索属性下拉列表中的图像控件的 ID。使用传递的 Context.Container.Components,我可以循环页面组件以识别要用作画布的图像控件。未来的修改将是适应表格单元格、div 和其他容器标签。

Public Overloads Overrides Function GetStandardValues(ByVal context As _
       System.ComponentModel.ITypeDescriptorContext) As _
       System.ComponentModel.TypeConverter.StandardValuesCollection

    Dim supportedCtrls As New ArrayList
    Dim Component As IComponent

    'Supported Control Types
    Dim wcImg As WebControls.Image

    For Each Component In context.Container.Components
        If TypeOf Component Is WebControls.Image Then
            wcImg = CType(Component, WebControls.Image)
            supportedCtrls.Add(wcImg.ID)
        End If
    Next

    'Sort the list
    supportedCtrls.Sort()

    'return values to be listed in the property browser's dropdown list
    Dim svc As New _
      StandardValuesCollection(CType(supportedCtrls.ToArray(GetType(String)), _
      String()))

    Return svc

End Function

*源代码:BoundingControlsConverter.vb

VML 样式

我创建了三个 VML 样式类,它们用作绘图控件类的属性,用于定义颜色、宽度、样式、填充图案等。

  • VMLLineStyle
  • VMLFillStyle
  • LineEndPointProperties(提供 FromTo 点样式)

VML Style Properties

例如,VMLPolygon 控件具有类型为 VMLFillStyleFillStyle 属性,该属性定义了要渲染的多边形的颜色、图案等。ExpandableObjectConverter 用于使属性在属性网格中可展开,类似于其他控件的 Font 属性。

<Category("VML Symbolization"), _
Description("Defines The VML Fill Style Properties To Be Used"), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
TypeConverter(GetType(ExpandableObjectConverter))> _
Public Property FillStyle() As VMLFillStyle
    Get
        Return _FillStyle
    End Get
    Set(ByVal Value As VMLFillStyle)
        _FillStyle = Value
    End Set
End Property

*源代码:VMLPolygon.vb

这是 VMLFillStyle

Imports System.ComponentModel
Imports System.Web.UI
Imports System.Drawing.Design
<Serializable(), PersistenceMode(PersistenceMode.Attribute)> _
Public Class VMLFillStyle
    Enum FillTypeEnum
        Solid
        Gradient
        GradientRadial
        Tile
        Pattern
        Frame
    End Enum
    Private _FillType As FillTypeEnum = FillTypeEnum.Solid
    Private _FillColor As System.Drawing.Color = Nothing
    Private _FillColor2 As System.Drawing.Color = Nothing
    Private _FillOpacity As Int16 = 100
    Private _FillOpacity2 As Int16 = 100
    Sub New()
        'Class Constructor
    End Sub
    <Category("Rectangle Fill Appearance"), _
    Description("Defines The Type Of Fill To Apply"), _
    NotifyParentProperty(True)> _
    Public Property FillType() As FillTypeEnum
        Get
            Return _FillType
        End Get
        Set(ByVal Value As FillTypeEnum)
            _FillType = Value
        End Set
    End Property
    <Category("Rectangle Fill Appearance"), _
    Description("Defines The Primary Fill Color"), _
    NotifyParentProperty(True)> _
    Property FillColor() As System.Drawing.Color
        Get
            Return _FillColor
        End Get
        Set(ByVal Value As System.Drawing.Color)
            _FillColor = Value
        End Set
    End Property
    <Category("Rectangle Fill Appearance"), _
    Description("Defines The Secondary Fill Color"), _
    NotifyParentProperty(True)> _
    Property FillColor2() As System.Drawing.Color
        Get
            Return _FillColor2
        End Get
        Set(ByVal Value As System.Drawing.Color)
            _FillColor2 = Value
        End Set
    End Property
    <Category("Rectangle Fill Appearance"), _
    Description("Defines The Opacity Of The Primary Color"), _
    NotifyParentProperty(True)> _
    Property FillOpacity() As Int16
        Get
            Return _FillOpacity
        End Get
        Set(ByVal Value As Int16)
            If ((Value < 0 Or Value > 100)) Then
                Throw New ArgumentException("The Opacity" & _ 
                              " Must Be Beteen 0 and 100")
            End If
            _FillOpacity = Value
        End Set
    End Property
    <Category("Rectangle Fill Appearance"), _
    Description("Defines The Opacity Of The Secondary Color"), _
    NotifyParentProperty(True)> _
    Property FillOpacity2() As Int16
        Get
            Return _FillOpacity2
        End Get
        Set(ByVal Value As Int16)
            If ((Value < 0 Or Value > 100)) Then
                Throw New ArgumentException("The Opacity2" & _ 
                               " Must Be Beteen 0 and 100")
            End If
            _FillOpacity2 = Value
        End Set
    End Property
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public ReadOnly Property FillTag() As String
        Get
            Dim retFillTag As New System.Text.StringBuilder
            retFillTag.Append("<v:fill ")
            retFillTag.Append(" Type=" & Quote(Me.FillType.ToString))
            retFillTag.Append(" Color=" & _
               Quote(HelperFunctions.BuildRGBString(Me.FillColor)))
            retFillTag.Append(" Color2=" & _
               Quote(HelperFunctions.BuildRGBString(Me.FillColor2)))
            retFillTag.Append(" Opacity=" & _
               Quote(FillOpacity.ToString & "%"))
            retFillTag.Append(" Opacity2=" & _
               Quote(Me.FillOpacity2.ToString & "%"))
            retFillTag.Append(" />")

            Return retFillTag.ToString
        End Get
    End Property
    <Description("Define The VML Fill Properties")> _
    Overrides Function ToString() As String
        Return "(VML Fill Style)"
    End Function
End Class

*源代码:VMLFillStyle.vb

创建了一个名为 HelperFunctions 的模块来扩展这些控件。创建了 ReadJavascript 过程来读取 JavaScript 源文件,以将客户端上的 VML 图形作为程序集的资源进行操作,并将内容写入输出流。使用这种方法,我可以将所有 JavaScript 保存在一个 .js 文件中,并根据需要进行修改,然后重新构建程序集。VMLControl 基类中还有一个属性,我添加了该属性来引用外部 .js 文件,但从未对其进行彻底实现。

Public Function ReadJavasript() As String

    Dim jsStream As IO.Stream
    jsStream = System.Reflection.Assembly.GetExecutingAssembly()._
               GetManifestResourceStream("VMLDrawingControls" & _ 
                                         ".VMLClientJavascript.js")

    Dim jsStreamReader As New StreamReader(jsStream)

    Return jsStreamReader.ReadToEnd

End Function

*源代码:HelperFunctions.vb

Protected Overrides Sub Render(ByVal output As _
                    System.Web.UI.HtmlTextWriter)

    'Stream the Javascript
    Page.RegisterStartupScript("VMLScript", _
         "<script language=""javascript""> " & _
         HelperFunctions.ReadJavasript & " </script>")

    MyBase.Render(output)
End Sub

*源代码:VMLPolygon.vb

VMLPolygon

VMLPolygonVMLDrawingControls 的一个示例。此控件继承自 VMLControl 基类,并具有定义渲染多边形外观的方法和属性。如上所示,该控件使用 VMLFillStyle 定义填充属性,并全部使用 VMLLineStyle 定义多边形的轮廓属性。

Imports System.web.UI.HtmlTextWriter
Imports System.Web.UI
Imports System.Web.UI.Design
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Drawing
Imports System.Design
Imports System.Windows.Forms
<Designer(GetType(VMLControlDesigner)), _
ToolboxData("<{0}:VMLPolygon runat=server></{0}:VMLPolygon>")> _
Public Class VMLPolygon
    Inherits VMLControl
    Implements INamingContainer
    Sub New()
        MyBase.ButtonText = "Draw Polygon"
    End Sub
    Private _LineStyle As New VMLLineStyle
    Private _FillStyleEnabled As Boolean = False
    Private _FillStyle As New VMLFillStyle
    <Category("Appearance"), _
    Description("Defines The Line Stroke Style"), _
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
    TypeConverter(GetType(ExpandableObjectConverter))> _
    Public Property LineStyle() As VMLLineStyle
        Get
            Return _LineStyle
        End Get
        Set(ByVal Value As VMLLineStyle)
            _LineStyle = Value
        End Set
    End Property
    <Category("VML Symbolization"), _
    Description("Enables The Use Of A FillStyle")> _
    Public Property FillStyleEnabled() As Boolean
        Get
            Return _FillStyleEnabled
        End Get
        Set(ByVal Value As Boolean)
            _FillStyleEnabled = Value
        End Set
    End Property
    <Category("VML Symbolization"), _
    Description("Defines The VML Fill Style Properties To Be Used"), _
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
    TypeConverter(GetType(ExpandableObjectConverter))> _
    Public Property FillStyle() As VMLFillStyle
        Get
            Return _FillStyle
        End Get
        Set(ByVal Value As VMLFillStyle)
            _FillStyle = Value
        End Set
    End Property
    Protected Overrides Sub Render(ByVal output As _
                        System.Web.UI.HtmlTextWriter)

        'Stream the Javascript
        Page.RegisterStartupScript("VMLScript", _
             "<script language=""javascript""> " & _
             HelperFunctions.ReadJavasript & " </script>")

        MyBase.Render(output)
    End Sub
    Protected Overrides Sub AddAttributesToRender(ByVal output _
                                As System.Web.UI.HtmlTextWriter)

        Dim cursorString As String
        If Me.CustomCursorEnabled Then
            cursorString = "url(" & Me.CustomCursor.ToString.ToLower & ")"
        Else
            cursorString = Me.CursorStyle.ToString
        End If

        Select Case Me.ButtonType
            Case ButtonTypeEnum.Button
                output.AddAttribute(HtmlTextWriterAttribute.Type, "button")
                output.AddAttribute(HtmlTextWriterAttribute.Onclick, _
                       "activate('" & Me.ID & "','" & Me.BoundingDrawCanvas & _
                       "','POLYGON','" & cursorString & "')")
                output.AddAttribute(HtmlTextWriterAttribute.Value, Me.ButtonText)
            Case ButtonTypeEnum.Image
                output.AddAttribute(HtmlTextWriterAttribute.Type, "image")
                output.AddAttribute(HtmlTextWriterAttribute.Onclick, _
                       "activate('" & Me.ID & "','" & Me.BoundingDrawCanvas & _
                       "','POLYGON','" & cursorString & "'); return false;")
                output.AddAttribute(HtmlTextWriterAttribute.Src, _
                                   Me.ImageSrc.Replace("\", "/"))
                output.AddAttribute("onmouseout", "this.src='" & _
                       Me.ImageSrc.Replace("\", "/") & "';")

                'Add Mouseover & MouseDown Images
                If Not Me.ImageSrcMouseOver.Equals(String.Empty) Then
                    output.AddAttribute("onmouseover", "this.src='" & _
                           Me.ImageSrcMouseOver.Replace("\", "/") & "';")
                End If

                If Not Me.ImageSrcMouseDown.Equals(String.Empty) Then
                    output.AddAttribute("onmousedown", _
                           "return false; this.src='" & _
                           Me.ImageSrcMouseDown.Replace("\", "/") & "';")
                End If
        End Select

        MyBase.AddAttributesToRender(output)
    End Sub
    Protected Overrides Sub RenderChildren(ByVal output As _
                            System.Web.UI.HtmlTextWriter)

        'Create The VML Tag
        Dim outTags As New System.Text.StringBuilder
        outTags.Append("<v:polyline ID=" & Quote(Me.ID) & " ")

        Dim cursorString As String
        If Me.CustomCursorEnabled Then
            cursorString = Me.CustomCursor
            outTags.Append("style=" & DoubleQuoteChar & _
                           "z-index: 1002;cursor: url('" & _
                           cursorString & "');" & DoubleQuoteChar & " >")
        Else
            cursorString = Me.CursorStyle.ToString
            outTags.Append("style='cursor: " & cursorString & ";z-index: 1002;'>")
        End If

        outTags.Append(Me.LineStyle.StrokeTag)

        If Me.FillStyleEnabled Then
            outTags.Append(Me.FillStyle.FillTag)
        End If

        outTags.Append("</v:polyline>")

        output.Write(outTags.ToString)

    End Sub
End Class

*源代码:VMLPolygon.vb

整合

在设置好属性后,我们可以使用形状类的 RenderChildren 将 VML 标签写入浏览器。控件属性中分配的 ID 由客户端脚本使用,以动态修改 VML 形状属性。

Protected Overrides Sub RenderChildren(ByVal output As System.Web.UI.HtmlTextWriter)

    'Create The VML Tag
    Dim outTags As New System.Text.StringBuilder
    outTags.Append("<v:polyline ID=" & Quote(Me.ID) & " ")

    Dim cursorString As String
    If Me.CustomCursorEnabled Then
        cursorString = Me.CustomCursor
        outTags.Append("style=" & DoubleQuoteChar & _
                "z-index: 1002;cursor: url('" & _
                cursorString & "');" & DoubleQuoteChar & " >")
    Else
        cursorString = Me.CursorStyle.ToString
        outTags.Append("style='cursor: " & _
                cursorString & ";z-index: 1002;'>")
    End If

    outTags.Append(Me.LineStyle.StrokeTag)

    If Me.FillStyleEnabled Then
        outTags.Append(Me.FillStyle.FillTag)
    End If

    outTags.Append("</v:polyline>")

    output.Write(outTags.ToString)

End Sub

*源代码:VMLPolygon.vb

关注点

我在创建这些控件时遇到的一个问题是,能否将控件属性持久化到设计器。我发现 Visual Studio IDE 有时在构建这些控件的过程中不会更新或持久化这些属性,并且似乎在重新启动 IDE 后会自行解决。我相信这些问题在新版本的 IDE 中已得到解决。

未来的增强

我希望在未来增强这些控件,以便为通用形状、文本、圆弧、椭圆、图像和其他 VML 元素提供 VML 控件。我还想实现一种方法,将形状的坐标返回到服务器以满足特殊处理需求。

历史

  • 首次发布 - 2005 年 12 月 15 日
© . All rights reserved.