I am designing a CAD application and am unsure how to design the event handling for mouse events in a clean way.
For simplicity, let's say I have two buttons in the UI: CreateSquare, and CreateCircle. When the user clicks one of these, I want the application to wait for a click in the canvas, and after the user clicks, to create either a square or a circle in the identified position.
From what I understand, it's necessary to listen to the MouseDown event in my canvas, and in the handler, write something like:
bool CreatingSquaresMode;
bool CreatingCirclesMode;
private void ModelViewport_MouseDown(object sender, MouseButtonEventArgs e)
{
if (CreatingSquaresMode) {
// create square at selected position.
}
if (CreatingCirclesMode) {
// create circle at selected position.
}
}
However that seems smelly, specially because I will have many different create commands and that quickly gets out of hand. It also breaks MVVM as I would like to have the CreateSquare and CreateCircle buttons to be bound to commands, and not have to worry about the MouseDown event.
One alternative I've considered is a state-machine, where I would determine all the possible modes of the application and replicate the above if-nest or switch-case with a little more elegance. It still feels like it's not the correct solution but it would be nicer.
The second alternative--which is the one I feel is the correct one--would be to somehow, within my CreateSquare command, to listen to the MouseDown event:
private void CreateSquare() {
//do something here
//wait for user mouse input
//create square
}
But I don't know if that's even possible.
What is the correct way to handle this situation? I'm sure there's a design pattern that ought to help me out here.
There are many different ways to solve your problem so there is no "correct" way however there are certainly better ways. I include one for you to think about however I can never say that mine (or anyone elses) design is the "best", thats up to you to decide.
In your solution you are using the first button press to set properties named "CreatingShapeMode". Do you only want one mode to be set at any particular time? If so consider using an enum
In my opinion your MouseDown event handler should not be doing the work of drawing the shape (as you point out this list is only going to get bigger). Instead I would look at using some custom shape drawing classes that all inherited from a base "ObjectCreator" type. Your first button click would set the correct type e.g. circleType or squareType; the second click on the canvas (that would fire your mouse down event) would only call the createShape method; it would not need any if statements because it is the objectcreator that would know how to draw the shape.
Here is a console application that has the minimum amount of code to demonstrate what I am trying to say. Obviously it will need a lot of adaptation to fit your problem.
You mention that you would like your buttons to be bound to commands, you would be able to do this using the command objects to set the correct ObjectCreator type.