The Ink Threading Model
One of the benefits of ink on a Tablet PC is that it feels a lot like writing with a regular pen and paper. To accomplish this, the tablet pen collects input data at a much higher rate than a mouse does and renders the ink as the user writes. The application's user interface (UI) thread is not sufficient for collecting pen data and rendering ink, because it can become blocked. To solve this, a WPF application uses two additional threads when a user writes ink.
The following list describes the threads that take part in collecting and rendering digital ink:
Pen thread - the thread that takes input from the stylus. (In reality, this is a thread pool, but this topic refers to it as a pen thread.)
Application user interface thread - the thread that controls the user interface of the application.
Dynamic rendering thread - the thread that renders the ink while the user draws a stroke. The dynamic rendering thread is different than the thread that renders other UI elements for the application, as mentioned in Window Presentation Foundation Threading Model.
The inking model is the same whether the application uses the InkCanvas or a custom control similar to the one in Creating an Ink Input Control. Although this topic discusses threading in terms of the InkCanvas, the same concepts apply when you create a custom control.
The following diagram illustrates the threading model when a user draws a stroke:
Actions occurring while the user draws the stroke
When the user draws a stroke, the stylus points come in on the pen thread. Stylus plug-ins, including the DynamicRenderer, accept the stylus points on the pen thread and have the chance to modify them before the InkCanvas receives them.
The DynamicRenderer renders the stylus points on the dynamic rendering thread. This happens at the same time as the previous step.
The InkCanvas receives the stylus points on the UI thread.
Actions occurring after the user ends the stroke
Each UIElement has a StylusPlugInCollection. The StylusPlugIn objects in the StylusPlugInCollection receive and can modify the stylus points on the pen thread. The StylusPlugIn objects receive the stylus points according to their order in the StylusPlugInCollection.
In the previous diagram, the following behavior takes place:
StylusPlugin1 modifies the values for x and y.
DynamicRenderer receives the modified stylus points and renders them on the dynamic rendering thread.
StylusPlugin2 receives the modified stylus points and further modifies the values for x and y.
The application collects the stylus points, and, when the user finishes the stroke, statically renders the stroke.
Suppose that stylusPlugin1 restricts the stylus points to a rectangle and stylusPlugin2 translates the stylus points to the right. In the previous scenario, the DynamicRenderer receives the restricted stylus points, but not the translated stylus points. When the user draws the stroke, the stroke is rendered within the bounds of the rectangle, but the stroke doesn't appear to be translated until the user lifts the pen.
Because accurate hit-testing cannot be performed on the pen thread, some elements might occasionally receive stylus input intended for other elements. If you need to make sure the input was routed correctly before performing an operation, subscribe to and perform the operation in the OnStylusDownProcessed, OnStylusMoveProcessed, or OnStylusUpProcessed method. These methods are invoked by the application thread after accurate hit-testing has been performed. To subscribe to these methods, call the NotifyWhenProcessed method in the method that occurs on the pen thread.
The following diagram illustrates the relationship between the pen thread and UI thread with respect to the stylus events of a StylusPlugIn.
As the user draws a stroke, DynamicRenderer renders the ink on a separate thread so the ink appears to "flow" from the pen even when the UI thread is busy. The DynamicRenderer builds a visual tree on the dynamic rendering thread as it collects stylus points. When the user finishes the stroke, the DynamicRenderer asks to be notified when the application does the next rendering pass. After the application completes the next rendering pass, the DynamicRenderer cleans up its visual tree. The following diagram illustrates this process.
The user begins the stroke.
The DynamicRenderer creates the visual tree.
The user is drawing the stroke.
The DynamicRenderer builds the visual tree.
The user ends the stroke.