Broloco

An Alternative to CodeSmith

I'm a big fan of code-generation for automating boiler-plate code. I used to use CodeSmith for this, but the freeware version is no longer maintained. T4 is my new favourite tool for code generation, and there's a wealth of information available on using it on Oleg Sych's site.

Passing Command-Line Parameters to TextTransform

The tools come with a command-line host for T4 (TextTransform.exe), but there's no obvious way to pass parameters to the templates.

You can pass parameters using the command-line option -a:

TextTransform.exe
    -a mappingFile!c:\myPath\MyClass.hbm.xml
    -out c:\myGeneratedFiles\MyClass_Generated.cs
    c:\myTemplates\GenerateNHibernateProperties.tt
        

In order to retrieve the parameter from inside the template, you can use a little reflection:

private string GetCommandLineProperty(string propertyName)
{
    PropertyInfo parametersProperty =
        Host
            .GetType()
            .GetProperty("Parameters",
                BindingFlags.NonPublic | BindingFlags.Instance);

    StringCollection parameters =
        (StringCollection)parametersProperty
            .GetValue(Host, null);

    foreach (string parameter in parameters)
    {
        string[] values = parameter.Split('!');
        int length = values.Length;
        if (values[length - 2] == propertyName)
            return values[length - 1];
    }
    return null;
}
        

Replacing NHibernate Magic Strings

Now I can loop through my .hbm.xml files in my build, generating the 'magic strings' for the properties of each class (example template and project is linked below).

This turns code like:

IList<Media> mediaWithNameAndType =
 DomainRegistry.Repository
  .CreateQuery<Media>()
  .Add(Expression.Eq("OwningLibrary", this))
  .Add(Expression.Eq("Type", media.Type))
  .Add(Expression.Eq("Name", media.Name))
  .List<Media>();
        

Into code like:

IList<Media> mediaWithNameAndType =
 DomainRegistry.Repository
  .CreateQuery<Media>()
  .Add(Expression.Eq(Media.Properties.OwningLibrary, this))
  .Add(Expression.Eq(Media.Properties.Type, media.Type))
  .Add(Expression.Eq(Media.Properties.Name, media.Name))
  .List<Media>();
        

Some links:

Submit this story to DotNetKicks Shout it

Introduction

In a previous post I demonstrated testing a Compact Framework application using Passive View. I also mentioned that I would use the same technique to test the presentation layer on other platforms. So to put that to the test I migrated that simple application to Silverlight.

Testing Platform

There are many more testing options for Silverlight than there are for the Compact Framework. If you're using the IDE, you might consider reading this for some of your options.

I chose to test my POCO controller classes inside the regular .Net framework using NUnit.

Instead of retargeting, I cross-compiled the controller class to the 'real' .Net framework. The view controls in Silverlight are a subset of the complete WPF controls, so they can be substituted in the test environment to simulate the runtime controls.

The only slight hindrance I came across was that when you attempt to use the WPF controls in NUnit you get the exception "InvalidOperationException : The calling thread must be STA". The controls insist on running on an STA thread, which requires a .config file for the test assembly containing the following:

<NUnit>
    <TestRunner>
        <add key="ApartmentState" value="STA" />
    </TestRunner>
</NUnit>
        

Passive View

As previously, the view is completely dumb. It contains no logic, just the controls that will be manipulated by the controller. The XAML is kept simple with mostly positioning logic.

<StackPanel x:Name="LayoutRoot"
    Background="White">

<TextBlock>
    Demonstration of Passive View to test
        Silverlight client
</TextBlock>

<Button x:Name="ShowMessage"
    Content="Show Message"
    Width="150"  Margin="20"/>
...
        

I exposed the controls as public fields on the View class. These were populated in the 'Loaded' event (note, this only fires in the Silverlight runtime - not the tests), then the controller was wired up:

public class MainView : UserControl
{

  public Button ShowMessage;
  public TextBlock Message;
  ...

  public MainView()
  {
    Loaded +=
      new RoutedEventHandler(UserControl_Loaded);
  }

  private void UserControl_Loaded(object sender,
      RoutedEventArgs e)
  {
    ShowMessage = (Button)FindName("ShowMessage");
    Message = (TextBlock)FindName("Message");
    ...
    new MainController(this);
    ...
        

During the tests a testable view class simply populates the fields with 'empty' controls allowing the controller logic to be tested, without requiring a full presentation runtime.

public class TestableView : MainView
{
  public TestableView()
  {
    ShowMessage = new Button();
    Message = new TextBlock();
    ...
    new MainController(this);
    ...
        

Migration from Windows Forms

The only remaining task was to migrate the namespaces to WPF from Windows Forms. Mostly this was just a search/replace exercise (with the compiler telling me everywhere something had changed):

  • Namespace moved to System.Windows;
  • Enabled property is now IsEnabled property;
  • MessageBox.Show has slightly fewer options;
  • Setting colours now uses a Brush class;
  • etc., ...

With just a little more work, you could wrap each control in a common interface (e.g., IButton, IComboBox), then the same controller could be used to run a view on multiple platforms (Silverlight/WPF/Windows Forms). You might even be able to use the controller to run a web application using a framework like Visual WebGui (which makes coding a web application magically like coding a Windows Forms application).

For a larger application I would definitely do this extra work - it is bound to pay itself back at some point, not least of all because you could have stubbed control implementations in the tests instead of the 'real' ones.

Summary

The modifications to move the demo from Windows Forms to Silverlight were a rewriting of the View (inevitable), and some minor syntax changes in the controller.

On a larger application the ability to keep 90% of your code (and the tests!) when migrating to another platform is invaluable, and Passive View is the best way I've seen of writing (and testing) the presentation layer.


Silverlight Passive View Demo
download, unzip, run CommandPrompt.bat, and type 'NAnt'

View Silverlight Passive View Demo Online

Submit this story to DotNetKicks Shout it

Following Richard's "HTML is crap" post this got me thinking about the real underlying message of his post. Yes technology has moved on a lot in recent years to the point where old reasons for choosing those [HTML applications] types of application have largely gone. However this isn't the first or the last time software people will have this frustration and here's why: too many business people come with software solutions and not business problems! I've no idea why this has happened other than some mistaken idea that software is easy and business is hard. In my experience when customers bring me software solutions rather than business problems the final software solutions I'm able to deliver to them are invariably poorer than they should be. So the next question is how can we stop people bringing us their half-baked solutions...

Submit this story to DotNetKicks Shout it

OK, so it's not crap. It's great for web sites, disseminating and formatting information, and linking content across the entire world. In fact, it's pretty amazing. But ... it is crap for writing applications.

Over the years we have jumped through all sort of hoops to get round the (extreme) limitations of HTML and browser technology. We have invented ways of maintaining user state by keeping cookies on browsers, by adding cryptic information to URLs, by posting hidden values in forms, and by restoring large state objects on the server to overcome some of these problems.

We have invented increasingly complicated frameworks to allow us to split presentation logic over two physical tiers with a (typically) compiled language running on the server while Javascript manipulates widgets on the client, and then found more and more ingenious ways of making this seem slick to the user. We have made code more complicated by the fact that these two separate tiers have to communicate in some fashion, and we convince ourselves that the solution is elegant.

As soon as we have to target more than one type of browser (or even different versions of the same browser) it is not going to look the same on what is essentially different platforms. (Not to mention the difficulties of actually writing and debugging for two versions of IE on the same machine.) You would have to be nuts to choose a platform where the output will be unpredictable, right?!

A common argument with the customer might go something like this:

Programmer: You have high expectations of usability. It is best to use a rich client.
Customer: It needs to be a web (HTML) application.
Programmer: Um ... why?
Customer: Because that's what we want.
Programmer: You do realise it will take twice as long to write, and be less usable?
Customer: It needs to be a web (HTML) application.
Three months later the programmer is blamed for the product not being amazingly slick, being slightly behind the time-scales, and the customer is insisting that they need their spelling mistakes underlined 'just like MS Word'!

Another common reason for a customer to insist on a web application is for ease of deployment, but it is not hard to push rich applications to the desktop using technologies like ClickOnce these days either.

The only people who most often have a genuine need to target such a low common denominator are those writing applications that target the Internet (e.g., Amazon, eBay, etc.) But even then it is clear that for years the best looking stuff on the web has Flash content.

In summary:

Choose WPF;
Choose Windows Forms;
Choose Silverlight;
Choose Flash;
Just don't choose HTML.

Choosing to write an HTML application, especially when you are going to deploy in a company intranet, is nothing short of insanity these days ... it's long past the time for developers to make customers realise this.

Submit this story to DotNetKicks Shout it