HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

WPF circle and line drawer solution

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
circlesolutionlinewpfanddrawer

Problem

I've created a WPF program which can draw lines and circles on a canvas. It works well, but I don't think it's the best solution. How can I make it work better? How would you create this program?

MainWindow class:

public partial class MainWindow : Window
{
DrawCircle dc;
DrawLine dl;
public MainWindow()
{
InitializeComponent();
}

private void cVas_MouseMove(object sender, MouseEventArgs e)
{
switch (rbLine.IsChecked)
{
case true:
if (dl != null)
dl.MouseMove(Mouse.GetPosition(cVas));
break;
default:
if (dc != null)
dc.MouseMove(Mouse.GetPosition(cVas));
break;
}
}

private void cVas_MouseDown(object sender, MouseButtonEventArgs e)
{
switch (rbLine.IsChecked)
{
case true:
dl = new DrawLine(this);
dl.MouseDown(Mouse.GetPosition(cVas));
break;
default:
dc = new DrawCircle(this);
dc.MouseDown(Mouse.GetPosition(cVas));
break;
}

}

private void cVas_MouseUp(object sender, MouseButtonEventArgs e)
{
dc = null;
dl = null;
}

private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
cVas.Children.Clear();
}
}


DrawLine class (the DrawCircle is similar to this):

`class DrawLine
{
private Line myLine;
MainWindow mw;

public DrawLine(MainWindow mw)
{
this.mw = mw;
}

public void MouseDown(Point mousePoint)
{
myLine = new Line();

Solution

A little explanation first. I have written your code in a different way. This does not mean that my code is perfect in any way. I just wanted you to see other options and possibilities to achieve the same result. I'm open for comments if supported by valid arguments.

First of all I suggest you use another naming convention. This way, the name of your controls and variables have a better meaning. For example 'cVas', I'd rename that to 'DrawingCanvas' or 'DrawingSurface'.

Then there's the structure of your code. You have different shapes that have a similar structure and share a certain functionality. In situations like that, you can start thinking about inheritance and/or the use of interfaces.

Below you'll see the code that I have written. Again, this code is not perfect but it shows how it can be done. And to be honest: I think my code is easier to read, shorter and also easier to expand.

I have used an interface, from which both class derive. This way it was easy to create a Pen-object that will draw an IDrawable object. Using the Pen-object also de-couples your UI from the shape-objects. In the constructor you pass the UIElement (in this code only Canvas can be passed through) on which the pen will draw: they are coupled but in this case this is needed.

Due to the fact of the interface and pen-object, the code in your MainWindow.xaml.cs will also be a lot cleaner. You only need to instantiate one IDrawable-object and depending on the mouse-actions you provide what is needed.

I hope you find this useful and helpful! ;)

XAML:

I have not changed much about the XAML except for the names of the controls and I used a StackPanel instead of a Grid with columns.

IDrawable

public interface IDrawable
{
    void Draw(Point location);
}


MyCircle

//Since you didn't provide the code of your circle-class
//I quickly wrote a dummy-circle class, it works but not
//flawless (X and Y coordinates)
public class MyCircle : IDrawable
{
    public Ellipse Circle { get; private set; }

    public MyCircle(Point location)
    {
        Circle = new Ellipse
        {
            Stroke = Brushes.Black,
            StrokeThickness = 2,
            Margin = new Thickness(location.X, location.Y, 0, 0)
        };
    }

    public void Draw(Point location)
    {
        if(Circle != null)
        {
            Circle.Width = location.X - Circle.Margin.Left;
            Circle.Height = location.Y - Circle.Margin.Top;
        }
    }
}


MyLine

public class MyLine : IDrawable
{
    public Line Line { get; private set; }

    public MyLine(Point location)
    {
        Line = new Line
        {
            Stroke = Brushes.Black,
            StrokeThickness = 2,
            X1 = location.X,
            X2 = location.X,
            Y1 = location.Y,
            Y2 = location.Y 
        };
    }

    public void Draw(Point location)
    {
        Line.X2 = location.X;
        Line.Y2 = location.Y;
    }
}


Pen

public class Pen
{
    public Pen(Canvas holder)
    {
        _holder = holder;
    }

    private readonly Canvas _holder;

    public void Down(IDrawable obj)
    {
        //if more shapes are added:
        //better use a switch-statement
        if(obj is MyCircle)
            _holder.Children.Add((obj as MyCircle).Circle);
        if(obj is MyLine)
            _holder.Children.Add((obj as MyLine).Line);
    }

    public void Draw(IDrawable obj, Point location)
    {
        obj.Draw(location);
    }
}


MainWindow

public MainWindow()
{
    InitializeComponent();
    KeyDown += MainWindowKeyDown;
    DrawingSurface.MouseMove += DrawingSurfaceMouseMove;
    DrawingSurface.MouseDown += DrawingSurfaceMouseDown;
    DrawingSurface.MouseUp += DrawingSurfaceMouseUp;
    _pen = new Pen(DrawingSurface);
}

private IDrawable _dr;
private readonly Pen _pen;

private void DrawingSurfaceMouseUp(object sender, MouseButtonEventArgs e)
{
    _dr = null;
}

private void DrawingSurfaceMouseDown(object sender, MouseButtonEventArgs e)
{
    if(LineRadioButton.IsChecked == true)
        _dr = new MyLine(Mouse.GetPosition(DrawingSurface));
    else if(CircleRadioButton.IsChecked == true)
        _dr = new MyCircle(Mouse.GetPosition(DrawingSurface));

    _pen.Down(_dr);
}

private void DrawingSurfaceMouseMove(object sender, MouseEventArgs e)
{
    if(_dr != null)
        _pen.Draw(_dr, Mouse.GetPosition(DrawingSurface));
}

private void MainWindowKeyDown(object sender, KeyEventArgs e)
{
    if(e.Key == Key.Enter)
        DrawingSurface.Children.Clear();
}

Code Snippets

public interface IDrawable
{
    void Draw(Point location);
}
//Since you didn't provide the code of your circle-class
//I quickly wrote a dummy-circle class, it works but not
//flawless (X and Y coordinates)
public class MyCircle : IDrawable
{
    public Ellipse Circle { get; private set; }

    public MyCircle(Point location)
    {
        Circle = new Ellipse
        {
            Stroke = Brushes.Black,
            StrokeThickness = 2,
            Margin = new Thickness(location.X, location.Y, 0, 0)
        };
    }

    public void Draw(Point location)
    {
        if(Circle != null)
        {
            Circle.Width = location.X - Circle.Margin.Left;
            Circle.Height = location.Y - Circle.Margin.Top;
        }
    }
}
public class MyLine : IDrawable
{
    public Line Line { get; private set; }

    public MyLine(Point location)
    {
        Line = new Line
        {
            Stroke = Brushes.Black,
            StrokeThickness = 2,
            X1 = location.X,
            X2 = location.X,
            Y1 = location.Y,
            Y2 = location.Y 
        };
    }

    public void Draw(Point location)
    {
        Line.X2 = location.X;
        Line.Y2 = location.Y;
    }
}
public class Pen
{
    public Pen(Canvas holder)
    {
        _holder = holder;
    }

    private readonly Canvas _holder;

    public void Down(IDrawable obj)
    {
        //if more shapes are added:
        //better use a switch-statement
        if(obj is MyCircle)
            _holder.Children.Add((obj as MyCircle).Circle);
        if(obj is MyLine)
            _holder.Children.Add((obj as MyLine).Line);
    }

    public void Draw(IDrawable obj, Point location)
    {
        obj.Draw(location);
    }
}
public MainWindow()
{
    InitializeComponent();
    KeyDown += MainWindowKeyDown;
    DrawingSurface.MouseMove += DrawingSurfaceMouseMove;
    DrawingSurface.MouseDown += DrawingSurfaceMouseDown;
    DrawingSurface.MouseUp += DrawingSurfaceMouseUp;
    _pen = new Pen(DrawingSurface);
}

private IDrawable _dr;
private readonly Pen _pen;

private void DrawingSurfaceMouseUp(object sender, MouseButtonEventArgs e)
{
    _dr = null;
}

private void DrawingSurfaceMouseDown(object sender, MouseButtonEventArgs e)
{
    if(LineRadioButton.IsChecked == true)
        _dr = new MyLine(Mouse.GetPosition(DrawingSurface));
    else if(CircleRadioButton.IsChecked == true)
        _dr = new MyCircle(Mouse.GetPosition(DrawingSurface));

    _pen.Down(_dr);
}

private void DrawingSurfaceMouseMove(object sender, MouseEventArgs e)
{
    if(_dr != null)
        _pen.Draw(_dr, Mouse.GetPosition(DrawingSurface));
}

private void MainWindowKeyDown(object sender, KeyEventArgs e)
{
    if(e.Key == Key.Enter)
        DrawingSurface.Children.Clear();
}

Context

StackExchange Code Review Q#21579, answer score: 5

Revisions (0)

No revisions yet.