Monthly Archives: August 2010

MVVM in Windows Forms

Not everyone has the luxury of jumping on new technologies, it can be historical reasons, don’t have time to learn, deployment reasons – or flat out you simply don’t like anything new 🙂 .

Anyways, I was doing a codereview on a project last week, which was written in Windows Forms. The developers had some pain, Windows Forms being one of them – and a constant feeling that they were barking up the wrong tree when using Windows Forms. Its been a couple of years since my last Windows Forms project, and I must admit I remember the feeling of constantly banging my head against the wall without getting the code quality I wanted when doing UI.

One of the things I saw that the project could benefit from, was unit testing – to help them fight problems with regression, and get a constant quality indicator on the project. Having done a lot of WPF and Silverlight over the years and leaving Windows Forms behind, it just felt bad not having a proper UI pattern and I’m loving MVVM and the way it works – the simplicity of it. So I decided to do a spike, how could one implement MVVM in Windows Forms and get all the niceness of testability and full separation.

Keep in mind, I haven’t done a full implementation of a framework or anything, just the basics to get started. The one thing I wanted was to be able for the view (Form or UserControl) to be able to observe changes from a ViewModel. Windows Forms does in fact recognize the interfaces we are so familiar with from WPF and Silverlight (INotifyPropertyChanged and INotifyCollectionChanged), so it was just a matter of figuring out how to get the ViewModel easily accessible in the designer in Windows Forms. I came up with something called ViewModelBindingSource. A very simple component that can be dragged onto the designer surface:

 public class ViewModelBindingSource : BindingSource
 {
 public ViewModelBindingSource(IContainer container)
 : base(container)
 {
 }

private object _viewModel;
private Type _viewModelType;

[Category("MVVM"), DefaultValue((string)null), AttributeProvider(typeof(IListSource))]
public Type ViewModel
{
 get { return _viewModelType; }
 set
 {
 SuspendBinding();
 _viewModelType = value;
 _viewModel = Activator.CreateInstance(_viewModelType);
 Add(_viewModel);
 MoveFirst();
 ResumeBinding();
 }
}

public override object this[int index]
{
 get
 {
 return _viewModel;
 }
 set
 {
 base[index] = value;
 }
}
}

The next thing I wanted to accomplish, was the ability to hook up Commands for Buttons and such, supporting the ICommand interface found in WPF. I came up with a CommandExtenderProvider:

[ProvideProperty("Command", typeof(Button))]
public class CommandExtenderProvider : Component, IExtenderProvider
{
private readonly Dictionary _buttonsWithCommands;
private readonly List _uninitializedButtons;
public CommandExtenderProvider()
{
_buttonsWithCommands = new Dictionary();
_uninitializedButtons = new List();
}
public bool CanExtend(object extendee)
{
return extendee is Button;
}
private BindingSource _viewModelSource;
[Category("MVVM")]
public BindingSource ViewModelSource
{
get { return _viewModelSource; }
set
{
_viewModelSource = value;
InitializeUninitializedButtons();
}
}
private void InitializeUninitializedButtons()
{
foreach( var button in _uninitializedButtons )
{
var commandName = _buttonsWithCommands[button];
SetCommand(button, commandName);
}
_uninitializedButtons.Clear();
}
[Editor(typeof(CommandEditor), typeof(UITypeEditor))]
public string GetCommand(Button button)
{
if (!_buttonsWithCommands.ContainsKey(button))
{
return null;
}
var command = _buttonsWithCommands[button];
return command;
}
public void SetCommand(Button button, string commandName)
{
_buttonsWithCommands[button] = commandName;
if (null == ViewModelSource ||
null == ViewModelSource.Current)
{
_uninitializedButtons.Add(button);
return;
}
var property = ViewModelSource.Current.GetType().GetProperty(commandName);
if (null != property)
{
var command = property.GetValue(ViewModelSource.Current, null) as ICommand;
if (null != command)
{
button.Enabled = command.CanExecute(null);
command.CanExecuteChanged += (s, e) => button.Enabled = command.CanExecute(null);
button.Click += (s, e) =>
{
if (command.CanExecute(null))
{
command.Execute(null);
}
};
}
}
}
}

In the designer, you’ll get two new components you can then drag onto your Windows Forms design surface:

Simply start by dragging in the ViewModelBindingSource and look in the Properties of it. Under the MVVM category, you’ll find the property ViewModel – a dropdown were you can select the ViewModel.

If you then drag onto the design surface a TextBox that you want to have databound to a property in the ViewModel and look in the Properties window for the TextBox. There you’ll find your ViewModelBindingSource, expanding it will show all the properties available in the ViewModel:

For commands, we need the CommandExtenderProvider. That too can be dragged onto the surface. It has a property for selecting the ViewModelBindingSource:

Now you can add a button to the surface and go to properties, you’ll find a property called Command. There you can select the command you want to be executed during Click.

There is still a bunch of things to be desired in order for this to fully support all aspects of MVVM. But it proves its possible to think in this way, even though your technology is not necessarily state of the art.

Hope anyone finds this interesting.

The source code can be found here.

 

Silverlight 4 “bug” in release mode in Safari / Chrome on OSX

Yesterday I released a new version of the Sample Browser for Balder – but some of the samples just didn’t work on Safari or Chrome on OSX, but worked in FireFox and Opera and all browsers on Windows (including Safari and Chrome).

Turns out there is an accuracy issue with using the System.Math.Log() method. It produces different results on OSX in Safari and Chrome when the code is compiled in release mode. When compiled in debug, it produces the correct result.

The method in Balder in question:

IsPowerOfTwoMethod
By casting the Log() result to float you get past the problem.

 

Balder gets donation from NDepend

Today the Balder project was lucky enough to get a donation from NDepend. Yet another tool that will be used to improve the code quality of Balder.

Thanks a lot to the people at NDepend for the donation.

PoweredByNDepend

Balder – 0.8.8.9 very close

Its been a hectic week – but finally most of the pieces are falling into place for the next release. There is only a couple of minor features I want to add before calling it a release. Meanwhile, I’ve updated the samplebrowser and released it as a sneak peak. I’ve replaced the Material Picker sample with a Material Editor were you can actually edit all the properties of the materials and also the lights added to the scene.

Here’s a screenshot of how it can look like when configured with all features enabled:

Balder_0_8_8_9_Preview
If you’re having trouble with the SampleBrowser on Safari or Chrome on the Mac, this is something I’m investigating. I’ve tested it with all browsers on Windows and Mac, but these two had some issues when textures were involved on my machine. Will look into it more carefully before releasing the next version. The odd thing though, it worked with the samplebrowser compiled as debug.

 

Clarifying Balders feature set

Hopefully I’m not forgetting anything (probably am) – but below is a clarification of what features Balder has.

Coordinate system

Balder uses a left-handed coordinate system – currently there is no way to change this, but there are plans for opening up for any coordinate system.

Nodes

  • Interaction – one can enable interaction on any nodes and they can be interactively manipulated during runtime
  • Position coordinates
  • Rotation coordinates, in angles for each axis
  • Scale coordinates
  • Hierarchical rendering of nodes

Scene

  • A Scene contains objects, any Balder object implementing the interface INode; Lights, geometries, sprites

Viewport

  • Defines a clipping area on the screen, coordinate relative to its container

View

  • Camera
    • Position
    • Target
    • Field Of View

Sprites

  • Flat image based objects that exist in 3D space – they are rendered along with other 3D content and positioned and scaled correct according to their 3D space position.

Geometries

  • Box – simple box with dimensions
  • Cylinder – top and bottom dimensions can be specified independently
  • Ring – inner and outer circle dimensions can be specified independently
  • ChamferBox – simple box with a chamfer segment
  • HeightMap – a plane that consist of a set of vertices (points) that can be manipulated by its height
  • ArbitraryHeightMap – a derivative of HeightMap, but its plane is arbitrary
  • Mesh – a complex mesh which can be loaded from a file

Other objects

  • Container – not renderable itself, but can contain any Node in it – hierarchically

Assets

All data loaded from disk or elsewhere is known as Assets. There are Asset loaders for file types, you can even create your own Asset loaders quite easily. For Silverlight, there exist today a limitation which only allows loading of assets found in the XAP file – meaning that any asset added to the project must be a Resource in the XAP file. This is being worked on and will in a future release be more flexible.

  • ASE file – supports 90% of the Autodesk Ascii Scene Export format for 3D objects
  • Experimental support for Demoniak3D format
  • JPEG, PNG for Images

Assets are cached in a content manager, which means that if you load the same asset twice, it will clone the first one.

Lighting

  • OmniLight – non directional light, emits light in all directions
  • DirectionalLight – emits light in a specific direction, without any starting point or ending point
  • ViewLight – view dependent light that will always emit from the view and into the Scene

Rendering

  • Z buffered rendering
  • Flat shaded – single color faces
  • Gouraud shaded – color can be specific on each corner of a triangle
  • TextureMapping – perspective corrected, gouraud shaded.
  • Lines
  • Passive rendering mode – renders only when there are changes
  • Bilinear filtering
  • Face-culling – do not render faces/triangles facing away, can be controlled via Materials
  • Frustum-culling – objects not inside view will not be rendered – increases performance a lot

Materials

  • Diffuse color
  • Diffuse map
  • Ambient color
  • Specular color
  • Reflection map
  • Opacity for the different maps can be specified

Imaging

  • Supports PNG and JPEG
  • For textures, you must use images with dimensions being power of 2 (2,4,8,16,32,64,128,256,512,1024 and so on)

Input

  • For Silverlight; mouse events are supported (MouseEnter, MouseLeave, MouseMove, MouseLeftButtonDown, MouseLeftButtonUp)
  • Manipulation events for all Nodes to make it easier to interact with objects

Debug visualization

  • Bounding information
  • Show vertices

Statistics

  • Rendering timing
  • Amount of faces being rendered
  • Amount of objects being rendered

Execution

  • Actor system with statemachine for actors in the scene
  • Game – base class for any game

Silverlight / Windows Phone 7 specifics

  • Full XAML support – you can declaratively do everything that Balder can do in XAML
  • Specific CoordinateAnimation that can be used in storyboards for animating properties on Nodes
  • NodesControl / NodesStack – databindable controls that be bound to any source with NodeTemplate to specify its representation on screen. Similar to ItemsControl found in Silverlight.


Balder – vertex colors

I’ve been working lately on a demo that is for gaming a lot more realistic than the spinning teapot or box that one sees in the sample browser for Balder. A friend of mine Peter Rosenlund, an excellent graphics artist, gave me a 3D model of a city that I can use for that (thanks a lot!!).

In the 3D model, he had applied static lighting manually in 3DSMAX by painting the Vertex colors – a feature I had not implemented in Balder. After a few hours yesterday and this morning, I got it all up and running and I must say it looks kinda nice.

Balder, rendering city without vertex colors applied:

Balder_Without_VertexColors

Balder, rendering same city with the vertex colors applied:

Balder_With_VertexColors

 

Balder 0.8.8.9 coming

Things are moving fast these days with Balder and I am closing in on the next release already. Keeping up with my target pace.

At this point these are the things already done or is in progress for the next release:

  • New Skybox, utilizing built in Silverlight perspective projection, which can be hardware accelerated
  • Perspective correct texture mapping – previous version had only affine texture mapping
  • Bilinear filtering for texture mapping
  • Performance boost for the entire rendering pipeline
  • Better, faster and more accurate lighting
  • Subpixel rendering
  • Vertex-shader like functionality for SIlverlight
  • Multiple “Games” in one page
  • Multiple textures during rendering
  • Semitransparent / alpha-channel support for texture mapping
  • Introducing a control for no-game centric applications; Content3D
  • A lot of bugfixing


Balder Tutorial : Creating a Mesh programatically

Balder has been throughout the development had the focus of extensibility, everything should be extensible. In Balder one has a set of object types, both 2D and 3D. For loading 3D models one has something called Mesh. This will automatically pick the right AssetLoader for the filetype based upon the file extension and parse the file and generate a 3D Mesh for Balder to render. Some of the other objects, which are considered primitives such as Box, Cylinder and Ring – are generated on the fly.

This tutorial will show how one can create an object that generates the Mesh data on the fly.

The process is quite simple. A Geometry holds something called a GeometryContext which is represented by the interface IGeometryContext, within the GeometryContext one has the ability to create something called a GeometryDetailLevel. The GeometryDetailLevel is the object being rendered depending on the detail level Balder wants to render it in. For the most part, Balder renders using the Full detail level. For Geometry there is a convenience property for the full detail level called FullDetailLevel in which one can use to generate its geometry.

To get started, the first thing we need to do is to create our object and inherit from Geometry.

using Balder.Objects.Geometries;

public class MyBox : Geometry
{
}

Now that we have our class and its inheriting Geometry, for Silverlight this means we can add it to our Silverlight page. Add a namespace for Balder Execution and View in your page Xaml file (xmlns:Execution=”clr-namespace:Balder.Execution;assembly=Balder” and xmlns:View=”clr-namespace:Balder.View;assembly=Balder”). Then you need to add an Xml namespace to the namespace of your MyBox class (something like this : xmlns:Custom=”clr-namespace:Balder.Silverlight.SampleBrowser.Samples.Geometries.Custom”).

Now you can add the Xml for Balder and MyBox inside the container you want it to be in the Xaml :

Balder_Tutorial_Box_Xaml
It will not show anything yet, as we don’t have any geometry data in the object yet.

The Geometry has a method on can override for preparing the object, its called Prepare().

public override void Prepare(Viewport viewport)
{
}

In the prepare method we can now start generating the 3D mesh data. We’ll start by adding the vertices; 3D points that our geometry data (lines or faces) will be connected to. We create method called GenerateVertices() that we’ll call from the Prepare() method. Each point in 3D is represented by something called a Vertex. A box consists of 8 vertices.

private void GenerateVertices()
{
   var dimensionAsVector = new Vector(5f,5f,5f);
   var halfDimension = dimensionAsVector / 2f;
   var frontUpperLeft = new Vertex(-halfDimension.X, halfDimension.Y, -halfDimension.Z);
   var frontUpperRight = new Vertex(halfDimension.X, halfDimension.Y, -halfDimension.Z);
   var frontLowerLeft = new Vertex(-halfDimension.X, -halfDimension.Y, -halfDimension.Z);
   var frontLowerRight = new Vertex(halfDimension.X, -halfDimension.Y, -halfDimension.Z);
   var backUpperLeft = new Vertex(-halfDimension.X, halfDimension.Y, halfDimension.Z);
   var backUpperRight = new Vertex(halfDimension.X, halfDimension.Y, halfDimension.Z);
   var backLowerLeft = new Vertex(-halfDimension.X, -halfDimension.Y, halfDimension.Z);
   var backLowerRight = new Vertex(halfDimension.X, -halfDimension.Y, halfDimension.Z);
   FullDetailLevel.AllocateVertices(8);
   FullDetailLevel.SetVertex(0, frontUpperLeft);
   FullDetailLevel.SetVertex(1, frontUpperRight);
   FullDetailLevel.SetVertex(2, frontLowerLeft);
   FullDetailLevel.SetVertex(3, frontLowerRight);
   FullDetailLevel.SetVertex(4, backUpperLeft);
   FullDetailLevel.SetVertex(5, backUpperRight);
   FullDetailLevel.SetVertex(6, backLowerLeft);
   FullDetailLevel.SetVertex(7, backLowerRight);
}

Notice the first line in the method, it just defines the size of the box in 3D space.
The next thing we’ll do is to add some lines to it. We’ll add a method called GenerateLines() that we’ll call from the Prepare() method.

private void GenerateLines()
{
   FullDetailLevel.AllocateLines(12);
   FullDetailLevel.SetLine(0, new Line(0, 1));
   FullDetailLevel.SetLine(1, new Line(2, 3));
   FullDetailLevel.SetLine(2, new Line(0, 2));
   FullDetailLevel.SetLine(3, new Line(1, 3));
   FullDetailLevel.SetLine(4, new Line(4, 5));
   FullDetailLevel.SetLine(5, new Line(6, 7));
   FullDetailLevel.SetLine(6, new Line(4, 6));
   FullDetailLevel.SetLine(7, new Line(5, 7));
   FullDetailLevel.SetLine(8, new Line(0, 4));
   FullDetailLevel.SetLine(9, new Line(1, 5));
   FullDetailLevel.SetLine(10, new Line(2, 6));
   FullDetailLevel.SetLine(11, new Line(3, 7));
}

The way this work is that every line is referring to the vertex index generated by the GenerateVertices(). Take the first line, it has the parameters 0 and 1, that means it is telling Balder to use vertex 0 and 1 to generate the line. When Balder has rotated all vertices according to the objects world and the view and then projected everything onto the 2D screen, it will use the result of the vertices to draw the line.

Running everything now should give you a result like the below.

Balder_Tutorial_Box_LinesIf you want to use solids, making the object completely solid – we have to use Faces instead of Lines. A face is a triangle, and similar to a Line it references the vertices that represents the triangle. Only difference is that the order in which one connects the vertices are important. The reason for this is that Balder uses something known as backface culling, which means that triangles pointing away from the viewer is not drawn. Its important that one generates the faces with a counter clockwise connections between the vertices, as clockwise triangles will not show.

Let’s create a GenerateFaces() method and swap it for the GenerateLines() method.

private void GenerateFaces()
{
   FullDetailLevel.AllocateFaces(12);
   FullDetailLevel.SetFace(0, new Face(2,1,0));
   FullDetailLevel.SetFace(1, new Face(1,2,3));
   FullDetailLevel.SetFace(2, new Face(4,5,6));
   FullDetailLevel.SetFace(3, new Face(7,6,5));
   FullDetailLevel.SetFace(4, new Face(0,4,2));
   FullDetailLevel.SetFace(5, new Face(6,2,4));
   FullDetailLevel.SetFace(6, new Face(3,5,1));
   FullDetailLevel.SetFace(7, new Face(5,3,7));
   FullDetailLevel.SetFace(8, new Face(0,1,4));
   FullDetailLevel.SetFace(9, new Face(5,4,1));
   FullDetailLevel.SetFace(10, new Face(6,3,2));
   FullDetailLevel.SetFace(11, new Face(3,6,7));
}

Running this should yield the following result:

Balder_Tutorial_Box_FacesAs you’ll notice the box is all black. There are two reasons for this; we haven’t added a light to the scene yet and most importantly, in order for Balder to be able to calculate lights it needs to have something called a normal ve
ctor on the vertices and faces. Luckily, there is something called a GeometryHelper that can be used to generate these. You simple add the following two lines of code in the prepare method after you’ve generated the vertices and faces :

GeometryHelper.CalculateFaceNormals(FullDetailLevel);
GeometryHelper.CalculateVertexNormals(FullDetailLevel);

Then in the Xaml of the page, we’ll need to import the Lighting namespace from Balder (xmlns:Lighting=”clr-namespace:Balder.Lighting;assembly=Balder”) and then we can add a light to the scene:

Balder_Tutorial_Box_FinalXaml
The box has a property called Color, which it inherits – so setting it to Blue (Color=”Blue”) will give the box a specific color, instead of a random one, which is the default behavior.

You should be seeing something like this:

Balder_Tutoria._Box_Faces_LitSummary
Its fairly easy to generate meshes in Balder, but there are more possibilities that this tutorial does not cover, which will be covered by other tutorials. One thing to mention, if you need to regenerate the mesh data you can call the InvalidatePrepare() method, and your prepare method will be called again. Also worth mentioning, if you want to have a texture on the object, you need to generate TextureCoordinates, which is also done through the GeometryDetailLevel. Each face has something called DiffuseA,B,C which in a similar way as with vertices connects the different corners of the triangle to a specific texture coordinate. A texture coordinate is a relative coordinate ranging from 0 to 1 within a texture.