I found this brief MVC example for Java which used four direction buttons (North, South, East, West) and a speed slider, and provided a message to indicate whether the direction had changed and whether any speed was specified. I decided well, it should be possible to do something similar in C# WinForms, even though I was well aware that WinForms isn't considered a suitable vehicle for this pattern. (I'm obstinate.)
I also thought it would be nice to actually draw lines, like that old "game boy" Etch-A-Sketch.
Model-View-Controller :wikipedia
Pre-Warning: This isn't an MVC tutorial, so don't expect to conquer this subject here!
(I do have a tutorial for MVP though, which is a more suitable pattern.)

The drawing happens in a Panel (pnlDraw):

I anchored the Controls so that the form can be resized. (I didn't include any code to prevent drawing spiling outside of the Panel.)
The Model (MoveModel.cs) is simple enough:
It is just storing the state (Direction and Pixels) and notifying of changes to this state.
What about the View? It is possible to create a separate View class but, as the View should be fairly passive, the Form class itself can provide/be the View. This is different to Java where we write code to construct the GUI. (We could, of course, do the same in C#, which I know is many people's preference.) In this case, the construction of the GUI and its consideration as "the View" doesn't really cause us any pause for thought. With C#, and the Form Designer, it seems unnecessary, overkill, to go to the lengths of creating a distinct View class. That is, in my opinion.
Anyway, the View (our Form) stores a reference to the Model, listens for changes in the Model, and updates the UI, by providing information in a Label's Text and drawing lines.
This is where MVC starts to unravel a little. Shouldn't the Controller tell the View to draw lines? Should the Controller call a method of the View to draw lines? The inbuilt ability of a WinForm to draw on itself makes it a little awkward for the Controller to commandeer this feature.
Now the Controller is supposed to respond to UI events, to Button clicks (in the Form/View). How do we transfer this event-processing to the Controller? Using INotifyPropertyChanged (again)? With Custom Events? It just doesn't seem "right" that, for example, click-events in the Form class would raise an event that the Controller would listen for, and the Controller would then call a method of the Form to draw on itself.
[I suppose the Buttons and NumericUpDown themselves could be exposed as public properties of the Form so that the Controller can take total charge or responding to Click/Change events. This is a step too far, though, an anti-pattern, as the Controller would then be too tightly integrated with the specific UI/WinForm.]
Anyway, I'm over-thinking this. Besides, it is just a mini-project which I knew was likely to end in tears (sad face).
Let's continue. I decided to use Custom Events, Directionchanged and PixelsChanged. When a Button is clicked, or the pixel-value is increased (using a NumericUpDown Control) it raises either of these events. The Controller listens for these events and updates the Model. The Form/View is listening for these changes to the Model, and responds by providing feedback in the Label and re-drawing the lines.
Her is the View/Form (frmMove.cs):
The Controller itself (MoveController.cs) is straightforward, listening for activity in the form (responding to UI events) and updating the Model:
However, at the end of the day I've skipped a bit. From Wikipedia:
I am not sending any commands to the View, the View updates itself by responding to the Model. Earlier though, it states "The third part, the controller, accepts input and converts it to commands for the model or view". The "or" is significant and means that I am still following, essentially, this pattern:

This hints at why MVC is typically more suited as a Web framework. In a website the View, the web page, is stateless. The Controller receives responses and will then send commands back to the View (or with the View).
Here is Program.cs that ties it all together:
Was this a useful exercise? Have I learnt much about MVC? Maybe. I suppose I've managed to confirm that WinForms and MVC are not a good match, and I've had a little more practice with Custom Events. The application itself is reasonably interesting.
I also thought it would be nice to actually draw lines, like that old "game boy" Etch-A-Sketch.
Model-View-Controller :wikipedia
Pre-Warning: This isn't an MVC tutorial, so don't expect to conquer this subject here!
(I do have a tutorial for MVP though, which is a more suitable pattern.)

The drawing happens in a Panel (pnlDraw):

I anchored the Controls so that the form can be resized. (I didn't include any code to prevent drawing spiling outside of the Panel.)
The Model (MoveModel.cs) is simple enough:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; namespace MoveMVC { public enum DIRECTION { UP, DOWN, LEFT, RIGHT }; public class MoveModel : INotifyPropertyChanged { // INotifyPropertyChanged Interface // https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx public event PropertyChangedEventHandler PropertyChanged; private DIRECTION _direction; private int _pixels; public MoveModel() { this._direction = DIRECTION.UP; this._pixels = 10; } public DIRECTION Direction { get { return this._direction; } set { this._direction = value; NotifyPropertyChanged(); } } public int Pixels { get { return this._pixels; } set { this._pixels = value; NotifyPropertyChanged(); } } // This method is called by the Set accessor of each property. // The CallerMemberName attribute that is applied to the optional propertyName // parameter causes the property name of the caller to be substituted as an argument. private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } }
It is just storing the state (Direction and Pixels) and notifying of changes to this state.
What about the View? It is possible to create a separate View class but, as the View should be fairly passive, the Form class itself can provide/be the View. This is different to Java where we write code to construct the GUI. (We could, of course, do the same in C#, which I know is many people's preference.) In this case, the construction of the GUI and its consideration as "the View" doesn't really cause us any pause for thought. With C#, and the Form Designer, it seems unnecessary, overkill, to go to the lengths of creating a distinct View class. That is, in my opinion.
Anyway, the View (our Form) stores a reference to the Model, listens for changes in the Model, and updates the UI, by providing information in a Label's Text and drawing lines.
This is where MVC starts to unravel a little. Shouldn't the Controller tell the View to draw lines? Should the Controller call a method of the View to draw lines? The inbuilt ability of a WinForm to draw on itself makes it a little awkward for the Controller to commandeer this feature.
Now the Controller is supposed to respond to UI events, to Button clicks (in the Form/View). How do we transfer this event-processing to the Controller? Using INotifyPropertyChanged (again)? With Custom Events? It just doesn't seem "right" that, for example, click-events in the Form class would raise an event that the Controller would listen for, and the Controller would then call a method of the Form to draw on itself.
[I suppose the Buttons and NumericUpDown themselves could be exposed as public properties of the Form so that the Controller can take total charge or responding to Click/Change events. This is a step too far, though, an anti-pattern, as the Controller would then be too tightly integrated with the specific UI/WinForm.]
Anyway, I'm over-thinking this. Besides, it is just a mini-project which I knew was likely to end in tears (sad face).
Let's continue. I decided to use Custom Events, Directionchanged and PixelsChanged. When a Button is clicked, or the pixel-value is increased (using a NumericUpDown Control) it raises either of these events. The Controller listens for these events and updates the Model. The Form/View is listening for these changes to the Model, and responds by providing feedback in the Label and re-drawing the lines.
Her is the View/Form (frmMove.cs):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace MoveMVC { public partial class frmMove : Form { // the view needs a reference to the model private MoveModel _model; // for feedback private DIRECTION _direction; private int _pixels; private Point _origPoint, _prevPoint; Boolean notMoved = true; private List<Point> _pts = new List<Point>(); // custom events // http://www.dreamincode.net/forums/topic/176796-quick-and-easy-custom-events/ public event EventHandler<DirectionArgs> Directionchanged; public event EventHandler<PixelArgs> PixelsChanged; private void RaiseDirectionchanged(DIRECTION direction) { EventHandler<DirectionArgs> handler = Directionchanged; if (handler != null) { handler(null, new DirectionArgs(direction)); } } private void RaisePixelsChanged(int pixels) { EventHandler<PixelArgs> handler = PixelsChanged; if (handler != null) { handler(null, new PixelArgs(pixels)); } } public frmMove(MoveModel model) { InitializeComponent(); this._model = model; this._model.PropertyChanged += _model_PropertyChanged; this._origPoint = new Point(this.pnlDraw.Width / 2, this.pnlDraw.Height / 2); } private void frmMove_Load(object sender, EventArgs e) { // determine initial state this._direction = _model.Direction; this._pixels = _model.Pixels; this.nudPixels.Value = this._pixels; lblFeedback.Text = "Direction: " + this._direction + "\nPixels:" + this._pixels; } private void btnDirection_Click(object sender, EventArgs e) { Button btn = (Button)sender; RaiseDirectionchanged((DIRECTION)Enum.Parse(typeof(DIRECTION), btn.Text)); } private void nudPixels_ValueChanged(object sender, EventArgs e) { NumericUpDown nud = (NumericUpDown)sender; RaisePixelsChanged((int)nud.Value); } private void _model_PropertyChanged(object sender, PropertyChangedEventArgs e) { // update values from model this._direction = _model.Direction; this._pixels = _model.Pixels; lblFeedback.Text = "Direction: " + this._direction + "\nPixels:" + this._pixels; if (e.PropertyName == "Direction") { this.pnlDraw.Invalidate(); } } private void pnlDraw_Paint(object sender, PaintEventArgs e) { int newX = 0, newY = 0; if (notMoved) { newX = this._origPoint.X; newY = this._origPoint.Y; notMoved = false; } else { newX = this._prevPoint.X; newY = this._prevPoint.Y; } switch (this._direction) { case DIRECTION.UP: newY -= this._pixels; break; case DIRECTION.DOWN: newY += this._pixels; break; case DIRECTION.LEFT: newX -= this._pixels; break; case DIRECTION.RIGHT: newX += this._pixels; break; default: break; } this._prevPoint = new Point(newX, newY); this._pts.Add(this._prevPoint); using (Pen pen = new Pen(Color.Black, 3)) { Point tempPt = this._origPoint; foreach (Point pt in this._pts) { e.Graphics.DrawLine(pen, tempPt, pt); tempPt = pt; } } } } public class DirectionArgs : EventArgs { #region Fields private DIRECTION _direction; #endregion Fields #region Constructors public DirectionArgs(DIRECTION direction) { _direction = direction; } #endregion Constructors #region Properties public DIRECTION Direction { get { return _direction; } set { _direction = value; } } #endregion Properties } public class PixelArgs : EventArgs { #region Fields private int _pixels; #endregion Fields #region Constructors public PixelArgs(int pixels) { _pixels = pixels; } #endregion Constructors #region Properties public int Pixels { get { return _pixels; } set { _pixels = value; } } #endregion Properties } }
The Controller itself (MoveController.cs) is straightforward, listening for activity in the form (responding to UI events) and updating the Model:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MoveMVC { class MoveController { // the controller needs references for both the model and view private MoveModel _model; private frmMove _view; public MoveController(MoveModel model, frmMove view) { this._model = model; this._view = view; // the controller needs to listen to the view view.Directionchanged += view_Directionchanged; view.PixelsChanged += view_PixelsChanged; } private void view_Directionchanged(object sender, DirectionArgs e) { _model.Direction = e.Direction; } private void view_PixelsChanged(object sender, PixelArgs e) { _model.Pixels = e.Pixels; } } }
However, at the end of the day I've skipped a bit. From Wikipedia:
Quote
A controller can send commands to the model to update the model's state (e.g. editing a document). It can also send commands to its associated view to change the view's presentation of the model (e.g. by scrolling through a document).
I am not sending any commands to the View, the View updates itself by responding to the Model. Earlier though, it states "The third part, the controller, accepts input and converts it to commands for the model or view". The "or" is significant and means that I am still following, essentially, this pattern:

This hints at why MVC is typically more suited as a Web framework. In a website the View, the web page, is stateless. The Controller receives responses and will then send commands back to the View (or with the View).
Here is Program.cs that ties it all together:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; namespace MoveMVC { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); MoveModel model = new MoveModel(); frmMove view = new frmMove(model); MoveController controller = new MoveController(model, view); Application.Run(view); } } }
Was this a useful exercise? Have I learnt much about MVC? Maybe. I suppose I've managed to confirm that WinForms and MVC are not a good match, and I've had a little more practice with Custom Events. The application itself is reasonably interesting.
5 Comments On This Entry
Page 1 of 1

Recoil
11 June 2016 - 08:20 AM
I found it mildly humorous that you would use an Etch-A-Sketch-style approach, and still not implement a way to undo anything...
Throw on a button and name it "Flip-n-Shake" and have it reset the drawing
Throw on a button and name it "Flip-n-Shake" and have it reset the drawing


CasiOo
15 June 2016 - 10:18 AM
I prefer having thick models and thin views and controllers, which is why I don't like the view's paint method doing so much work
Ideally it should be simple
Ideally it should be simple
private void pnlDraw_Paint(object sender, PaintEventArgs e) { using (Pen pen = new Pen(model.Color, 3)) { foreach (LineSegment lineSegment in model.LineSegments) { e.Graphics.DrawLine(pen, lineSegment.P1, lineSegment.P2); } } }
Page 1 of 1
Trackbacks for this entry [ Trackback URL ]
← January 2021 →
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
My Blog Links
Recent Entries
Recent Comments
Search My Blog
1 user(s) viewing
1 Guests
0 member(s)
0 anonymous member(s)
0 member(s)
0 anonymous member(s)