Monday, June 23, 2008

Creating a sketch application in Silverlight - Part 1, Sketching on the InkPresenter

As some of you know, I try to help people out on the Silverlight Forum. It's nice to help people and I have learned a lot by doing that.


One of the members of the forum (RamsZone) had a question about saving strokes that are drawn on the InkPresenter. Since I was already working on a sketch application and had already solved that puzzle, I promised him/her to post an article on my weblog explaining how to do this.
So I started a few hours ago to create a simple sketch application with save functionality. This article is the first part of describing how to create a sketch application. This part is focussed on the sketching itself. The next part will address the save issue. I hope to post the second part today as well.


1. Getting started


First, we need to create the XAML necessary for our sketch application. All we need right now is the InkPresenter and a button which the user can press to save the drawing:



2. Add eventhandlers


We need eventhandlers for the MouseLeftButtonDown, MouseLeftButtonUp and MouseMove events to make sure our sketch application responds to input the way we would expect it to:

LayoutRoot.MouseLeftButtonDown += new MouseButtonEventHandler(LayoutRoot_MouseLeftButtonDown);
LayoutRoot.MouseLeftButtonUp += new MouseButtonEventHandler(LayoutRoot_MouseLeftButtonUp);
LayoutRoot.MouseMove += new MouseEventHandler(LayoutRoot_MouseMove);


3. MouseLeftButtonDown and MouseLeftButtonUp handlers


We need very little code to produce the functionality we need in these handlers.
All we need to do here is set the value of private field to make sure that drawing is starting and stopped. This way we can determine if the visitor is keeping the mousebutton down so he/she is drawing:
void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
this._drawing = false;
this._drawingContinuousLine = false;
}

void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this._drawing = true;
}


4. MouseMove event


This is were the drawing is really done. Normally you would assume the code to be quite simple, something like this:
StylusPointCollection originalPoints = e.StylusDevice.GetStylusPoints(inkpresenter);
Stroke stroke = new Stroke();
stroke.DrawingAttributes.Color = Colors.Black;
stroke.DrawingAttributes.Width = 13.0;
stroke.DrawingAttributes.Height = 13.0;
stroke.StylusPoints = originalPoints;
inkpresenter.Strokes.Add(stroke);


So, this works.... There are strokes drawn on the inkpresenter. However... (bet you didn't see that one coming ;) ), the strokes are visible as dots. When moving the mouse very slow, it seems to be no problem. When moving very fast, there are gaps between the dots which makes it look like a dotted line instead of a smooth line.
We can fix this by using keeping track of the last detected point and if a continuous line is drawn. We do this by declaring 2 private field, so our private fields look like this:
public partial class Page : UserControl
{
private bool _drawing = false;
private bool _drawingContinuousLine = false;
private StylusPoint _lastDetectedPoint;


The MouseMove will look like this:
void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
{
if (this._drawing)
{
StylusPointCollection originalPoints = e.StylusDevice.GetStylusPoints(inkpresenter);

StylusPointCollection newPoints = new StylusPointCollection();
StylusPointCollection moreNewPoints = new StylusPointCollection();

if (this._drawingContinuousLine)
{
StylusPoint point = new StylusPoint(this._lastDetectedPoint.X + 3, this._lastDetectedPoint.Y + 3);
point.PressureFactor = this._lastDetectedPoint.PressureFactor;
newPoints.Add(point);

StylusPoint morepoint = new StylusPoint(this._lastDetectedPoint.X + 6, this._lastDetectedPoint.Y + 6);
morepoint.PressureFactor = this._lastDetectedPoint.PressureFactor;
moreNewPoints.Add(morepoint);
}

foreach (StylusPoint thisPoint in originalPoints)
{
StylusPoint morepoint = new StylusPoint(thisPoint.X + 6, thisPoint.Y + 6);
morepoint.PressureFactor = thisPoint.PressureFactor;
moreNewPoints.Add(morepoint);

StylusPoint newpoint = new StylusPoint(thisPoint.X + 3, thisPoint.Y + 3);
newpoint.PressureFactor = thisPoint.PressureFactor;
newPoints.Add(newpoint);
}

Stroke stroke = new Stroke();
stroke.DrawingAttributes.Color = Colors.Black;
stroke.DrawingAttributes.Width = 13.0;
stroke.DrawingAttributes.Height = 13.0;
stroke.StylusPoints = newPoints;
inkpresenter.Strokes.Add(stroke);

Stroke morestroke = new Stroke();
morestroke.StylusPoints = moreNewPoints;

this._drawingContinuousLine = true;

this._lastDetectedPoint = originalPoints[originalPoints.Count - 1];
}
}


One thing that isn't handled in this application is resizing of the browser. If you remove the width and height in the UserControl in XAML and of the LayoutRoot Grid, you will be able to draw in the full available space of the browser. However, the application must be (re)loaded when the browserwindow is maximized.

That's it for now. Now you can start practising your sketching skills. In the next part I'll try to explain how to save the drawings on the server.


Click here for the demo

Click here for the source

No comments:

Post a Comment