Share on Facebook
Part One is here: You can find Scribbler here. There's an old 1.1 Version of Scribbler too, if you want to spend a few hours converting - it looks like it has more functionality. I haven't finished converting it yet, and probably won't. But I got close, if that counts!
Since I learn by doing, and I have a goal in mind I thought I'd get started. I described my project in an earlier post Silverlight Tools for eCommerce Sites: Infloor Heating Diagram Maker. So, this is my adventure - enhancing and adding to that Scribbler app to turn it into a diagram maker.
Since the Infloor-heating Diagram Maker is a tool that uses a lot of the same types of actions as a drawing program, Scribbler seemed like a fantastic base. I'm going to start by making a bit of a structural change to the locations of the classes that make up the application. Since in Scribbler all the classes are in the root folder, I just made a BL folder and placed them in there instead.
Next, I took it to Blend and got things arranged for this application, which takes a bitmapped grid as a drawing surface. I moved the tool palette. I added a Pen Icon and a Line tool class. And a Rectangle Icon and a Rectangle class.
Both Icon Button classes were modeled after the Brush tool icon which came with Scribbler. BrushControl.xaml. These classes use another Scribbler control called ToggleButton. This control allows a different image to be specified for it's two states. The two states translate to Selected and Unselected for the tool state. If the tool is selected, when the user drags the mouse the represented object is drawn. Just as you would expect.
I added some new Modes to the DrawingToolMode enum - which is located in Workspace.cs - so that it looks like this:
public enum DrawingToolMode {None,Brush,Select,Pen,Rectangle,}
The enum which is in use by the active tool is checked in the DrawingSurface.cs class. So this was modified to add the two new modes:
switch (this.Workspace.ToolMode) {
case DrawingToolMode.Brush:
this.currentTool = new DrawingPencilTool(this.Drawing, this);
break;
case DrawingToolMode.Select:
this.currentTool = new SelectionTool(this.Drawing, this);
break;
case DrawingToolMode.Pen:
this.currentTool = new PenTool(this.Drawing, this);
break;
case DrawingToolMode.Rectangle:
this.currentTool = new RectangleTool(this.Drawing, this);
break;
}
The two classes which allow either a straight line, or a rectangle to be drawn were both based on the same class as the existing Scribbler brush tool. That base class is DrawingTool. Using the same base class made the new classes automatically wired up into using the Drawing Surface and even hooked them into the color palette automatically. So that was cool. All I had to do was figure out one method for each new tool which would take care of getting the correct Geometric object onto the drawing surface.
In the PenTool.cs class, I used as a model the class that came with Scribbler and used with the Brush icon which is DrawingPencilTool.cs. (Sorry, I didn't name these two. Brush, Pencil - to me they're two different tools. Anyway, besides changing class specific terms, the main change came down to one line in HandleMouseMove event handler, that line is
this.currentStroke.ChangeLine(point);
private void HandleMouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown)
{
Point2 point = new Point2(e.GetPosition(this.drawingSurface.DrawingDisplayer));
if ((point - this.lastPoint).Length > 2)
{
//drawingSurface a Line not add a point.
this.currentStroke.ChangeLine(point);
this.lastPoint = point;
}
}
}
And then the method which adds the line, I also used as a model the AddPoint method which is called by the
Pencil/Brush tool.So, here's the ChangeLine Method from the StrokeModel.cs class.
public void ChangeLine(Point2 point) { this.points.Add(point); this.segments.Clear(); LineSegment lineSegment = new LineSegment(); lineSegment.Point = point.Point; this.segments.Add(lineSegment); Point p1 = points[0].Point; //1. LineSegment lineSegment2 = new LineSegment(); lineSegment2.Point = p1; this.segments.Add(lineSegment2); //2. Point p2 = new Point(); p2.X = point.Point.X; p2.Y = point.Point.Y; LineSegment lineSegment3 = new LineSegment(); lineSegment3.Point = p2; this.segments.Add(lineSegment3); if (this.Parent != null) this.Parent.FireBoundsChanged(); this.FireBoundsChanged(); }
for the rectangle tool, that method becomes:
public void ChangeRectangle(Point2 point) { this.points.Add(point); this.segments.Clear(); Point2 min = points[0]; Point2 max = point; LineSegment lineSegment = new LineSegment(); lineSegment.Point = min.Point; this.segments.Add(lineSegment); Point p1 = points[0].Point; //1. LineSegment lineSegment2 = new LineSegment(); lineSegment2.Point = p1; this.segments.Add(lineSegment2); //2. Point p2 = new Point(); p2.X = p1.X; p2.Y = point.Point.Y; LineSegment lineSegment3 = new LineSegment(); lineSegment3.Point = p2; this.segments.Add(lineSegment3); //3. Point p3 = new Point(); p3.X = point.Point.X; p3.Y = point.Point.Y; LineSegment lineSegment4 = new LineSegment(); lineSegment4.Point = p3; this.segments.Add(lineSegment4); Point p4 = new Point(); p4.X = point.Point.X; p4.Y = p1.Y; LineSegment lineSegment5 = new LineSegment(); lineSegment5.Point = p4; this.segments.Add(lineSegment5); LineSegment lineSegment6 = new LineSegment();//top lineSegment6.Point = p1; this.segments.Add(lineSegment6); if (this.Parent != null) this.Parent.FireBoundsChanged(); this.FireBoundsChanged(); }