Title: Let the user draw a smooth curve with WPF and C#
This example combines techniques used by the following examples.
The program uses the following code to keep track off what it is doing while it draws the smooth curve.
// The curve's tension.
private double Tension = 1.0;
// True when we are drawing.
private bool Drawing = true;
// The currently selected points.
private List<Point> Points = new List<Point>();
The Tension field keeps track of the currently selected tension, which is used to draw the curve. The variable Drawing will be true when the user is allowed to add a point to the curve. The Points list stores the points that the user clicks.
When the user presses the mouse down, the following event handler executes.
// The user clicked. Add a point,
// stop drawing, or start a new curve.
private void canDrawing_MouseDown(object sender, MouseButtonEventArgs e)
{
// See if we are currently drawing.
if (Drawing)
{
// See if this is the left or right mouse button.
if (e.ChangedButton == MouseButton.Left)
{
// Left button. Add a new point.
Points.Add(e.GetPosition(canDrawing));
}
else
{
// Right button. Stop drawing.
Drawing = false;
}
}
else
{
// We are not drawing. Start a new curve.
Drawing = true;
Points = new List<Point>();
// Add a new point.
Points.Add(e.GetPosition(canDrawing));
}
// Redraw.
DrawCurve(Tension);
}
If the user is currently drawing, then the event handler checks to see which button was pressed. If the user clicked the left button, then the program gets the mouse's position and adds it to the Points list. If the user clicked the right button, then the code sets Drawing to false to stop drawing the current curve.
If Drawing was false when the user pressed the button, then the program starts a new curve. To do that, it sets Drawing to true, creates a new Points list, and adds the mouse's position to the new list.
After it has dealt with the mouse press, the event handler calls the following DrawCurve method.
// Make the curve.
private void DrawCurve(double tension)
{
// Remove any previous curves.
canDrawing.Children.Clear();
// Draw the curve.
if (Points.Count > 1)
{
Path path = MakeCurve(Points.ToArray(), tension);
path.Stroke = Brushes.LightBlue;
path.StrokeThickness = 5;
// If we are drawing, make the curve dashed.
if (Drawing)
{
path.StrokeDashArray.Add(2);
path.StrokeDashArray.Add(2);
}
canDrawing.Children.Add(path);
}
// Draw the points.
foreach (Point point in Points)
{
Rectangle rect = new Rectangle();
rect.Width = 6;
rect.Height = 6;
Canvas.SetLeft(rect, point.X - 3);
Canvas.SetTop(rect, point.Y - 3);
rect.Fill = Brushes.White;
rect.Stroke = Brushes.Black;
rect.StrokeThickness = 1;
canDrawing.Children.Add(rect);
}
}
This code start by removing any previous shapes from the Canvas control named canDrawing.
If the Points list contains more than one point, the program then calls the MakeCurve method to make a Path representing a smooth curve that connects the points. See the example Draw a smooth curve in WPF and C# for an explanation of how that method works.
Next, the code sets the path's Stroke and StrokeThickness properties to produce a thick, light blue line. If the user is still drawing the curve, the code also adds 2 to the path's StrokeDashArray collection twice. The values in that collection indicate the amounts that the curve should draw and skip to produce a dashed result. In this example, the values {2, 2} make the curve draw 2 units, skip 2 units, and then repeat. Note that the units are multiples of the curve's width. Because this example's curve is 5 pixels wide, that means the dashes and gaps between the dashes are 10 pixels long.
If the StrokeDashArray collection contains an odd number of values, then they are reused as necessary. For example, suppose the collection contains the values {1, 4, 1}. Then that is equivalent to using the values {1, 4, 1, 1, 4, 1}. The picture on the right shows the result.
This example could have specified the value 2 one time in the StrokeDashArray collection and gotten the same result it gets by specifying that value twice. I included the value twice to make the code easier to understand.
After it finishes defining the path, the program adds it to the Canvas control. It then loops through the Points list and makes a small rectangle for each point. Notice how the code uses the Canvas class's SetLeft and SetTop methods to position the rectangles. This is how you set an attached property in code. In this example, the Canvas class provides the two attached properties Left and Top. It also provides the methods SetLeft and SetTop to set those values.
Download the example to experiment with it and to see additional details.
|