Using the Provided Data
Now that you know how to write a Real Time Stylus plug-in and register it with a control that you want to use the stylus/pen on, you have knowledge of almost everything you need to use this API. There is one important tidbit of elusive information remaining: making sense of the data provided, especially in the Packets()
There are a few key issues that force you to take a close look at the data passed to you. For one, the PacketsData object is a very bare-bones object that is an indexed list of information coming along from the digitizer. You might expect properties such as X and Y on each contained information object, but instead, the object is a very low-level list of stylus information. This is due to two simple facts. For one, performance is extremely important, so spinning up a very simple list of objects is preferable over a more complex list of objects. The second reason is that different tablet manufacturers, different system configurations, or different versions of the API could pass different data. The fact that two different tablets may produce different stylus data, even in the same version of the SDK, is a core consideration.
|The Real Time Stylus API gives you access to raw pen data, rather than just the Ink input resulting from pen operations.|
So how exactly can you make sense of this data? The good news is that core data always appears at the same position within the list. The X position of the pen is always item number 0. Y is always item number 1. In a simple scenario, this might be all the data communicated to you, so each packet consists of two properties. This information is communicated through the data object's PacketPropertyCount
Knowing the packet length is crucial because there is one more twist: every time the Packet()
method is called and a data object is passed, you may receive more than one packet. In other words, if the pen is moved very rapidly, more than one pen position may be communicated in each pass. For instance, the data object may have a Count
of four, which might mean that you have two sets of X and Y coordinates. In this case, the PacketPropertyCount
is two. In a very similar scenario, the Count
may be six. But this does not necessarily mean that you got three different sets of X and Y coordinates. You have to inspect the PacketPropertyCount
property first. If it is still set to 2, you have received three sets of X and Y coordinates, but if it is set to 3, you have two sets of X and Y coordinates, as well as a third piece of information (pen pressure).
The following code snippet shows how all this information can be used in a modified version of the Packet()
Public Sub Packets(ByVal s As RealTimeStylus, _
ByVal data As PluginData.PacketsData) _
Console.WriteLine("Number of Packets: " + _
Console.WriteLine("Properties per packet: " _
Dim pc As Integer
For pc = 0 To _
data.Count - data.PacketPropertyCount _
Console.WriteLine("Packet: X " _
+ data(pc).ToString() + _
"/ Y " + data(pc + 1).ToString())
If data.PacketPropertyCount > 2 Then
Console.WriteLine("Pressure: " _
+ data(pc + 2).ToString())
Because the C# version of the code is very similar, and in the interest of space, I will not list it from here on. You can download all the samples used in this article in both C# and VB.NET.
This example runs a loop over all data received, skipping forward by the number of properties that make up each packet.
Here is an interesting thing to try: run this example and use your mouse as the pen. Simply click the mouse button, move around a bit, and let go of the button. You will see that the plug-in shows a lot of X and Y coordinates. The length of each packet will be 2. No information other than X/Y is provided. Then, try this with a real stylus (pen). You will see that the packet property count increases and all of sudden, pressure information is available as well. Note that you have to account for this in code with an If
There is one final twist when it comes to handling packet data. You may have noticed that the coordinates you received were somewhat unusual. My Tablet PC has a screen resolution of 1400 x 1050 pixels. When I use the pen on that very tablet, I receive X and Y coordinates that could be something like 4577/3575. The reason for this is that today's digitizers operate at much higher resolutions than current displays. In many (but not all) scenarios, it is important to map the resolution of the digitizer to the resolution of the screen. For instance, you may want to draw a dot where the pen touched the screen. For that, you need to know what position 4577/3575 maps to on the display.
This task can be achieved relatively easily as long as you have access to a GDI+ Graphics object, which provides important information such as horizontal and vertical screen dpi. In your current version, you do not have access to such an object, but you can easily create one, as long as you have access to the control the stylus plug-in is used with. I modified the code so that each plug-in can be handed a reference to the assigned control (see Listing 5
). The following is the sub-section of the modified code that is responsible for the translation:
Dim g As Graphics = _
Dim iX As Integer
Dim iY As Integer
iX = g.DpiX * data(packetCounter) / 2540
iY = g.DpiY * data(packetCounter + 1) / 2540
The important aspect is that you use the DPI assigned to the X and Y axis of the current display system and then multiply it by the X and Y coordinates passed to you, divided by 2540 (this value remains constant).
This takes care of all the hard parts, and you can now start having some fun with this technology.