I got my shot of TNF blockers for crohn's again yesterday, so I'm a bit on a high right now, getting a lot of stuff done. One of those things was implementing an input system based on the InkCanvas for one of my other projects. Now, I am by no means a graphical kind of guy, but I do have this pen tablet thingie lying around which has pressure sensitive input and is just good fun to play around with. Linking the pressure to other variables can get you some cool results.
And as you would expect, WPF's InkCanvas also supports pressure sensitive input devices. All be it not completely how I had expected. In most packages, it is possible to alter the transparency of the stroke through the pressure exercised on the stylus, which the WPF version doesn't do. Bugger.
Luckily, this turns out to be dead simple to implement for ourselves. InkCanvas has a protected property OnStrokeCollected which is called whenever a new stroke is added. If you override it, you can change the values of the stroke, like it's color which we will assign a new alpha value based on the average pressure of each point in the stroke.
/// <summary>
/// Raises the <see cref="E:System.Windows.Controls.InkCanvas.StrokeCollected"/>
/// event.
/// </summary>
/// <param name="e">The event data.</param>
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
if (IsPressuredTransparency == true)
{
float iPressure = (from i in e.Stroke.StylusPoints
select i.PressureFactor).Average();
Color iTemp = e.Stroke.DrawingAttributes.Color;
iTemp.A = (byte)((float)byte.MaxValue * iPressure);
e.Stroke.DrawingAttributes.Color = iTemp;
}
base.OnStrokeCollected(e);
}
Not much special to report here. Kirupa.com provided me the info on how to get the pressure data out of a stroke. The article also demonstrates a lot of other features of the InkCanvas. It definitely helped me to get started. In this implementation though, I used a linq statement to get the average value. The color is copied over to a temporary variable cause the color appears to be locked once it has been assigned to the drawing attributes.
Off course, life is never as simple as that, so there is one more little issue to handle. Apparently, the InkCanvas uses different renderers for the final result compared to while the strokes are being drawn. To show a transparency based on the pressure while the drawing action is still in progress, we need to use the protected property called DyamicRenderer which gets/sets the object used to render the strokes on a drawing context while the stroke is being drawn. This rendering object must be a descendent of DynamicRenderer. All you need to do here is override the OnDraw method and change the brush that is used. When you assign a new value to this property, the InkCanvas actually changes an internal 'PlugIn list' which is called whenever data is entered using the stylus. In fact, all UI elements have this plug in list. They use it to recognize gestures. We on the other hand, use it to change the rendering behavior of the ink canvas.
protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
{
SolidColorBrush iBrush = fillBrush as SolidColorBrush;
if (iBrush != null)
{
float iPressure = (from i in stylusPoints
select i.PressureFactor).Average();
Color iTemp = iBrush.Color;
//can't directly change the A of a color that is assigned
//to somehting
iTemp.A = (byte)((float)byte.MaxValue * iPressure);
base.OnDraw(drawingContext, stylusPoints, geometry,
new SolidColorBrush(iTemp));
}
else
base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush);
}
Again, not much going on here. The class can only change the alpha channel if a solid color is used to fill the stroke, if this is not the case, only the base method is called. Otherwise, we calculate the average pressure of the stylus points in the same way as the previous class. The only difference is that this function only draws a few points instead of the entire stroke.
So to recap, we need to override 2 classes to add pressure sensitive stroke transparency functionality:
- A render class in which we are going to change the brush of the stroke to render, based on the pressure value assigned to the points that are being rendered.
- A descendent of InkCanvas that uses this new render class and which changes the brush of the final stroke.
The full implementation of the 2 classes can be downloaded from here. To use it, replace your InkCanvas with the TransparentInkCanvas and set it's IsPressuredTransparency to true. Here's a small screenshot of the end result: