LANGUAGES: VB.NET | C#
ASP.NET VERSIONS: 1.x
Get the Most Out of Windows Forms Controls
Create Rich User Interfaces by Injecting Windows Forms Controls into Your ASP.NET
Web Pages
Developers love Web applications because they are easier to deploy than Windows
applications. But developers love Windows applications because functionally rich
user interfaces can be created rapidly. Wouldn’t it be nice to have the best of
both worlds? In this article I’ll show you how you can have your cake and eat it
too by supercharging your ASP.NET Web pages with Windows Forms controls.
Why Use Windows Forms Controls?
There are many examples of things that can be done easily with Windows Forms controls
that would be difficult or impossible with Web controls alone. For example, there
is no Web control that provides the rich functionality of the Windows Forms ComboBox
control. So what’s to stop a developer from using the ComboBox control on their
Web forms? Not much, as will be demonstrated shortly. The custom ComboBox control
defined in this article can even fetch its own data from a Web service.
How about interactive images that can constantly redraw and keep themselves up do
date? The Bar Graph custom Windows control detailed later in this article will demonstrate
a mesmerizing animated bar chart that can redraw itself based on user input without
having to call back to the server.
Browser and .NET Framework Detection
Every user will need two things to successfully consume Windows Forms controls in
a Web page: Internet Explorer and the .NET Framework. These requirements shouldn’t
be hard to meet these days, but there will sometimes be a minority of users who
don’t meet the requirements. Luckily, as a developer, you can help them find their
way. A few lines of code are all it takes to verify the user has Internet Explorer
and the .NET Framework installed; if they don’t, you can redirect them to an alternate
page or guide them toward a solution:
Dim msg As
String
If Request.Browser.Browser.ToUpper().IndexOf("IE")
>= 0 Then
If Request.Browser.ClrVersion.Major
> 0 Then
Msg ="Your browser is good and powerful."
Else
msg ="Get the .NET Framework from www.WindowsUpdate.com"
End If
Else
Response.Write("This page requires Internet Explorer.")
End If
Response.Write(msg)
The Request.Browser property contains information about the user’s Web browser.
In this example, searching for the string “IE” confirms the user is running Internet
Explorer.
The Request.Browser.ClrVersion property contains detailed version information for
the most up-to-date version of the .NET Framework that’s installed on the user’s
machine. The Major property will be zero if the user doesn’t have any version of
the .NET Framework installed. The GetClrVersions method (not shown) will return
an array of all the versions of the .NET Framework that are installed on the user’s
machine (in case you need such detailed information). In the vast majority of cases,
that level of detail shouldn’t be necessary, but it’s nice to know it’s there.
The Basics
To embed a custom Windows control in a Web page, you’ll need an HTML snippet such
as this:
<OBJECT
id="MyControl1"
height="20"
width="192"
classid="http:MyCustom.dll#MyNamespace.MyClassName">
<PARAM
NAME="BackColor"
VALUE="Yellow">
You have an unsupported
browser.
</OBJECT>
A special technique is used to overload the ClassID attribute so all the necessary
information can be specified to launch the .NET component. The ClassID must start
with “http:”, which must be followed by the name of the DLL containing the .NET
component. This DLL must reside in the Web application’s root directory. After the
DLL name there must be a pound sign (#), followed by the full name of the component
(namespace included).
<PARAM> tags can be used to declaratively set properties of the control. In
the previous example, the BackColor property of the control is set to yellow. Client-side
script can also be used to call properties and methods of the custom .NET Windows
control, as will be demonstrated later in the article.
The “unsupported browser” text will only appear in cases where the message is true.
A Basic ComboBox
Using the technique just described, it’s not possible to refer to any components
that reside in the GAC, and, therefore, it’s impossible to refer directly to a standard
Windows control. It is possible to use standard Windows controls in your Web pages,
but they must be encapsulated within a custom assembly that can be referred to with
the precise HTML syntax previously listed. It’s not difficult.
For example, compile this simple class into a new Windows Control Library using
Visual Studio:
Public Class
MyComboBox
Inherits System.Windows.Forms.ComboBox
End Class
Add the resulting control to a Web page using the earlier HTML snippet syntax and
you’re good to go. Don’t forget to place the control’s resulting DLL in the Web
application’s root directory, or else it won’t be downloaded to the user’s computer.
(advertisement)
An Enhanced ComboBox
A ComboBox isn’t very useful unless you can get data into and out of it. The above
ComboBox control will be enhanced to retrieve its own data from a Web service.
After setting a reference to a local Web service that returns a DataSet, this improved
ComboBox control calls the Web service back on its home server and fills itself
with the resulting data:
Public Class
ComboBox
Inherits System.Windows.Forms.ComboBox
Public Sub
New()
MyBase.New()
'Fetch data from a web service and fill
the ComboBox
Dim ws
As New MyWebService.MyDataSource
For
Each dr As DataRow
In ws.GetDataSet.Tables(0).Rows
Items.Add(dr(0).ToString)
Next
End Sub
End Class
This control inherits from the standard Windows Forms ComboBox control, thereby
getting all its functionality. It then extends the standard functionality by calling
a Web service from within the constructor. Finally, it loops through the records
and adds them to the Items collection of the ComboBox. Figure 1
shows it in action.

Figure 1: Don’t be discouraged by
the fact that there’s no ComboBox Web control in ASP.NET. The Windows Forms ComboBox
control can be used in Web applications.
To retrieve the value the user selects in the ComboBox, an OnSubmit attribute is
added to the HTML Form tag. Using a line of JavaScript, the ComboBox value is placed
into an HTML HiddenTextBox control just before the page is submitted back to the
server:
onsubmit="document.forms[0].HiddenTextBox1.value
=
document.forms[0].ComboBox1.Text;"
Because that HiddenTextBox is marked with the runat=“Server” attribute, the value
can be easily retrieved from server-side code:
Response.Write("You chose " + HiddenTextBox1.Value
An Animated Bar Graph Control
The bar graph shown in Figure 2 is another example of a custom
Windows Forms control that doesn’t need to post back to the server. In fact, there
is no server-side code needed at all for this page, as you can see by the plain
HTML page listed in Figure 3.

Figure 2: It would be difficult —
if not impossible — to implement such a rich, animated bar graph as this with HTML
and JavaScript alone. Windows Forms controls are simply better at many tasks than
Web controls.
<html>
<head>
<title>BarTest</title>
<meta content="JavaScript" name="vs_defaultClientScript">
</head>
<body
MS_POSITIONING="FlowLayout">
<OBJECT
id="WinWebBarGraph1"
height="300"
width="400"
classid="http:ControlFreak.dll#ControlFreak.BarGraph"
name="WinWebBarGraph1"
VIEWASTEXT>
<PARAM NAME="Backcolor" VALUE="lightblue">
</OBJECT>
<br><br>
September:
<INPUT
id="txtSep" type="text" value="40"
name="txtSep"><br>
October:
<INPUT
id="txtOct" type="text" value="50"
name="txtOct"><br>
November:
<INPUT
id="txtNov" type="text" value="60"
name="txtNov"><br>
December:
<INPUT
id="txtDec" type="text" value="70"
name="txtDec"><br><br>
<BUTTON
id="graphbutton"
onclick="graphbutton_onclick(1);"
name="graphbutton"
type="button"
value="Button">Static
</BUTTON>
<BUTTON
id="Button1"
onclick="graphbutton_onclick(2);"
name="graphbutton"
type="button"
value="Button">Random Animation
</BUTTON>
<BUTTON
id="Button2"
onclick="graphbutton_onclick(3);"
name="graphbutton"
type="button"
value="Button">Smooth Animation
</BUTTON>
<script
language="javascript">
WinWebBarGraph1.AddBar("September",40);
WinWebBarGraph1.AddBar("October",50);
WinWebBarGraph1.AddBar("November",60);
WinWebBarGraph1.AddBar("December",70);
WinWebBarGraph1.DrawBarsAnimatedSmooth()
function graphbutton_onclick(AnimStyle)
{
WinWebBarGraph1.ClearBars();
WinWebBarGraph1.AddBar("September",txtSep.value);
WinWebBarGraph1.AddBar("October",txtOct.value);
WinWebBarGraph1.AddBar("November",txtNov.value);
WinWebBarGraph1.AddBar("December",txtDec.value);
if (AnimStyle==1) WinWebBarGraph1.DrawBars();
if (AnimStyle==2) WinWebBarGraph1.DrawBarsAnimatedRandom();
if (AnimStyle==3) WinWebBarGraph1.DrawBarsAnimatedSmooth()
}
</script>
</body>
</html>
Figure 3: Windows controls can be
embedded in plain HTML pages, with no server-side required. In this example, client-side
JavaScript is used to call methods of the Windows control that are hosted within
the browser.
Just after the beginning <body> tag you can see the <OBJECT> tag, with
syntax matching the description at the beginning of this article. The Backcolor
property is set to light blue via a <PARAM> tag. After that, a few textboxes
are followed by a few buttons. Finally there is a block of JavaScript that sets
initial values for the bar graph control and tells the control to begin its colorful
animation cycle.
The final graphbutton_onclick JavaScript function operates similarly when one of
the buttons is clicked, setting the bar graph values to match the user’s input.
The bar graph control is capable of two different kinds of animation, as well as
a static (non-animated) layout. There is an HTML button on the form matching each
animation style. All three buttons call this client-side JavaScript function, which
clears the old bar values, adds new bars with sizes that match the user-entered
values, and sets the animation style according to the user’s command.
Internally, the BarGraph control contains a collection of Bar objects. The Bar class
is nothing very fancy; basically, it’s just a container to hold a few values. The
Bar class is defined in Figure 4.
Figure 4: A bar object is instantiated
for each bar in the BarGraph control.
What’s more interesting is the code for the actual BarGraph control. The basic drawing
method for this control is listed in Figure 5. This function is
for drawing the static (non-animated) version of the BarGraph control.
Public Sub
DrawBars()
Timer1.Enabled = False
'initialize a drawing surface
Dim objBitmap
As Bitmap = New Bitmap(Me.Width,
Me.Height)
Dim objGraphics
As Graphics = _
Graphics.FromImage(objBitmap)
objGraphics.FillRectangle(New SolidBrush(Me.BackColor), _
0, 0, Me.Width, Me.Height)
'loop through and draw each bar
Dim iCount
As Integer = 0
For Each
Bar As cBar In
_Bars
'determine the bar dimensions
Dim Width
As Single = _
Convert.ToSingle(Me.Width
/ _Bars.Count - 20)
Dim BarHeightPercent
As Integer = _
Convert.ToInt32(Bar.Value / _Bars.MaxValue
* 100)
Dim BarHeight As Single = _
Convert.ToSingle(BarHeightPercent _
/ 100 * Me.Height
- 20)
Dim rect
As New RectangleF(iCount * Width + 20
_
* iCount + 10, (Me.Height
- 10) - BarHeight, _
Width, BarHeight - 10)
'specify the bar colors
Dim Color1
As Color = Color.Green
Dim Color2
As Color = Color.Blue
'draw the bar
objGraphics.FillRectangle(New
_
Drawing2D.LinearGradientBrush(rect, Color1, Color2,
_
Drawing2D.LinearGradientMode.ForwardDiagonal),
rect)
'Draw the text
Dim drawFont
As New Font("Arial Narrow", 10)
Dim drawPoint As New PointF(rect.X,
rect.Y + _
rect.Height + 1)
Dim drawBrush As New SolidBrush(Color.Black)
objGraphics.DrawString(Bar.Text, drawFont, _
drawBrush, drawPoint)
'Draw the value
drawPoint = New PointF(rect.X,
rect.Y - 14)
objGraphics.DrawString(Bar.Value.ToString, _
drawFont, drawBrush, drawPoint)
iCount += 1
Next
Me.BackgroundImage = objBitmap
_DisplayStyle = DisplayStyleEnum.NonAnimated
End Sub
Figure 5: The DrawBars method of
the BarGraph control loops through each bar and draws them along with their associated
text.
First, the internal timer is disabled because it’s used only for the animation functions.
Then the drawing surface is initialized and is filled with the specified background
color. The code then loops through each Bar object in the Bars collection. The bar
dimensions are then calculated and the two bar colors are chosen before each rectangular
bar is drawn with a gradient brush. The bar text is drawn just below the bar, and
the bar value is drawn just above the bar.
Finally, the completed in-memory image is assigned to be the background image of
the control, which causes it to appear. Keep in mind that all this code is being
executed by the .NET Framework on the user’s machine and the output is displayed
in the user’s browser.
A similar function is shown in Figure 6. This function is called
each time the encapsulated timer ticks, rapidly redrawing the bars with random colors
and gradient modes.
Public Sub
DrawBarsAnimatedRandom()
Dim rdm As
Random = New Random
'initialize a drawing surface
Dim objBitmap
As Bitmap = New Bitmap(Me.Width,
Me.Height)
Dim objGraphics
As Graphics = _
Graphics.FromImage(objBitmap)
objGraphics.FillRectangle(New SolidBrush(Me.BackColor),
_
0, 0, Me.Width,
Me.Height)
'loop through and draw each bar
Dim iCount
As Integer = 0
For Each
Bar As cBar In
_Bars
'determine the bar dimensions
Dim Width
As Single = Convert.ToSingle(Me.Width
/ _
_Bars.Count - 20)
Dim BarHeightPercent As Integer = _
Convert.ToInt32(Bar.Value / _Bars.MaxValue * 100)
Dim BarHeight
As Single = _
Convert.ToSingle(BarHeightPercent / 100 _
* Me.Height - 20)
Dim rect
As New RectangleF(iCount * Width + 20
_
* iCount + 10, (Me.Height
- 10) - BarHeight, _
Width, BarHeight - 10)
'determine the bar’s colors randomly
Dim Color1
As Color = Color.FromArgb(rdm.Next(0, 255), _
rdm.Next(0, 255), rdm.Next(0, 255))
Dim Color2
As Color = Color.FromArgb(rdm.Next(0, 255), _
rdm.Next(0, 255), rdm.Next(0, 255))
'random gradient style
Dim ldb
As Drawing2D.LinearGradientMode
Select
Case rdm.Next(0, 3)
Case 0
ldb =Drawing2D.LinearGradientMode.BackwardDiagonal
Case 1
ldb = Drawing2D.LinearGradientMode.ForwardDiagonal
Case 2
ldb = Drawing2D.LinearGradientMode.Horizontal
Case 3
ldb = Drawing2D.LinearGradientMode.Vertical
End
Select
'draw the bar
objGraphics.FillRectangle(New
_
Drawing2D.LinearGradientBrush(rect, _
Color1, Color2, ldb), rect)
'Draw the text
Dim drawFont
As New Font("Arial Narrow", 10)
Dim drawPoint
As New PointF(rect.X, rect.Y + _
rect.Height
+ 1)
Dim drawBrush
As New SolidBrush(Color.Black)
objGraphics.DrawString(Bar.Text, drawFont, _
drawBrush,
drawPoint)
'Draw the value
drawPoint = New PointF(rect.X,
rect.Y - 14)
objGraphics.DrawString(Bar.Value.ToString, _
drawFont,
drawBrush, drawPoint)
iCount += 1
Next
Me.BackgroundImage = objBitmap
_DisplayStyle = DisplayStyleEnum.AnimatedRandom
Timer1.Enabled = True
End Sub
Figure 6: The System.Random class
is used extensively in this alternate version of the DrawBars method to create an
alluring animation of random, pulsating colors.
The general structure of this method is essentially the same as the DrawBars method.
It starts by initializing a drawing surface and filling it with the specified background
color. Looping through each bar, the same calculations are used to determine the
bar dimensions. Then things start to diverge a bit. A random gradient mode is chosen;
the gradient colors are chosen randomly, as well. Then, as in the DrawBars function,
the text values are drawn and the function finishes by assigning the image to be
the background image of the control. A client-side timer is enabled, ensuring that
this function will be called again shortly so that the next frame of the animation
can be drawn.
The full source code for the BarGraph control contains another function named DrawBarsAnimatedSmooth
(the full source code is available; see end of article for download details). This
function is similar, but uses slightly different randomization algorithms for the
drawing to create an alternate (but equally alluring) animation.
(advertisement)
Limitations and Security Issues
Windows Forms controls are better than ActiveX controls from a security standpoint.
Every Web-based ActiveX control comes with a security warning that users must accept
or deny no matter what the control does. The control might be entirely harmless
— or it might maliciously reformat their hard drive. It’s an all-or-nothing scenario,
and if an uneducated user makes the wrong choice, there is no limit to the potential
negative outcomes. Today, ActiveX controls are one of the primary sources of Spyware
and Adware infections.
The .NET Framework provides a much more robust security system, improving upon Java’s
“Sandbox” methodology. To make decisions about whether the control is harmless or
potentially dangerous it examines the kinds of functions that the control wants
to use, evidence about the user, and application context. Potentially dangerous
functions are blocked from execution, although educated users can make exceptions
with the .NET Framework Wizard located under Control Panel | Administrative Tools.
The controls in this article contain no potentially dangerous function calls; therefore,
they are automatically downloaded and run on the user’s computer without any kind
of warning or dialog box needing to appear at all.
What kinds of functions are considered potentially dangerous? Internet-based applications
have the most restrictions, while intranet applications have somewhat more lenient
boundaries. For example, all network calls are blocked for Internet applications
except calls back to the originating application. The ComboBox control listed earlier
in this article calls back to the same server from which the containing Web page
is hosted. Calls to a different server would fail unless the user (or a custom installation
program) explicitly grants the assembly the necessary permissions. This security
is based on the URL, so http://localhost is considered different than http://192.168.0.1
— even if both those URLs ultimately point to the same server. If you tinker with
the ComboBox control from this article, make sure the browser’s URL root matches
the URL root of the Web service it is calling.
Access to the user’s hard drive is also heavily restricted. Although isolated storage
is available for temporary files, reading from other locations is strictly prohibited
unless the user explicitly chooses a file through the permitted OpenFileDialog class.
Reflection and Serialization are also entirely prohibited. No XSL transformations
are permitted, and ADO.NET access is limited. Calls to unmanaged code are prohibited,
such as Windows API functions.
Another issue is that communication between client-side script and Windows controls
is currently one-way only. That is, JavaScript functions within the page can call
methods of the hosted Windows control, but the Windows control cannot raise events
back to the page. This (hopefully temporarily) broken functionality is related to
the current mix of patches for Windows, Internet Explorer, and the .NET Framework.
Although this limitation is annoying, you should be familiar with such one-way communication
because it is essentially a metaphor for HTTP communication. That is, Web browsers
can make requests to Web servers, but Web servers cannot call directly to a Web
browser without the Web browser initiating the conversation.
Unfortunately, all of these limitations rule out a lot of potentially useful applications,
but most of these limitations are there for good reason — so users can sleep more
soundly at night (and so Microsoft won’t catch so much heat about security problems!).
Despite the limitations, clearly there are many good uses for Windows Forms controls
in Web applications, just a few of which have been demonstrated in this article.
The sample code in this article is available for download.
This article was originally published in
ASP.NET Pro Magazine.