CoverStory
LANGUAGES: VB.NET
ASP.NET VERSIONS: 2.0
Inked
Create Ink-enabled Web Sites
By Julia Lerman
This article provides an introduction to the Ink APIs for Web
developers targeted at Windows XP Tablet PC users. Follow along and you ll learn
to build a simple Windows Forms control that is Ink-enabled, embed it into a Web
page, then interact with it.
Although Tablet PCs have a lot of great built-in
functionality, such as handwriting recognition, the Tablet PC SDK enables
developers to tap into the power of Ink to write a lot of interesting
applications. Over the years I ve tried to look at applications that think out
of the box of typical data entry to see where Ink can really shine. In
addition to some of the awesome illustration software, some eye-opening
examples of these are XThink s MathJournal and Physics Illustrator, which came
out of the Microsoft Research Labs. Although handwriting and recognition are
the first things that most people think of, it s the true mobility of these
computers that make them stand out. Consider any scenario where a worker uses a
clipboard to fill out a form, then hands that to someone else to enter into a
computer. This is the perfect target for an Ink-enabled business application.
But there are many other, more interesting things that can be done, such as
marking up images (consider an architect, or perhaps an insurance adjuster
inspecting a damaged car). The real estate and medical industries have also embraced
the technology in a very big way.
One thing that many of these applications have in common
is that they are all being built as client-side Windows applications. The
reason for this is that the Tablet PC is dependent on a digitizer that covers
the computer s screen. The digitizer receives the magnetized signals from the
stylus and sends those to the operating system, which identifies the strokes
and passes them on to the software (internal or customized). The software then
uses the Tablet PC API to interpret and work with the resulting data. With
Windows XP, a special version of the O/S, Windows XP Tablet PC Edition, was
required. However, with Windows Vista, the Tablet PC functionality is wrapped
into the base O/S. This article will focus on Windows XP Tablet PC Edition.
Because all of this work is done on the client side,
embedding Ink into Web sites has not been an obvious target for developers.
Additionally, the challenges of working with ActiveX or other embedded controls
in a Web page have been daunting.
Basic Requirements
You don t need a Tablet PC to do Tablet PC development.
Installing the Tablet PC SDK will give your computer most of what you need to
develop and test your applications. MSDN has articles on setting up for
development, as well as developing off-tablet (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tpcsdk10/lonestar/devcenter/tbidxindex.asp).
If you want to test the inking abilities with something finer than what a mouse
will render, you can plug into your system an inexpensive digitizer, such as
one made by Wacom. Whether you have a Tablet PC or not, you ll need to download
and install the Tablet PC SDK 1.7, as well as the updates to integrate with .NET
2.0 (http://www.microsoft.com/downloads/details.aspx?FamilyId=B46D4B83-A821-40BC-AA85-C9EE3D6E9699&displaylang=en; http://www.microsoft.com/downloads/details.aspx?FamilyID=69640b5c-0ee9-421e-8d5c-d40debee36c2&displaylang=en).
The MSDN articles will assist you with this. Users must have a Tablet PC to
interact with your Ink-enabled controls.
Your First Ink-enabled Web Application
Now to the code! The remainder of this article will walk
you through building a simple Ink-enabled Windows Forms control, as well as
embedding it into a Web page.
This control will allow users to draw or write into it
using a few colors and a few pen widths, erase strokes, and clear the control
completely. It will also have the ability to expose the contents of the control
so you can get the data to the server side for saving into a database or
whatever you choose to do with the data.
It is important to note that you should build as much
logic into the control as possible, thereby minimizing the necessary
interaction between the Web page and the control. For example, place controls
for changing the pen color within the user control. That way it won t be
necessary to write JavaScript code to tell the user control to change a color.
In Visual Studio, create a Windows Control Library project
and add to the project a reference to the Microsoft Tablet PC API. A user
control is automatically created. We ll work with that one. The first thing to
do to this control is give it a border using the control s BorderStyle
property. Without a border, it s impossible to determine where to ink when the
control is embedded in the Web page.
A ToolStrip control is a great place to organize all the
buttons for interacting with Ink. With the buttons, we can tell the control to
select a new color for the pen or change other characteristics of how the ink
will be drawn into the control. Add eight ToolStripButtons to the ToolStrip.
Using the properties for each button, change the first four buttons. Each
button should have DisplayStyle set to None and the BackGroundColor set to a
color of your choice. The fifth and sixth buttons will be used to set the ink
weight to Thick and Thin . The next button will be used to allow the user to
erase strokes. Change the DisplayStyle of these buttons to Text and their Text
properties accordingly. Of course, you can use an eraser image instead of text,
if you d like. The last ToolTipButton should be for clearing the entire
control. Figure 1 shows what the control should look like so far.
Figure 1: The control as it should
look so far.
Ink-enabling the User Control
It s time to get Ink capabilities into the control. This
is done using the Microsoft.Ink.InkOverlay object, which is a component, not a
control. InkOverlay is instantiated and attached to whichever control you would
like to be Ink-enabled. If this were a Windows Form, you may want to Ink-enable
a TextBox control. In some cases, we ll want the entire user control Ink-enabled.
However, because the ToolStrip control is there, we ll create a panel to fill
the balance of the user control and Ink-enable this panel.
The basic structure of the object model is that InkOverlay
contains an Ink object, which is made up of a collection of strokes. A stroke
is defined by the starting point when a user touches the stylus to the screen, to
the ending point when the stylus is lifted from the screen. You can drill down further
to work with all the points that make up a stroke.
Drop a panel onto the control and set its docking property
to Fill. In the code, create a variable for the InkOverlay object, and in the
Load method instantiate the object, attach it to the panel, and enable it:
Imports Microsoft.Ink
Public Class UserControl1
Dim inkO As InkOverlay
Private Sub
UserControl1_Load(ByVal sender As Object, _
ByVal e As
System.EventArgs) Handles Me.Load
inkO = New
InkOverlay(Panel1)
inkO.Enabled = True
End Sub
End Class
Thanks to Visual Studio 2005 s UserControl TestContainer,
you can run the project and test the drawing ability of the control. If you are
not on a Tablet PC, you ll be able to draw with your mouse although it will
be a bit ragged as the resolution of the mouse is not nearly as fine as that of
a stylus and digitizer.
Formatting the Ink
The Tablet PC API has a surprising amount of functionality.
We ll barely scratch the surface in this example, but it s a great place to
start.
The InkOverlay.DefaultDrawingAttributes property contains
properties to control the color and other attributes of the ink. This affects
ink to be drawn, not ink that already exists in the control. The ChangeInkColor
method below can be used to change the ink color. Wire up the method to the Click
events of the four color buttons:
Private Sub ChangeInkColor(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Dim button As
ToolStripButton = sender
inkO.DefaultDrawingAttributes.Color = button.BackColor
End Sub
The thinInk and thickInk Click events change the Width of
the DefaultDrawingAttributes:
Private Sub thinInk_Click(ByVal sender As System.Object, _
ByVal e As
System.EventArgs) Handles thinInk.Click
inkO.DefaultDrawingAttributes.Width = 50
End Sub
Private Sub
thickInk_Click(ByVal sender As System.Object, _
ByVal e As
System.EventArgs) Handles thickInk.Click
inkO.DefaultDrawingAttributes.Width = 150
End Sub
The InkOverlay.EditingMode options are Ink, Delete, and
Select. In the Click event of the Eraser button, we ll toggle the mode from ink
to eraser. At the same time, the text of the button will change so that when
the InkOverlay is in eraser mode, it will be obvious that clicking the button
will set the user back to ink mode. Note that there are two ways to erase,
controlled by the InkOverlay.EraserMode property. The default mode is
StrokeErase, which will remove an entire stroke by touching any point on the
stroke. The alternate mode is PointErase, which behaves more like a typical
erase action, removing ink only at the positions the eraser touches:
Private Sub Eraser_Click(ByVal sender As System.Object, _
ByVal e As
System.EventArgs) Handles Eraser.Click
If inkO.EditingMode =
InkOverlayEditingMode.Delete Then
inkO.EditingMode =
InkOverlayEditingMode.Ink
Eraser.Text =
"Erase"
Else
inkO.EditingMode =
InkOverlayEditingMode.Delete
Eraser.Text =
"Ink"
End If
End Sub
The last button on the ToolStrip is used to clear all the
ink from the control, using the InkOverlay.Ink.DeleteStrokes method. You also
need to invalidate the control that is attached to the InkOverlay to force it
to redraw itself. Otherwise, even though the strokes no longer exist in the Ink
control, they remain displayed on the drawing surface:
Private Sub ClearControl_Click(ByVal sender _
As System.Object, ByVal e
As System.EventArgs) _
Handles ClearControl.Click
inkO.Ink.DeleteStrokes()
Panel1.Invalidate()
End Sub
Run the project and check out all the functionality of the
control in the UserControl TestContainer (see Figure 2).
Figure 2: Test the control s
functionality.
Adding Functions to Interact with the Web Page
At this point you could easily embed the control in a Web
page and let the user have some fun drawing. All the functionality built in to
the control will work on the Web page with no additional coding required.
However, the user won t be able to do anything more than draw on the page; there
would be no way to save images or send them anywhere, either by e-mail or to
server applications, such as a Web service or a database.
Let s rectify this situation; let s add functionality that
the Web page can call. For this article, we ll enable the application to
extract the Ink data from the Ink control so you can save it as a .bmp file on
the server. Although it is certainly possible to store the Ink data so that it
may be retrieved and pulled back into an Ink-enabled control and edited
further, that is beyond the scope of this article.
We ll be adding a public function named GetInkData that
can be called by the host (the Web page); it will return a string to the page.
The Ink class has a Save function that outputs a byte array
in several formats. One format, InkSerializedFormat (or ISF) persists all the
metadata about the Ink object. This is convenient for placing the data into an Ink
control at a later date. Another format, GIF, saves the data as a GIF file and
adds the ISF metadata, as well. That means if you want to have a true image
file and the ISF data, you need only save once. The resulting byte array can
then be written out to a GIF file or pulled back into an Ink-enabled control.
The GetInkData function will output the GIF-persisted data;
however, for it to be transmitted from the client-side script to the server-side
code it first must be converted to a Base64 string format. Although the
Ink.Save function has alternative persistence formats for Base64, they require
additional manipulation. Therefore, this code will persist to GIF and then use
the .NET conversion method to get the Base64 string. Add the following function
to the Ink control:
Public Funtion GetInkData() As String
If inkO.Ink.Strokes.Count
= 0 Then
Return ("empty")
Else
Dim inkBytes As Byte() =
inkO.Ink.Save(PersistenceFormat.Gif)
Return
Convert.ToBase64String(inkBytes)
End If
End Function
Now the control is complete. But before building the
project, there s one more very important step! The assembly must be visible to
COM. This can be done by opening the Application Tab of the project properties,
then clicking the Assembly Information button and checking the Make Assembly
COM-Visible checkbox.
Compile the project. The resulting DLL is what gets added
to the Web site. Because it will be an embedded resource, you ll actually do
this simply by copying and pasting the DLL file, just as you would an image
file.
Creating the Host Web Site
Create a new ASP.NET Web site. I have successfully run
this sample using an IIS (HTTP) Web site, a file-based server (File System) Web
site, and a Web Application project. Note that if you do development in a File
System server, it will be necessary to change the user control s class name any
time you modify the user control and recompile the assembly.
Next, add the DLL to the project. If the Web site is in
the same solution as the user control project, you can copy and paste from one
project to the other. Otherwise, you can use the Add Existing Item method to
add the DLL to the Web site.
Prior to Internet Explorer 7, it was enough to embed the
control directly into the HTML of the Web page with the <object> tags.
However, now you must embed the Ink control using a separate JavaScript file or
else the user will have to click on the control to activate it. Clicking with
the stylus will leave unwanted marks on the drawing surface. Add a new JavaScript
file to the Web site using the Add New Item option; name the file
EmbedInkControl.js.
The object will be embedded using the assembly name and
the strongly typed name of the user control s class. My user control project
was named aspnetPROInkControl; by default this is the name of the DLL assembly.
The class has the default name of the user control (UserControl1). The code for
the JavaScript file looks like this:
document.write('<object id="InkControl" classid=
"aspnetPROInkControl.dll#aspnetPROInkControl.UserControl1"
height="400"
width="400"></object>');
Because the control is being embedded when the page is
rendered, you won t see it on the Web page at design time. You ll most likely
want to adjust the height and width and determine the placement of the control.
I get around this by embedding the control directly at design time using the
following snippet in the HTML of my page:
<object id="InkControl"
classid="aspnetPROInkControl.
dll#aspnetPROInkControl.UserControl1"
height="400"
width="400"></object>
When I am satisfied with this, I modify the JavaScript
file accordingly, then replace the <object> tags with the call to the
JavaScript file:
<script src="EmbedInkControl.js"></script>
This will render the Ink control in the position where the
script tags are placed. The last steps are to call the GetInkData function from
the client side and send the data to the server.
Place an <asp:button> control on the Web page. Also,
place an HTML Hidden input control somewhere on the page. Be sure to make the
Hidden input control available to the server by setting its runat property to
server. The following code assumes that the form and these controls have their
default names of form1, Button1, and Hidden1.
Two things need to happen when the button is clicked. The
first is a client-side call to the GetInkData function in the embedded Ink control.
This call will also place the resulting Ink in a hidden HTML control. Then the
button s Click event will read the data from the hidden HTML control and save
it to a GIF file on the server.
Thanks to a new property in the ASP.NET Button control,
named OnClientClick, it is possible to fire off both the client-side and server-side
events with just one click of the button. Change the OnClientClick property to
SaveInk. Then add the SaveInk JavaScript function to the source of the Web
page. Place it inside the opening <body> tag:
<script>
function SaveInk()
{
form1.Hidden1.value =
form1.InkControl.GetInkData();
}
</script>
Fortunately, the client-side event is hit first.
Therefore, in the server-side Click event function for the button you ll be
able to read the value of the Hidden1 control, which will already have the Ink
data. Because the data is in Base64String format, it will be necessary to
convert it back to bytes to create the GIF image file from it. Note that in the
control there was a test to ensure that there was data in the Ink control. If
there was no data, the control returns empty . The Button1_Click event tests
for the return value of empty before proceeding with the file creation:
Protected Sub Button1_Click(ByVal sender as Object, _
ByVal e as
System.EventArgs) Handles Button1.Click
Dim inkString as String
inkString=Hidden1.Value
If InkString<>"empty"
Then
Dim bytes() as
Byte=Convert.FromBase64String(InkString)
System.IO.File.WriteAllBytes(Server.MapPath("") & _
"\myInkImage.gif",bytes)
End Sub
Now you should be able to run the Web page, draw in it,
and save your drawing. One thing to point out is that once you get to the
button s Click event and have the string that is returned by Hidden1.Value, you
can do whatever you want with the data. Though the example merely saves it to
an image file, you could save the data into a database or an XML file, store it
in Session for use elsewhere on the site, send it to a Web service, or take
some other action on it. At this point it s simply a string. Figure 3 shows the
Web page with the Ink control embedded in it.
Figure 3: The Web page with the Ink
control embedded in it.
Conclusion
Ink-enabled Web sites can be used for fun or as part of
business applications. For example, rather than a drawing palette, consider
allowing users to mark-up online documents or to actually use their signature
to sign a document. Be aware that hidden controls are not secure, so anything
that requires security should be encrypted prior to storing it in the hidden
control. The biggest challenge, of course, is to find end users with more
artistic talent than the typical developer, as evidenced by the drawings in
this article.
The source code accompanying
this article is available for download.
Julia Lerman is an
independent consultant and .NET Mentor
who has been designing and writing software applications for more than 20
years. She lives in Vermont,
where she runs the Vermont.NET User Group. Julia is well known in the .NET
community as an INETA Speaker, .NET MVP, ASPInsider, conference speaker, and
prolific blogger. You can read Julia s blog at http://thedatafarm.com/blog.