VML Web 控件






4.61/5 (12投票s)
2005年12月15日
5分钟阅读

73292

1909
用于 ASP.NET Web Forms 的 VML 绘图控件。
引言
我作为地理学家/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
基类的形状控件类组成。
VMLLine
VMLPolyline
VMLPolygon
VMLRectangle
VMLRoundRectangle
(继承自VMLRectangle
)
VMLControl
MustInherit
VMLControl
基类为控件提供了框架,并包含以下属性:
ID
ButtonType
[Button
|Image
]ButtonText
ImageSrc
,ImageSrcMouseDown
,ImageSourceMouseOver
CursorStyle
,CustomCursor
,CustomCursorEnabled
BoundingDrawCanvas
BoundingDrawCanvas
是一个**必需**的属性,它保存将用作矢量图形边界绘图画布的图像控件的 ID
。我使用了 System.Web.UI.WebControls.WebControl
的 AddAttributesToRender
覆盖方法来提醒用户此要求。一旦在控件的属性中满足了此要求,警告将消失。
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
以下是 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
(提供From
和To
点样式)
例如,VMLPolygon
控件具有类型为 VMLFillStyle
的 FillStyle
属性,该属性定义了要渲染的多边形的颜色、图案等。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
VMLPolygon
是 VMLDrawingControls
的一个示例。此控件继承自 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 日