MVVM on a Windows Store App

I blog for a couple of reasons:

  1. Professionalism – to think and write about a topic at least once a week
  2. Marketability – when I talk to people about jobs, I can point them to my blog to see how I code (and think about coding).  To me, it is much more effective and insightful than a exam or esoteric interview questions
  3. To keep track of stuff for me -  I write stuff down so I don’t have to remember something.

That last point came back to me today.  I wanted to take a break from F# so I looked at MVVM in a Windows RT application.  I went to refresh my brain on MVVM so I hit up Bing and Google.  All of the recent articles that I ran across talked about MVVM –as an after thought.  They were all pimping MVVM helpers like relay command, frameworks like MVVMLight, and other non-core MVVM concepts.  All important to be sure, but non related to MVVM.

I then hit up my own blog and sure enough – I blogged about MVVM 2 years ago when I did a Windows Phone 7 app and I could see just the MVVM in action.  So then I fired up a basic Windows RT application and added 3 folders to it: Models, Views, and ViewModels.

I then added a Model like so:

  1. public class Person
  2. {
  3.     public Int32 Id { get; set; }
  4.     public String FirstName { get; set; }
  5.     public String LastName { get; set; }
  6. }

 

I then added a View Model

  1. public class PersonViewModel: INotifyPropertyChanged
  2. {
  3.     private Person _person = null;
  4.     public event PropertyChangedEventHandler PropertyChanged;
  5.  
  6.     public PersonViewModel(Person person)
  7.     {
  8.         _person = person;
  9.     }
  10.  
  11.     public Int32 Id
  12.     {
  13.         get { return _person.Id; }
  14.         set
  15.         {
  16.             PropertyChanged(this, new PropertyChangedEventArgs("Id"));
  17.             _person.Id = value;
  18.         }
  19.     }
  20.     public String FirstName
  21.     {
  22.         get { return _person.FirstName; }
  23.         set
  24.         {
  25.             PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
  26.             _person.FirstName = value;
  27.         }
  28.     }
  29.     public String LastName
  30.     {
  31.         get { return _person.LastName; }
  32.         set
  33.         {
  34.             PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
  35.             _person.LastName = value;
  36.         }
  37.     }
  38.  
  39. }

I then added a View like so:

image

and then in the code behind of the View:

  1. public PersonView(PersonViewModel viewModel)
  2. {
  3.     InitializeComponent();
  4.     this.mainGrid.DataContext = viewModel;
  5. }

 

Then in the main page, the ViewModel is injected into the View:

  1. public partial class MainWindow : Window
  2. {
  3.     public MainWindow()
  4.     {
  5.         InitializeComponent();
  6.         Person person = new Person(){Id=0,FirstName="Test",LastName="Person"};
  7.         PersonViewModel viewModel = new PersonViewModel(person);
  8.         PersonView view = new PersonView(viewModel);
  9.         view.Show();
  10.     }
  11. }

 

And we have data binding:

image

 

Now I know this is not a complete project and that many patterns are helpful (esp. the relay command one), but this is the core of making MVVM work the MSFT way: data binding and IPropertyNotifyChanged.

Signature Capture–WPF

I kept working on the SignaturePanel this last week.  I decided to make the same thing in WPF.   I fired up a new WPF Application and hooked up a reference to my existing SignatureCapture.Support class.  I then added a Rectangle (changed it later to a Canvas) to the MainWindow and three buttons.

image

 

  I then started copy/pasting the code from my WinForms application to the window’s code-behind.

image

 

Yikes!  It looks like the Point and Pen class has been changed from the Windows Implementation to the WPF implementation.  My first thought was to throw away the WPF version by using the trusty Remove and Sort Using context menu:

image

I then fully-qualified my classes:

image

And then I realized that I was completely wrong and if I spent more than 1 second thinking about the problem, the whole remove usings/fully qualifying the classes won’t work.

image

BECAUSE THEY ARE TWO DIFFERENT TYPES.

Given the choice of adding new functions to my Support class or converted, I chose the later.  I added a Convert Line method like so:

private System.Drawing.Point ConvertPoint(System.Windows.Point point)
{
    System.Drawing.Point returnPoint = new System.Drawing.Point();
    returnPoint.X = (Int32)point.X;
    returnPoint.Y = (Int32)point.Y;
    return returnPoint;
}

and then I wired up the UI:

private void signatureCanvas_MouseMove(object sender, MouseEventArgs e)
{
    if (IsCapturing)
    {
        if (startPoint.IsEmpty && endPoint.IsEmpty)
        {
            endPoint = ConvertPoint(e.GetPosition(this.signatureCanvas));
        }
        else
        {
            startPoint = endPoint;
            endPoint = ConvertPoint(e.GetPosition(this.signatureCanvas));
            Line line = new Line(startPoint, endPoint);
            glyph.Lines.Add(line);
            DrawLine(line);
        }
    }
}

Note that the e.GetPosition() method replaces the WinForm’s e.Location property.  The key thing is that the second argument (location) needs to be your capturing panel to make it equivalent to the WinForm implementation…

private void DrawLine(Line line)
{
    System.Windows.Shapes.Line currentLine = new System.Windows.Shapes.Line();
    currentLine.Stroke = System.Windows.Media.Brushes.Black;
    currentLine.X1 = line.StartPoint.X;
    currentLine.Y1 = line.StartPoint.Y;
    currentLine.X2 = line.EndPoint.X;
    currentLine.Y2 = line.EndPoint.Y;
    currentLine.StrokeThickness = 1;
    signatureCanvas.Children.Add(currentLine);

}
private void signatureCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
    IsCapturing = false;
    signature.Glyphs.Add(glyph);
    startPoint = new System.Drawing.Point();
    endPoint = new System.Drawing.Point();

}
private void signatureCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
    IsCapturing = true;
    glyph = new Glyph();
}

All that was left was to handle the button clicks:

private void clearButton_Click(object sender, RoutedEventArgs e)
{
    signatureCanvas.Children.Clear();
}
private void exportButton_Click(object sender, RoutedEventArgs e)
{
    signatureFactory.SerializeSignatureToDisk(signature, fileName);
}
private void DrawSignature()
{
    foreach (Glyph glyph in signature.Glyphs)
    {
        foreach (Line line in glyph.Lines)
        {
            DrawLine(line);
        }
    }
}
private void importButton_Click(object sender, RoutedEventArgs e)
{
    signature = signatureFactory.DeserializeSignatureFromDisk(fileName);
    signatureCanvas.Children.Clear();
    DrawSignature();
}

And now I have a WPF implementation of the signature capture panel:

image

With the cool thing is that I can import and signature from a WinForm into my WPF app, or export my WPF generated signature into the WinForm app or that Web Form app,

MVVM and Game Design

I fired up an old laptop last week and I found Panzer General installed on it. Bring back fond memories of wasted hours, I thought how cool it would be to port this game to Windows Phone. Ambitious? Absolutely. Realistic? Not even close. Undaunted, I thought a bit about how a game board are might be modeled. You can see from this screen shot that the game board is a series of Hexes – like so:

image

Dialing up the MVVM pattern, I thought the basic unit of code is the Hex. I looked at this article about building a MVVM Pattern for a Sudoku found in MSDN here but quickly threw it away. I think that Miller’s implementation is a bit mangled and all of the code in the View smells to me like an incorrect design. I realized that I need a HexModel, and HexView, and a HexViewModel. To start, I put all of the properties that I care about into the Model like so:

public class HexModel { public double Width { get; set; } public double Height { get; set; } public double CanvasTop { get; set; } public double CanvasLeft { get; set; } public SolidColorBrush Fill { get; set; } public int XCoordinate { get; set; } public int YCoordinate { get; set; } } }

I then put all of the implementation of INotifiedPropertyChanged in the ViewModel. It makes alot of sense to me that the View is a passive implementation of the underlying data structures (EF classes, POCOs, TakeYourPickDataStructure) and the ViewModel where all of the heavy lifting occurs (For example, your LINQ for the CRUD if this was working with a permanent data store) and that is where you notify the consuming layers that properties changed. Following this mental model, I first implemented an abstract class to handle INotifiedPropertyChanged:

public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(String propertyName) { if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("Argument 'propertyName' cannot be null or empty."); if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }

I then implemented the HexViewModel like this (partial snip):

public class HexViewModel: ViewModelBase { HexModel _hexModel; public HexViewModel() { _hexModel = new HexModel(); Width = 100; Height = 100; CanvasTop = 0.0; CanvasLeft = 0.0; Fill = new SolidColorBrush(Colors.Gray); XCoordinate = 0; YCoordinate = 0; } public double Width { get { return _hexModel.Width; } set { _hexModel.Width = value; NotifyPropertyChanged("Width"); } }

With this view model done, I then was onto the View itself. A hex shape is a bit more tricky than a square for a game board – heck you can use a grid if you just want to build a game board of squares. I found a pretty good implementation of a WPF hex on Stack Overflow here. Since the Panzer General Hex is flat on the top (versus the flat sides in the Stack Overflow question), I reversed the path coordinates and threw it into a User Control:

<UserControl.DataContext> <vm:HexViewModel /> </UserControl.DataContext> <Canvas x:Name="CanvasRoot" Height="100" Width="100"> <Path x:Name="PathRoot" IsHitTestVisible="True" Width="100" Height="100" d:LayoutOverrides="None" d:LastTangent="0,0" Stroke="Black" Fill="{Binding Path=Fill}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" Stretch="Fill" Data="M0,8.660254 L5,17.320508 15,17.320508 20,8.660254 15,0 5,0 0,8.660254 z" Canvas.Left="{Binding Path=CanvasLeft}" Canvas.Top="{Binding Path=CanvasTop}"> </Path> <TextBox x:Name="LocationTextBox" IsHitTestVisible="False" Canvas.Left="21" Canvas.Top="22" FontSize="12" Background="{Binding Path=Fill}" HorizontalAlignment="Center" BorderThickness="0" Text="{Binding Path=Coordinates}"> </TextBox> </Canvas> </UserControl>

Sure enough, I rendered great in VS2010 at design time because of the magic of data binding and INotifyPropertyChanged:

image

I then though about how I would get a series of hexes as the game board using the same MVVM pattern. I set up a BoardModel like so:

public class BoardModel { public int NumberOfRows { get; set; } public int NumberOfColumns { get; set; } public int ZoomFactor { get; set; } }

I then implemented the model and added a collection to the ViewModel to have a collection of HexModels (screen shot, not code):

image

I then put together a BoardView that goes through the collection and renders out the HexViews in the constructor:

private void BindCanvas() { MainCanvas.Height = (_boardViewModel.NumberOfRows * 100); MainCanvas.Width = (_boardViewModel.NumberOfColumns * 100); foreach (HexViewModel hexViewModel in _boardViewModel.HexViewModels) { this.MainCanvas.Children.Add(CreateHexView(hexViewModel)); } }

Because this is not data bound, I don’t see the HexViews at design time. However, when I slap the BoardView into the MainPage at design time, I get a nice playboard:

image

Also, I used the Windows Phone Control Toolkit’s Gesture Listen class and I can intercept touches and active and deactivates hexes on a user’s touch (I love the SOC of the IPropertyNotifiedChanged):

private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e) { Path path = (Path)e.OriginalSource; Canvas canvas = (Canvas)path.Parent; HexView hexView = (HexView)canvas.Parent; if (currentHexViewModel != null) currentHexViewModel.Fill = new SolidColorBrush(Colors.Gray); currentHexViewModel = (HexViewModel)hexView.DataContext; currentHexViewModel.Fill = new SolidColorBrush(Colors.Red); e.Handled = true; }

Notice how I am changing the property of the ViewModel – not of the UI control.

I am not sure how to make the BoardVewModel can be data bound like the HexViewModel. I was thinking that the Board should expose a collection of HexViews (versus ViewModel) but that does seem right. Also, I am not sure how to code up design time data binding to that collection – I would think I would need a container control. After futzing around with Rob Seder and not getting anywhere, I gave up and went with the code-behind implementation. I am sure I will revisit, but I am jazzed just to get the board working. Up next, I want to see if I can put in a picture into the Hex.