Creating Our Own Types
We know that the whiteboard application will have two modes, i.e., Write mode and Erase mode so we go ahead and create an enum
to keep track of the current mode.
enum MODE
{
WRITE,
ERASE
};
Now the way we are going to achieve the drawing functionality is:
- Get the position where user clicked the mouse down (say
point1
) - Check for mouse movement
- If mouse is moved, then get the new mouse position (say
point2
) - Draw a line from
point1
to point2
- Now this new position will become the old position because user will eventually move the mouse further (i.e.,
point1 = point2
) - Keep doing all the above steps till user releases the mouse.
So we now create a small struct
for holding the Line
data:
struct Line
{
public Pen pen;
public Point p1;
public Point p2;
public Line(Pen p, Point one, Point two)
{
pen = p;
p1 = one;
p2 = two;
}
}
This Line
keeps track of the terminal points of the line and the Pen
which was used to draw this line (since Pen
contains Color
& Width
information for the Line
).
Tracking the Mouse Down and Up Events
The way we do this is to have a member variable to remember the state and handing the Mouse_down
and Mouse_Up
Events of the SplitContainers
's Panel
. Also, since we are planning to keep track of mouse position when the mouse is pressed or released, we need to keep track of the mouse position too in these events.
bool isPressed = false;
int x1;
int y1;
private void splitContainer1_Panel2_MouseUp(object sender, MouseEventArgs e)
{
x1 = e.X;
y1 = e.Y;
isPressed = false;
}
private void splitContainer1_Panel2_MouseDown(object sender, MouseEventArgs e)
{
x1 = e.X;
y1 = e.Y;
isPressed = true;
}
Now when the mouse is moved, we need to draw the line and update the points which will be done in Mouse_Move
event.
int x2;
int y2;
private void splitContainer1_Panel2_MouseMove(object sender, MouseEventArgs e)
{
if (isPressed == true)
{
x2 = e.X;
y2 = e.Y;
Line line = new Line(CurrentPen, new Point(x1, y1), new Point(x2, y2));
g.DrawLine(line.pen, line.p1, line.p2);
x1 = x2;
y1 = y2;
}
}
The code above shows CurrentPen
is being to draw the line. This variable will actually be updated based on users selection of markers. If the user selects a marker, it will return a marker of appropriate color and if user selects eraser, it will return a Pen
of white color (effectively an eraser). The implementation details are shown below. (Refer to the source code for details.)
pen = new Pen(Color.Blue, 2.0f);
eraser = new Pen(Color.White, 100.0f);
private Color CurrentColor
{
get { return pen.Color; }
set { pen.Color = value; }
}
private Pen CurrentPen
{
get
{
if (mode == MODE.WRITE)
{
return pen;
}
else
{
return eraser;
}
}
}
The Problem
We are now almost done with the main functionality as we are drawing on the board with either marker or eraser. But there is a small problem. The problem is when our application goes out of focus, i.e., if user minimizes it or does an alt-tab, the drawings on our board will go away. To persist the drawing in such conditions, we need to:
- Remember the drawings on the board (this will change our
Mouse_Move
method) - Override the
Paint
method and redraw everything in it.
So let us have a list that keeps track of whatever we drew so far.
List<Line> lines = null;
private void splitContainer1_Panel2_MouseMove(object sender, MouseEventArgs e)
{
if (isPressed == true)
{
x2 = e.X;
y2 = e.Y;
Line line = new Line(CurrentPen, new Point(x1, y1), new Point(x2, y2));
g.DrawLine(line.pen, line.p1, line.p2);
lines.Add(line);
x1 = x2;
y1 = y2;
}
}
And then, we just have to redraw everything in the overridden Paint
method.
private void splitContainer1_Panel2_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
foreach (Line l in lines)
{
g.DrawLine(l.pen, l.p1, l.p2);
}
}
And that's it, we have a rudimentary implementation of whiteboard in place. This is not the most elegant way to do it, but it is one way to do it.
History
- 8th February, 2012: First implementation of a rudimentary white board
- 9th February, 2012: Updated to use all colors and choose custom marker size (as suggested in the comments)
- 10th February, 2012: Minor changes in user interface, removed the annoying radio button
- 14th March, 2012: Fixed the bug where drawing colors are getting changed on repaint