Home

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")null), DefaultValue((string)null), AttributeProvider(typeof(IListSource))]
public Type ViewModel
{
get { return _viewModelType; }
set
{
SuspendBinding();
_viewModelType = value;
_viewModel = Activator.CreateInstance(_viewModelType);
Add(_viewModel);
MoveFirst();
ResumeBinding();
}
}

<p style="clear: both">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<Button, string> _buttonsWithCommands;
private readonly List<Button> _uninitializedButtons;
public CommandExtenderProvider()
{
_buttonsWithCommands = new Dictionary<Button, string>();
_uninitializedButtons = new List<Button>();
}
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.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: C# | .net
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

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:


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


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: Silverlight | .net
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

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:


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.


Currently rated 5.0 by 5 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: Balder | C# | Silverlight | 3D | .net
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

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.


Currently rated 4.7 by 3 people

  • Currently 4.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: Balder | Silverlight | C# | .net | 3D
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

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, rendering same city with the vertex colors applied:


Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: Balder | C# | Silverlight | Balder | .net | 3D | 3D | C# | .net | 3D
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Dynamically discovering assemblies and types on Windows Phone 7

The last few days I've been busy porting Balder to other platforms, amongst those; Windows Phone 7 Series. There are some nuances between the different platforms Balder will support, one of these for Windows Phone 7 was that the Load() method does not exist on an AssemblyPart from the deployment object.

Balder has a discover architecture were it discovers different types from the loaded assemblies, in the Silverlight version I could simply go and get all the AssemblyParts and get it as a resource stream and load them to get the actual Assembly instance. Since the Load() method didn't exist I had to to look elsewhere.
Fortunately, the Assembly class in the Windows Phone 7 version of .net has an overload for the Load() method that takes a string. According to the MSDN documentation it needs to take a fully qualified assemblyname, that turned out to be difficult to acquire. But, it turns out that its sufficient to pass it the short name - this can be derived from the DLL name found in the AssemblyParts property called Source.

From the TypeDiscoverer code in Balder :

private void CollectTypes()
{
if( null != Deployment.Current )
{
var parts = Deployment.Current.Parts;
foreach (var part in parts)
{
var assemblyName = part.Source.Replace(".dll", string.Empty);
var assembly = Assembly.Load(assemblyName);
var types = assembly.GetTypes();
_types.AddRange(types);
}
}
}


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: Silverlight | .net
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Balder 0.8.8.7 out


I promised a release last month, but I had to do surgery and got a bit sidetracked with feeling sorry for myself being in pain. Anyhow, all is good and finally got the release out the door.

Its been almost 6 months since the last release. So this better be good. :)

The following are as complete a list I could compile of whats new and improved, btw. you'll find the new release here. A new version of the SampleBrowser can be found here.


Performance improvements

  • Scanline renderers has improved quite a bit
  • Lighting calculations
  • NodesControl
  • Startup improvements
  • Content caching - loading the same content twice will only clone existing in cache
  • Color class - conversion plus operations are rewritten, introduced ColorAsFloats
  • Dependency properties now represented as Property<> with caching in it

Bugfixes

  • World matrix + general matrix fixups
  • NodesControl fixed, one can now nest these, plus a massive performance improvement during databinding using the ItemsSource.
  • Mouse events actually working - 0.8.8.6 had quite a few bugs with this, they are pixel perfect
  • PivotPoint fixed - working recursively
  • Completely rewritten rendering pipeline for stability, expandability and performance
  • Memory leaks in mouse event handling
  • Fixed Asset handling system - had a few bugs in it. Still has to revisit this.
  • Parsing of ASE files now use invariant culture all over the place. Had a couple of places it didn't.

New features

  • Manipulation events on objects, implemented using mouse events.
  • They contain more detailed information about the object being manipulated, such as material information. Also, the events are not like the mouse events, they actually contain delta information about the manipulation occuring.
  • Tooltips, there is a property on all objects called Tooltip, you can create tooltips as you please, as you'd do with the TooltipService in Silverlight for other objects. The TooltipService will not work, hence the specialized property for it.
  • DirectionalLight - basic directional lighting
  • ViewLight - lighting that is "attached" to the camera/view
  • ArbitraryHeightMap - heightmap that can have its corners placed arbitrarily
  • SmoothingGroups implemented
  • New ASE parser - supporting multiple objects, world matrix. Also a lot faster.
  • Ring - geometry type
  • Started implementation of a ChamferBox - very basic at this point
  • Passive Rendering, will draw in wireframe during interaction and flip to full rendering when interaction has stopped. When no interaction, it will not render, Balder goes idle.
  • Pausing - for pausing everything.
  • Grabbing a frame from the Display can now be done
  • Container for renderable nodes - has its own world coordinate system that can be manipulated, lights can't be placed in this container.
  • BubbledEvent system for events that needs to bubble up in the hierarchy
  • Messenger system for decoupling and stability
  • IGeometryDetailLevel - basically only used for Passive rendering at this point
  • Geometry artifacts such as Face, Vertex and so forth are now classes and has been generalized. Its now up to the implementing platform to create specialized versions of them.
  • Removed MEF and introduced a specialized TypeDiscoverer instead.

Development environment

  • Changed to Visual Studio 2010
  • Build server up and running with a more complete Balder.build file for continuous integration

Breaking changes

  • IGeometryContext does no longer hold the geometry data directly, introduced something called IGeometryDetailLevel. One can get the full detail level, which is the default (and only for now), by calling the method GetDetailLevel() on the IGeometryContext. In addition, any Geometry has a property called FullDetailLevel for convenience.
  • Mouse events has been specialized - they are named the same as one is used to from Silverlight, but the MouseEventArgs has been "duplicated" in Balder.Core.Input. The reason for this is that the Silverlight class for this is sealed and does not have a public constructor, and the mouse event handling needs to handled in a special manner in Balder. Also, the events are now bubbled as well through the geometry hierarchy.


Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: .net | Balder | Silverlight | 3D
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

4 hours optimizing later...

After just 4 hours of dedicated optimizations with the aid of the EQATEC profiler for Silverlight - I managed to speed up the rendering pipeline of Balder quite dramatically. The profiler has a great comparison feature - simple and straight to the point. It shows that the rendering pipeline for Balder got optimized at an average og 2.28 times. (RenderingManagerRender) : 

 

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: .net | Balder | Optimizing | Silverlight
Post Information: Permalink | Comments (2) | Post RSSRSS comment feed

First go with the EQATEC Silverlight Profiler

Its been a little more than 24 hours since I started playing with the EQATEC profiler and have identified bottlenecks in Balder that is close to impossible to discover without a profiler. One of things I really liked about the EQATEC profiler was its really easy to use user interface, its super-intuitive and I got up and running in seconds and could drill down straight to the bottlenecks just as fast. What impresses me with the profiler is the speed of it, I've worked with quite a few profilers in the past for the desktop - and they have always surprised me in a negative way with the amount of memory they use and not to mention the enourmous datafiles they generate. EQATECs just feels right, kudos to Richard Flamsholt and his team in Denmark for this product. You can download a free/personal license from here.

I thought I just give a quick guide into how its used.

The profiler is not integrated in Visual Studio and does not need to instrument your assemblies during compiletime, it works on the binary output of your compiled output. After starting the profiler, you need to point the profiler to your output directory in which the binaries for your application resides, typically the bin\debug directory in your Visual Studio project folder. Click the browse button as shown below:

The first time you run it and if you have 3rd party assemblies that are signed you'll be asked to include or exclude these - the default recommendation is to exclude these. There is a resigning option included as well, but I didn't need this so I just skipped them. After it completes gathering your assemblies and recognizes the Silverlight app by finding the testpage and everything, you can go ahead and click the Build button in the lower right corner. It will instrument your binaries and add hooks for all your methods.

Then you just simply click the "Run App" button and your application will start and performance samples will be collected. You now need to play around in your application for the profiler to get enough data to play with.

After stopping your application, you'll be presented with the sample output with details of when it was profiled, its size and total time it ran.

Double click the session you want to view (if you have more than one) and you can start to drill into method calls with the summary sitting on top and a more visualized version of the method with drilldown capabilities at the bottom.

 

 Simple drill down and you'll see the amount of time spent in every method you drill down into:

 

And further: 

 

 

This is by far, in my oppinion, the best tool that has been released since Silverlight was released. It completes the developer experience and finally we can really show the full potential of Silverlight and the power the CLR actually gives us.

Thanks again to EQATEC for the donation to Balder - we will certainly make use of the license and optimize as close as we can to clock-cycles. :) 

 

 

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: .net | C# | Silverlight
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

EQATEC donates profiler license to the Balder project

One of the things that has been missing in the Silverlight development story is a proper profiler that is simple to use and does not require you to hack the registry or do complex command line hacks to get it working. The Danish company called EQATEC has been making profilers for a while for the compact framework, but now they have implemented a Silverlight version that I must say I'm really impressed with. EQATEC has donated a professional license to the Balder project and I have already started using it, and my findings are very interesting. I've found more bottlenecks the last 24 hours than I have during the entire course of the project without a profiler. So I'm expecting Balder to get even more speedier in the near future.

If you want to have a look at how the profiler works, Channel 9 has a great video on it here.

 

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Categories: .net | Balder | Silverlight
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed