Fix crackling audio on your iPhone

If you find that music on your iPhone crackles when you listen through your headphones, try cleaning the headphone jack.

Because your iPhone lives in your pocket, pocket lint can accumulate inside your iPhone’s headphone jack.

I had 3 years of accumulated pocket lint inside my headphone jack, without knowing about it. For months I thought the iPhone was dead and I’d need to replace the headphone jack which is very difficult.

The solution – just get a bent paperclip and scrape it all out. Voila! No more crackling music!

Create a Movie Database Application with FubuMVC

I decided to have a look at FubuMVC, mainly to see what it offers over ASP.NET MVC. To do so I tasked myself with creating a simple Movies app, ala Stephen Walter’s Movie Database ASP.NET MVC tutorial.

So the requirements of the application are:

  1. List a set of movie database records
  2. Create a new movie database record (with validation!)
  3. Edit an existing movie database record

Quick, show me the code

You can download the source code from bitbucket.

The finished application is running here on AppHarbor. Click on the Title of a Movie to edit it – NB. the first 3 sample movies are read-only.

Let’s get started

In Visual Studio, File -> New Project -> Web -> ASP.NET Empty Web Application. Name it MovieApp.

Next, add FubuMVC via nuget (right-click MovieApp project, Manage NuGet packages, and search for FubuMVC).

Press F5 to check you get the FubuMVC welcome page.

Create the database

As per Stephen’s tutorial, I’m going to use SQL Server and Entity Framework for the data layer.

See his tutorial for instructions on how to set that up using Visual Studio. If you have SQL Server Management Studio then create a new database called MoviesDB, and run the following script to create a table.

CREATE TABLE [dbo].[Movies](
 [Id] [int] IDENTITY(1,1) NOT NULL,
 [Title] [nvarchar](255) NOT NULL,
 [Director] [nvarchar](255) NOT NULL,
 [DateReleased] [datetime] NOT NULL,
 [Synopsis] [ntext] NOT NULL,
 [ImageUri] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_Movies] PRIMARY KEY CLUSTERED
(
 [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Movies] ON
INSERT [dbo].[Movies] ([Id], [Title], [Director], [DateReleased], [Synopsis], [ImageUri]) VALUES (1, N'Star Wars', N'George Lucas', CAST(0x00006DDC00000000 AS DateTime), N'Part IV in George Lucas'' epic, Star Wars: A New Hope opens with a Rebel ship being boarded by the tyrannical Darth Vader. The plot then follows the life of a simple farm boy, Luke Skywalker, as he and his newly met allies (Han Solo, Chewbacca, Obi-Wan Kenobi, C-3PO, R2-D2) attempt to rescue a Rebel leader, Princess Leia, from the clutches of the Empire. The conclusion is culminated as the Rebels, including Skywalker and flying ace Wedge Antilles make an attack on the Empire''s most powerful and ominous weapon, the Death Star.', N'http://ia.media-imdb.com/images/M/MV5BMTU4NTczODkwM15BMl5BanBnXkFtZTcwMzEyMTIyMw@@._V1._SY317_.jpg')
INSERT [dbo].[Movies] ([Id], [Title], [Director], [DateReleased], [Synopsis], [ImageUri]) VALUES (2, N'Pulp Fiction', N'Quentin Tarantino', CAST(0x0000861D00000000 AS DateTime), N'Jules Winnfield and Vincent Vega are two hitmen who are out to retrieve a suitcase stolen from their employer, mob boss Marsellus Wallace. Wallace has also asked Vincent to take his wife Mia out a few days later when Wallace himself will be out of town. Butch Coolidge is an aging boxer who is paid by Wallace to lose his next fight. The lives of these seemingly unrelated people are woven together comprising of a series of funny, bizarre and uncalled-for incidents', N'http://ia.media-imdb.com/images/M/MV5BMjE0ODk2NjczOV5BMl5BanBnXkFtZTYwNDQ0NDg4._V1._SY317_CR4,0,214,317_.jpg')
INSERT [dbo].[Movies] ([Id], [Title], [Director], [DateReleased], [Synopsis], [ImageUri]) VALUES (3, N'Memento', N'Christopher Nolan', CAST(0x00008FD100000000 AS DateTime), N'Memento chronicles two separate stories of Leonard, an ex-insurance investigator who can no longer build new memories, as he attempts to find the murderer of his wife, which is the last thing he remembers. One story line moves forward in time while the other tells the story backwards revealing more each time.', N'http://ia.media-imdb.com/images/M/MV5BMjA3MTkzMzI3N15BMl5BanBnXkFtZTcwNzYwMzQ4MQ@@._V1._SY317_.jpg')
SET IDENTITY_INSERT [dbo].[Movies] OFF

I’ve added a couple of extra columns – ImageUri and Synopsis.

Create the Model

Back in Visual Studio, right-click on MovieApp and choose Add -> New Folder, and name it ‘Model’.

Right-click the Model folder, and choose Add -> New Item…, then Data -> ADO.NET Entity Data Model. Name it Movies.edmx. Select Generate from database, click Next. Choose the MoviesDB you just created, and click Next. Tick the Tables checkbox, and click Finish. Now click on the entity ‘Movy’. Open the Properties pane, and change its name to ‘Movie’ and the Entity Set Name to ‘Movies’.

With me so far? We should have something like this:

Create the FubuMVC Controller

FubuMVC isn’t as strict about what makes a controller as ASP.NET MVC. In fact, that is one of fubu’s big features – you can define your own conventions about what make a Controller (or a Handler in fubu-parlance). But in the interests of keeping this tutorial simple, I’m going to stick to the ‘ClassesSuffixedWithController’ convention.

Right-click MovieApp, Add -> New Folder, named ‘Movies’.
Right-click Movies folder, Add -> Class, named IndexController.cs:

using System.Collections.Generic;
using System.Linq;
using MovieApp.Model;

namespace MovieApp.Movies
{
    public class IndexController
    {
        private MoviesDBEntities _db = new MoviesDBEntities();

        public ViewModel Index(InputModel model)
        {
            return new ViewModel { Movies = _db.Movies.ToList() };
        }

        public class InputModel
        {
        }

        public class ViewModel
        {
            public IEnumerable<Movie> Movies { get; set; }
        }
    }
}

Because of the namespace (MoviesApp.Movies) and the name of the Action (public ViewModel Index(InputModel model)), the Index method is going to get hit when we navigate to /Movies/Index.

One of the best practices of FubuMVC is the OMIOMO rule – One Model In, One Model Out. This means every controller Action should have one unique model passed to it, and should return a different model.

My preference is to keep these Model classes nested in the Controller class to reduce namespace clutter, but you don’t have to.

So you can see that the Index method takes an instance of IndexController.Index, and returns an instance of IndexController.ViewModel.

Add a ViewEngine

FubuMVC supports the Spark view engine, the Web Forms view engine, and as of writing the Razor view engine is a work in progress.
I didn’t feel like learning Spark, and since Razor isn’t finished yet, I’ll use the Web Forms view engine (ala ASP.NET MVC1), which means .aspx files with a code-behind .aspx.cs – sorry about that.

Add the FubuMVC.WebForms NuGet package.

Now open /ConfigureFubuMVC.cs, and add the following lines:

public ConfigureFubuMVC()
{
    ....
    // View Engine
    Import<WebFormsEngine>();
}

Add the ‘Index’ View

Now right-click the Movies folder, Add -> New Item. Choose Web Form, and name it Index.aspx.
Delete the Index.aspx.designer.cs – we don’t need it.
Open the Index.aspx.cs, delete the existing code and replace it with this:

using FubuMVC.WebForms;
namespace MovieApp.Movies
{
    public partial class Index : FubuPage<IndexController.ViewModel>
    {
    }
}

This tells Index.aspx that its Model is an IndexController.ViewModel.
Now open Index.aspx, and replace it with this:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MovieApp.Movies.Index" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>All Movies</title>
</head>
<body>
    <% foreach (var movie in Model.Movies)
       { %>
            <h3><%: movie.Title %></h3>
            <b>Director:</b> <%: movie.Director %><br />
            <b>Date released:</b> <%: movie.DateReleased.ToString("MMM yyyy") %><br />
            <%: movie.Synopsis %><br />
    <% } %>
</body>
</html>

Notice that in the view we can use the ‘Model’ keyword just like in ASP.NET MVC.
Now press Ctrl-F5, and navigate to /movies/index, and you should see:

Remember, our Action is /Movies/Index because of the Controller’s namespace (MovieApp.Movies) + the name of the Action method (public ViewModel Index(InputModel model)). The Webforms view engine will look for a corresponding View in the same path – ~/Movies/, which has the same underlying Model type as the one returned by the Index action method – “IndexController.ViewModel”. Our Index.aspx.cs sets this (public partial class Index : FubuPage<IndexController.ViewModel>).

Add an Edit Movie page

Another FubuMVC recommendation is to have a separate Controller/Handler per action.
So let’s Right click the Movies folder, Add -> Class, and name it EditController.cs:

using System.Linq;
using FubuMVC.Core.Continuations;
using MovieApp.Model;

namespace MovieApp.Movies
{
    public class EditController
    {
        private MoviesDBEntities _db = new MoviesDBEntities();

        public Movie Get_Movies_Edit_MovieId(InputModel input)
        {
            return _db.Movies.FirstOrDefault(x => x.Id == input.MovieId);
        }

        public FubuContinuation Post_Movies_Edit(Movie input)
        {
            var movie = _db.Movies.First(m => m.Id == input.Id);
            movie.Title = input.Title;
            movie.Director = input.Director;
            movie.DateReleased = input.DateReleased;
            movie.Synopsis = input.Synopsis;

            _db.SaveChanges();
            return FubuContinuation.RedirectTo(new IndexController.InputModel());
        }

        public class InputModel
        {
            public int MovieId { get; set; }
        }
    }
}

This time, the name of the Action (Get_Movies_Edit_MovieId) follows another one of Fubu’s built-in conventions – underscores are replaced by /. So this Action will get hit when you navigate to /Movies/Edit/1. The Get at the front means we are restricting to HTTP GETs only.

Now let’s add the view.
Add a new Web Form as before, call it Edit.aspx. Edit.aspx.cs:

using FubuMVC.WebForms;
using MovieApp.Model;

namespace MovieApp.Movies
{
    public partial class Edit : FubuPage<Movie>
    {
    }
}

Edit.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Edit.aspx.cs" Inherits="MovieApp.Movies.Edit" %>
<%@ Import Namespace="MovieApp.Movies" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Edit Movie</title>
</head>
<body>
<% if (Model != null)
   { %>
    <%= this.FormFor(Model) %>
        <input type="hidden" name="Id" value="<%= Model.Id %>" />
        <fieldset>
            <legend>Edit Movie</legend>
            <p>
                <%= this.LabelFor(m => m.Title) %><%= this.InputFor(m => m.Title) %>
            </p>
            <p>
                <%= this.LabelFor(m => m.Director) %><%= this.InputFor(m => m.Director) %>
            </p>
            <p>
                <%= this.LabelFor(m => m.DateReleased) %><input type="text" name="DateReleased" value="<%: Model.DateReleased.ToShortDateString() %>" />
            </p>
            <p>
                <%= this.LabelFor(m => m.ImageUri) %><%= this.InputFor(m => m.ImageUri) %>
            </p>
            <p>
                <%= this.LabelFor(m => m.Synopsis) %><textarea name="Synopsis" rows="15" cols="70"><%: Model.Synopsis %></textarea>
            </p>
        </fieldset>
        <input type="submit" value="Save" />
        <%= this.LinkTo(new IndexController.InputModel()).Text("Cancel") %>
    <%= this.EndForm() %>
<% } %>
</body>
</html>

Notice that this time we’re using some of FubuMVC’s built-in HtmlTag helpers – this.FormFor(), this.LabelFor(), this.InputFor() etc.
One thing I like is the

<%= this.FormFor(Model) %>

This renders to:

   <form id="mainForm" method="post" action="/movies/edit">

Fubu is smart enough to put action=”/movies/edit” on the form – because FormFor(Model) is saying give me a form for the Model, and it knows the Model of type Movie, so it looks for any Action that takes a Movie as input. The Action it wants is defined in the EditController (public FubuContinuation Post_Movies_Edit(Movie input)) but it’s important to note that this Action could be anywhere and could be named anything. For example, if I rename the Post_Movies_Edit() method to “DumDeeDoo()”, then FormFor renders the form with

<form id="mainForm" method="post" action="/movies/dumdeedoo">

Note that the name of the Post method is following the same convention as the Get – Post_Movies_Edit exists at /Movies/Edit and will accept HTTP POSTs only. It returns a FubuContinuation which redirects us back to the Home page (/Movies/Index).

Now let’s look at the Cancel link at the bottom.

<%= this.LinkTo(new IndexController.InputModel()).Text("Cancel") %>

Renders to:

<a href="/movies/index">Cancel</a>

As with the FormFor, with “LinkTo” I ask for a link to an InputModel. This differs from the ASP.NET MVC approach of asking for an Html.ActionLink to a specific Action method. Fubu’s way is pretty cool – cos if I rename my actions or restructure the site, the links will still render the correct URLs.

Add Validation

Unfortunately validation isn’t included in the box, but we can add it. But it’s a pain in the ass.
First add the FubuMVC.Validation NuGet package.
Next change your /App_Start/FubuMVC.cs

using Bottles;
using FubuMVC.Core;
using FubuMVC.StructureMap;
using FubuValidation.StructureMap;
using StructureMap.Configuration.DSL;

// You can remove the reference to WebActivator by calling the Start() method from your Global.asax Application_Start
[assembly: WebActivator.PreApplicationStartMethod(typeof(MovieApp.App_Start.AppStartFubuMVC), "Start", callAfterGlobalAppStart: true)]

namespace MovieApp.App_Start
{
    public static class AppStartFubuMVC
    {
        public static void Start()
        {
            // FubuApplication "guides" the bootstrapping of the FubuMVC
            // application
            FubuApplication.For<ConfigureFubuMVC>() // ConfigureFubuMVC is the main FubuRegistry
                                                    // for this application.  FubuRegistry classes
                                                    // are used to register conventions, policies,
                                                    // and various other parts of a FubuMVC application

                // FubuMVC requires an IoC container for its own internals.
                .StructureMapObjectFactory(configure => configure.AddRegistry<MovieAppRegistry>())
                .Bootstrap();

			// Ensure that no errors occurred during bootstrapping
			PackageRegistry.AssertNoFailures();
        }

        public class MovieAppRegistry : Registry
        {
            public MovieAppRegistry()
            {
                Scan(x =>
                {
                    x.TheCallingAssembly();
                    x.WithDefaultConventions();
                });

                this.FubuValidation();
            }
        }
    }
}

Now add these lines to /ConfigureFubuMVC.cs

public ConfigureFubuMVC()
{
    ...
    Views.TryToAttachWithDefaultConventions()
                .RegisterActionLessViews(t => t.ViewModelType == typeof(FubuValidation.Notification));

    this.Validation(validation => {
        // Include all action calls that: 1) have input and 2) whose input models contain the string "ViewModel"
        validation
            .Actions
            .Include(call => call.HasInput && call.InputType().Name.Contains("ViewModel"));

        // This DSL reads as follows...
        // When handling failures:
        //  If the input type of the action call is not null and the name of the model contains the string "ViewModel",
        //  Then Transfer to a behavior chain that is resolved by my custom HandlerModelDescriptor class
        validation
            .Failures
            .If(f => f.InputType() != null && f.InputType().Name.Contains("ViewModel"))
            .TransferBy<HandlerModelDescriptor>();
    });
}

The above is saying that we’re adding validation to any method that takes an input, and has “ViewModel” in its name.
The method it will redirect to if validation fails is determined by the HandlerModelDescriptor class, which we will now add.

/HandlerModelDescriptor.cs

using System;
using System.Linq;
using FubuMVC.Core.Registration;
using FubuMVC.Validation;

namespace MovieApp
{
    public class HandlerModelDescriptor : IFubuContinuationModelDescriptor
    {
        private readonly BehaviorGraph _graph;

        public HandlerModelDescriptor(BehaviorGraph graph)
        {
            _graph = graph;
        }

        public Type DescribeModelFor(ValidationFailure context)
        {
            // Remember, behavior chains can be identified by the input model type
            // The IFubuContinuationModelDescriptor interface is used to describe the input model type of the chain
            // that we want to transfer to

            // we're going to query the BehaviorGraph to find the corresponding GET for the POST
            // obviously, we'd need to make this smarter but this is just a simple example
            var targetName = context.Target.HandlerType.Name;
            var getCall = _graph
                .Behaviors
                .Where(chain => chain.FirstCall() != null && chain.FirstCall().HandlerType.Name == targetName
                    && chain.Route.AllowedHttpMethods.Contains("GET"))
                .Select(chain => chain.FirstCall())
                .FirstOrDefault();

            if(getCall == null)
            {
                return null;
            }

            return getCall.InputType();
        }
    }
}

This is called when validation fails. It returns the Input Type of the method to pass control to.
To find it, it looks for an HTTP GET method inside the same Controller as the method doing the POSTing.

Now we need to add a View which is called when validation is needed.
Add a new folder, /Shared.
/Shared/ValidationSummary.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ValidationSummary.aspx.cs" Inherits="MovieApp.Shared.ValidationSummary" %>

<% if (Model.AllMessages.Any()) { %>
    <ul style="color:Red">
        <% foreach (var msg in Model.AllMessages)
           { %>
                <li><%= msg %></li>
        <% } %>
    </ul>
<% } %>

/Shared/ValidationSummary.aspx.cs

using FubuMVC.WebForms;
namespace MovieApp.Shared
{
    public partial class ValidationSummary : FubuPage <FubuValidation.Notification>
    {
    }
}

Add a Create Movie page (with validation)

/Movies/CreateController.cs

using System;
using FubuMVC.Core.Continuations;
using FubuValidation;
using MovieApp.Model;

namespace MovieApp.Movies
{
    public class CreateController
    {
        private MoviesDBEntities _db = new MoviesDBEntities();

        public ViewModel Get_Movies_Create(InputModel input)
        {
            return new ViewModel { DateReleased = DateTime.Parse("01/01/2001") };
        }

        public FubuContinuation Post_Movies_Create(ViewModel input)
        {
            var movie = new Movie
            {
                Title = input.Title,
                Director = input.Director,
                DateReleased = input.DateReleased,
                ImageUri = input.ImageUri,
                Synopsis = input.Synopsis
            };

            _db.Movies.AddObject(movie);
            _db.SaveChanges();

            return FubuContinuation.RedirectTo(new IndexController.InputModel());
        }

        public class InputModel
        {
        }

        public class ViewModel
        {
            [Required]
            public string Title { get; set; }
            [Required]
            [MaximumStringLength(50)]
            public string Director { get; set; }
            [Required]
            public DateTime DateReleased { get; set; }
            [Required]
            public string ImageUri { get; set; }
            [Required]
            public string Synopsis { get; set; }
        }
    }
}

I’ve taken a best practice approach with the Create – by binding to a view model (ViewModel), instead of binding directly to the Movie domain object. This also allows me to add FubuValidation attributes like [Required].

/Movies/Create.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Create.aspx.cs" Inherits="MovieApp.Movies.Create" %>
<%@ Import Namespace="MovieApp.Movies" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Create Movie</title>
</head>
<body>
    <%= this.FormFor(Model) %>
        <fieldset>
            <legend>Create Movie</legend>
            <%= this.Partial<FubuValidation.Notification>() %>
            <p>
                <%= this.LabelFor(m => m.Title) %><%= this.InputFor(m => m.Title) %>
            </p>
            <p>
                <%= this.LabelFor(m => m.Director) %><%= this.InputFor(m => m.Director) %>
            </p>
            <p>
                <%= this.LabelFor(m => m.DateReleased) %><input type="text" name="DateReleased" value="<%: Model.DateReleased.ToShortDateString() %>" />
            </p>
            <p>
                <%= this.LabelFor(m => m.ImageUri) %><%= this.InputFor(m => m.ImageUri) %>
            </p>
            <p>
                <%= this.LabelFor(m => m.Synopsis) %><textarea name="Synopsis" rows="15" cols="70"><%: Model.Synopsis %></textarea>
            </p>
        </fieldset>
        <input type="submit" value="Save" /> <%= this.LinkTo(new IndexController.InputModel()).Text("Cancel") %>
    <%= this.EndForm() %>
</body>
</html>

/Movies/Create.aspx.cs

using FubuMVC.WebForms;
namespace MovieApp.Movies
{
    public partial class Create : FubuPage<CreateController.ViewModel>
    {
    }
}

If all works correctly, you should be able to navigate to /Movies/Create, and see the following:

and then if we don’t enter a Required field, we get the Validation summary at the top:

Unfortunately that’s all you get for validation – no message alongside the invalid field. And the values which the user previously entered aren’t persisted, which is annoying.

Finishing touches

I’ll add a quick skin using one of the great templates at freecsstemplates.org. I’ll also add a link to the Create, and Edit pages, and also a Delete Movie button.

You can see the sample application running here on AppHarbor. Click on the Title of a Movie to edit it – NB. the first 3 sample movies are read-only.

You can download the source code from bitbucket.

jsTree tri-state checkboxes with ASP.NET MVC revisited

My original jsTree checkboxes post is still my most popluar, yet it is 18 months old and no longer works with the latest version of jsTree (pre 1.0). Since that post is so popular I thought I should update it.

My first surprise was how much the jsTree guys changed their API between releases – I was only going from version 0.9.9 to version 1.0, but  I had to practically start from scratch.

So, without further ado:

jsTree is a jQuery plugin for creating a treeviews, and jsTree’s checkbox plugin allows you to create a treeview with tri-state checkboxes, like so:

Notice how “Origination” appears half-checked because only some of its children are checked.

Getting started

For this demo I am using ASP.NET MVC 3 and jsTree pre 1.0 fixed. Let’s start with a new “ASP.NET MVC 3 Web Application”, choose Internet Application, name it jsTreeDemo, and add the required jsTree files to our solution:

In the View, create a div which you want to become a treeview. I’ll name mine demoTree. Also add references to the jsTree script, and add a new index.js file for our custom javascript.

Views/Home/Index.cshtml

@{
 ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>

<div id="demoTree">

</div>

treeModel = [{"data":"Confirm Application","attr":{"id":"10"},"children":null},{"data":"Things","attr":{"id":"20"},"children":[{"data":"Thing 1","attr":{"id":"21"},"children":null},{"data":"Thing 2","attr":{"id":"22"},"children":null},{"data":"Thing 3","attr":{"id":"23"},"children":null},{"data":"Thing 4","attr":{"id":"24"},"children":[{"data":"Thing 4.1","attr":{"id":"241"},"children":null},{"data":"Thing 4.2","attr":{"id":"242"},"children":null},{"data":"Thing 4.3","attr":{"id":"243"},"children":null}]}]},{"data":"Colors","attr":{"id":"40"},"children":[{"data":"Red","attr":{"id":"41"},"children":null},{"data":"Green","attr":{"id":"42"},"children":null},{"data":"Blue","attr":{"id":"43"},"children":null},{"data":"Yellow","attr":{"id":"44"},"children":null}]}];



Scripts/index.js

/// 

$(function () {
   $("#demoTree").jstree({
      json_data : {
         data : treeModel
      },
      plugins : ["themes", "json_data", "ui", "checkbox"]
 });
});

Briefly, jstree has a number of ways of poplulating the tree with data – in my previous jsTree post I was populating the tree via AJAX but for now the tree is populated via hardcoded JSON (using the built-in "json_data" plugin). Don’t forget to specify the "checkbox" plugin too.

Populate the tree from a viewmodel

Just like last time, let’s create a viewmodel for creating the tree structure.

Models/JsTreeModel.cs

namespace jsTreeDemo.Models
{
  public class JsTreeModel
  {
    public string data;
    public JsTreeAttribute attr;
    public JsTreeModel[] children;
  }
  
  public class JsTreeAttribute
  {
    public string id;
    public bool selected;
  }
}

Now let’s change our controller to create a viewmodel, serialize it to JSON, and pass that string to our View.
Controllers/HomeController.cs

public ActionResult Index()
{
    ViewBag.Message = "Welcome!";
            
    var model = GetTreeData();
            
    string jsonModel = new JavaScriptSerializer().Serialize(model);
            
    return View("Index", "_Layout", jsonModel);
}

private JsTreeModel[] GetTreeData()
{
var tree = new JsTreeModel[] 
{
    new JsTreeModel { data = "Confirm Application", attr = new JsTreeAttribute { id = "10", selected = true } },
    new JsTreeModel 
    { 
        data = "Things",
        attr = new JsTreeAttribute { id = "20" },
        children = new JsTreeModel[]
            {
                new JsTreeModel { data = "Thing 1", attr = new JsTreeAttribute { id = "21", selected = true } },
                new JsTreeModel { data = "Thing 2", attr = new JsTreeAttribute { id = "22" } },
                new JsTreeModel { data = "Thing 3", attr = new JsTreeAttribute { id = "23" } },
                new JsTreeModel 
                { 
                    data = "Thing 4", 
                    attr = new JsTreeAttribute { id = "24" },
                    children = new JsTreeModel[] 
                    { 
                        new JsTreeModel { data = "Thing 4.1", attr = new JsTreeAttribute { id = "241" } }, 
                        new JsTreeModel { data = "Thing 4.2", attr = new JsTreeAttribute { id = "242" } }, 
                        new JsTreeModel { data = "Thing 4.3", attr = new JsTreeAttribute { id = "243" } }
                    },
                },
            }
    },
    new JsTreeModel 
    {
        data = "Colors",
        attr = new JsTreeAttribute { id = "40" },
        children = new JsTreeModel[]
            {
                new JsTreeModel { data = "Red", attr = new JsTreeAttribute { id = "41" } },
                new JsTreeModel { data = "Green", attr = new JsTreeAttribute { id = "42" } },
                new JsTreeModel { data = "Blue", attr = new JsTreeAttribute { id = "43" } },
                new JsTreeModel { data = "Yellow", attr = new JsTreeAttribute { id = "44" } },
            }
    }
};

return tree;
}

Views/Index.cshtml

@model string
       
@{
    ViewBag.Title = "Home Page";
}

...


    treeModel = @Html.Raw(Model);

Determining which items are checked when posting

Let’s put our tree inside a <form> and submit it.
Views/Index.cshtml

...
@using (Html.BeginForm("Results", "Home", FormMethod.Post))
{ 
    <div id="demoTree">
    </div>
    <br />
    <div>
        
    </div>
} 
...

And now let’s add the Results method to the HomeController

Controllers/HomeController.cs

[HttpPost]
public ActionResult Results(FormCollection form)
{
    return View(form);
}

and the View

Views/Home/Results.cshtml

@model FormCollection

@{
    ViewBag.Title = "Results";
}

<h2>Results</h2>
<p>
    You chose:
    @foreach (var item in Model.AllKeys)
    {
        @Model[item]@: 
    } 
</p>

<p>
    @Html.ActionLink("Home", "Index")
</p>

If you press the Submit button, nothing will happen as nothing is passed through in the FormCollection to Results() in HomeController. Despite appearances, jsTree doesn’t by default render any HTML <input>s for the checkboxes. But it’s easy enough to tell it to render them using the real_checkboxes flag:

Scripts/index.js

$(function () {
    $("#demoTree").jstree({
        json_data: {
            data: treeModel
        },
        checkbox: {
            real_checkboxes: true,
            real_checkboxes_names: function (n) {
                return [("check_" + (n[0].id || Math.ceil(Math.random() * 10000))), n[0].id]
            }
        },
        plugins: ["themes", "json_data", "ui", "checkbox"]
    });
});

Now when we submit our form, we should see the ids of the values which were checked:

What about telling the tree to pre-check some items?

So how do we render the tree with some items already checked? Notice how we added “public bool selected” to JsTreeAttribute? This doesn’t do anything as far as the checkboxes are concerned, but it does add a custom property called “selected” to each node’s <li>. We can use that to tell the jsTree to check the given node, by binding to the ‘loaded.jstree’ event.

Scripts/index.js

$(function () {
    $("#demoTree").jstree({
        json_data: {
            data: treeModel
        },
        checkbox: {
            real_checkboxes: true,
            real_checkboxes_names: function (n) {
                return [("check_" + (n[0].id || Math.ceil(Math.random() * 10000))), n[0].id]
            }
        },
        plugins: ["themes", "json_data", "ui", "checkbox"]
    }).bind("loaded.jstree", function (event, data) {
        $('#demoTree').jstree('check_node', 'li[selected=selected]');
    });
});

You’ll see that the nodes that are marked with selected=true in GetTreeData() are now pre-checked when you first load the page.

Here’s a link to the solution (VS 2010).

Running a Win 7 VM on a Macbook Air (2011)

A few days ago I bought a new Macbook Air. I went for the fully pimped one which comes with an i7 and a 256Gb SSD. With the VMware Fusion demo I was able to bring my old laptop over and run it as a VM inside the Mac.

A few notes:

1. VMware’s Migration Assistant wanted to create a VM of my entire laptop, with both C: and D: disks. I didn’t want to bring over the 200Gb D: and the Migration Assistant didn’t have any options to prevent that from happening. So on my old laptop, I installed VMware’s free VMware converter. That had many more options including the option to exclude D:

2. It looks like I don’t need to buy a new Windows license. So far my VM hasn’t phoned home to Microsoft – maybe it will?

3. For networking, although I’m connected on WiFi to my flat’s network on the Mac, the VM sees it as an ethernet connection. Initially I couldn’t connect to the network on the VM because it was complaining that there were no ethernet drivers installed. I had to edit the .vmx file and add a line ethernet0.virtualDEV = “e1000”.

4. Visual Studio is completely red and unusable! I haven’t figured that one out yet.

Update (Dec 2011)

The Visual Studio red problem I had was with VMWare Fusion 3 and Parallels 6, but it seems to be fixed in VMWare Fusion 4 and Parallels 7. Having said that, I don’t use virtualisation and reboot and run Win7 in Boot Camp.

Visual Studio 2010 PerfWatson extension

Today I installed the Visual Studio PerfWatson extension. Briefly, it automatically reports when Visual Studio hangs (for > 2 seconds) to Microsoft so that they can one day fix it.

But a nicety is the PerfWatson Monitor extension, which adds a cool little graphic it adds to the bottom of Visual Studio:

Since I’m running Visual Studio on my lowely netbook which constantly hangs (i.e. doesn’t respond for a few seconds), Microsoft will be getting plenty of data from me!

The Perf Watson blog is here but if you want to install it just go to Tools -> Extension Manager (in VS) and search for Watson in the Online Gallery. Install Visual Studio PerfWatson and PerfWatson Monitor.

iTunes sucks balls

Every time I use iTunes, I hate it. And now I have yet another reason to hate it:

itunes upgrade required

Oh fuck off, I don’t believe you. I’m running 10.2.2 and you’re telling me that I need to upgrade to the latest version just to stream a video? Bullshit. You’re making me upgrade because you think you can make me upgrade. Well fuck you, I’m not going to, so I won’t bother watching the iTunes Festival performances either.

SortedDictionary of countries in the world

In case you need it – this might come in handy.

A dictionary of all countries in the world with their ISO 3166 two digit country code.

SortedDictionary countries = new SortedDictionary {

{“AF”, “Afghanistan”},
{“AX”, “Aland Islands”},
{“AL”, “Albania”},
{“DZ”, “Algeria”},
{“AS”, “American Samoa”},
{“AD”, “Andorra”},
{“AO”, “Angola”},
{“AI”, “Anguilla”},
{“AQ”, “Antarctica”},
{“AG”, “Antigua and Barbuda”},
{“AR”, “Argentina”},
{“AM”, “Armenia”},
{“AW”, “Aruba”},
{“AU”, “Australia”},
{“AT”, “Austria”},
{“AZ”, “Azerbaijan”},
{“BS”, “Bahamas”},
{“BH”, “Bahrain”},
{“BD”, “Bangladesh”},
{“BB”, “Barbados”},
{“BY”, “Belarus”},
{“BE”, “Belgium”},
{“BZ”, “Belize”},
{“BJ”, “Benin”},
{“BM”, “Bermuda”},
{“BT”, “Bhutan”},
{“BO”, “Bolivia “},
{“BQ”, “Bonaire”},
{“BA”, “Bosnia and Herzegovina”},
{“BW”, “Botswana”},
{“BV”, “Bouvet Island”},
{“BR”, “Brazil”},
{“IO”, “British indian Ocean Territory”},
{“BN”, “Brunei Darussalam”},
{“BG”, “Bulgaria”},
{“BF”, “Burkina Faso”},
{“BI”, “Burundi”},
{“KH”, “Cambodia”},
{“CM”, “Cameroon”},
{“CA”, “Canada”},
{“CV”, “Cape Verde”},
{“KY”, “Cayman Islands”},
{“CF”, “Central African Republic”},
{“TD”, “Chad”},
{“CL”, “Chile”},
{“CN”, “China”},
{“CX”, “Christmas Island”},
{“CC”, “Cocos (keeling) Islands”},
{“CO”, “Colombia”},
{“KM”, “Comoros”},
{“CG”, “Congo”},
{“CD”, “Congo, The Democratic Republic of the”},
{“CK”, “Cook Islands”},
{“CR”, “Costa Rica”},
{“CI”, “Cote d’Ivoire”},
{“HR”, “Croatia”},
{“CU”, “Cuba”},
{“CW”, “Curacao”},
{“CY”, “Cyprus”},
{“CZ”, “Czech Republic”},
{“DK”, “Denmark”},
{“DJ”, “Djibouti”},
{“DM”, “Dominica”},
{“DO”, “Dominican Republic”},
{“EC”, “Ecuador”},
{“EG”, “Egypt”},
{“SV”, “El salvador”},
{“GQ”, “Equatorial guinea”},
{“ER”, “Eritrea”},
{“EE”, “Estonia”},
{“ET”, “Ethiopia”},
{“FK”, “Falkland Islands (Malvinas)”},
{“FO”, “Faroe Islands”},
{“FJ”, “Fiji”},
{“FI”, “Finland”},
{“FR”, “France”},
{“GF”, “French Guiana”},
{“PF”, “French Polynesia”},
{“TF”, “French Southern Territories”},
{“GA”, “Gabon”},
{“GM”, “Gambia”},
{“GE”, “Georgia”},
{“DE”, “Germany”},
{“GH”, “Ghana”},
{“GI”, “Gibraltar”},
{“GR”, “Greece”},
{“GL”, “Greenland”},
{“GD”, “Grenada”},
{“GP”, “Guadeloupe”},
{“GU”, “Guam”},
{“GT”, “Guatemala”},
{“GG”, “Guernsey”},
{“GN”, “Guinea”},
{“GW”, “Guinea-Bissau”},
{“GY”, “Guyana”},
{“HT”, “Haiti”},
{“HM”, “Heard Island and Mcdonald Islands”},
{“HN”, “Honduras”},
{“HK”, “Hong Kong”},
{“HU”, “Hungary”},
{“IS”, “Iceland”},
{“IN”, “India”},
{“ID”, “Indonesia”},
{“IR”, “Iran,”},
{“IQ”, “Iraq”},
{“IE”, “Ireland”},
{“IM”, “Isle of Man”},
{“IL”, “Israel”},
{“IT”, “Italy”},
{“JM”, “Jamaica”},
{“JP”, “Japan”},
{“JE”, “Jersey”},
{“JO”, “Jordan”},
{“KZ”, “Kazakhstan”},
{“KE”, “Kenya”},
{“KI”, “Kiribati”},
{“KP”, “Korea, Democratic People’s Republic of”},
{“KR”, “Korea, Republic of”},
{“KW”, “Kuwait”},
{“KG”, “Kyrgyzstan”},
{“LA”, “Lao People’s Democratic Republic”},
{“LV”, “Latvia”},
{“LB”, “Lebanon”},
{“LS”, “Lesotho”},
{“LR”, “Liberia”},
{“LY”, “Libya”},
{“LI”, “Liechtenstein”},
{“LT”, “Lithuania”},
{“LU”, “Luxembourg”},
{“MO”, “Macao”},
{“MK”, “Macedonia,”},
{“MG”, “Madagascar”},
{“MW”, “Malawi”},
{“MY”, “Malaysia”},
{“MV”, “Maldives”},
{“ML”, “Mali”},
{“MT”, “Malta”},
{“MH”, “Marshall Islands”},
{“MQ”, “Martinique”},
{“MR”, “Mauritania”},
{“MU”, “Mauritius”},
{“YT”, “Mayotte”},
{“MX”, “Mexico”},
{“FM”, “Micronesia,”},
{“MD”, “Moldova, Republic of”},
{“MC”, “Monaco”},
{“MN”, “Mongolia”},
{“ME”, “Montenegro”},
{“MS”, “Montserrat”},
{“MA”, “Morocco”},
{“MZ”, “Mozambique”},
{“MM”, “Myanmar”},
{“NA”, “Namibia”},
{“NR”, “Nauru”},
{“NP”, “Nepal”},
{“NL”, “Netherlands”},
{“NC”, “New Caledonia”},
{“NZ”, “New Zealand”},
{“NI”, “Nicaragua”},
{“NE”, “Niger”},
{“NG”, “Nigeria”},
{“NU”, “Niue”},
{“NF”, “Norfolk Island”},
{“MP”, “Northern Mariana Islands”},
{“NO”, “Norway”},
{“OM”, “Oman”},
{“PK”, “Pakistan”},
{“PW”, “Palau”},
{“PS”, “Palestinian Territory, Occupied”},
{“PA”, “Panama”},
{“PG”, “Papua New Guinea”},
{“PY”, “Paraguay”},
{“PE”, “Peru”},
{“PH”, “Philippines”},
{“PN”, “Pitcairn”},
{“PL”, “Poland”},
{“PT”, “Portugal”},
{“PR”, “Puerto Rico”},
{“QA”, “Qatar”},
{“RE”, “Reunion”},
{“RO”, “Romania”},
{“RU”, “Russian Federation”},
{“RW”, “Rwanda”},
{“BL”, “Saint Barthelemy”},
{“SH”, “Saint Helena,”},
{“KN”, “Saint Kitts and Nevis”},
{“LC”, “Saint Lucia”},
{“MF”, “Saint Martin (French Part)”},
{“PM”, “Saint Pierre and Miquelon”},
{“VC”, “Saint Vincent and the Grenadines”},
{“WS”, “Samoa”},
{“SM”, “San Marino”},
{“ST”, “Sao Tome and Principe”},
{“SA”, “Saudi Arabia”},
{“SN”, “Senegal”},
{“RS”, “Serbia”},
{“SC”, “Seychelles”},
{“SL”, “Sierra Leone”},
{“SG”, “Singapore”},
{“SX”, “Sint Maarten (Dutch part)”},
{“SK”, “Slovakia”},
{“SI”, “Slovenia”},
{“SB”, “Solomon Islands”},
{“SO”, “Somalia”},
{“ZA”, “South africa”},
{“GS”, “South Georgia and the South Sandwich Islands”},
{“ES”, “Spain”},
{“LK”, “Sri Lanka”},
{“SD”, “Sudan”},
{“SR”, “Suriname”},
{“SJ”, “Svalbard and Jan Mayen”},
{“SZ”, “Swaziland”},
{“SE”, “Sweden”},
{“CH”, “Switzerland”},
{“SY”, “Syrian Arab Republic”},
{“TW”, “Taiwan, Province of China”},
{“TJ”, “Tajikistan”},
{“TZ”, “Tanzania,”},
{“TH”, “Thailand”},
{“TL”, “Timor-leste”},
{“TG”, “Togo”},
{“TK”, “Tokelau”},
{“TO”, “Tonga”},
{“TT”, “Trinidad and Tobago”},
{“TN”, “Tunisia”},
{“TR”, “Turkey”},
{“TM”, “Turkmenistan”},
{“TC”, “Turks and Caicos Islands”},
{“TV”, “Tuvalu”},
{“UG”, “Uganda”},
{“UA”, “Ukraine”},
{“AE”, “United Arab Emirates”},
{“GB”, “United Kingdom”},
{“US”, “United States”},
{“UM”, “United States Minor Outlying Islands”},
{“UY”, “Uruguay”},
{“UZ”, “Uzbekistan”},
{“VU”, “Vanuatu”},
{“VA”, “Vatican City State”},
{“VE”, “Venezuela,”},
{“VN”, “Vietnam”},
{“VG”, “Virgin Islands, British”},
{“VI”, “Virgin Islands, U.S.”},
{“WF”, “Wallis and Futuna”},
{“EH”, “Western Sahara”},
{“YE”, “Yemen”},
{“ZM”, “Zambia”},
{“ZW”, “Zimbabwe”}
};

Using EF code first with an mdf database in App_Data

I’ve been playing around some more with EF code first. Today’s task was to try to get it to work with a .mdf file located in the App_Data folder of the web project.

It wasn’t much fun.

I hijacked the existing aspnetdb.mdf and tried to get that to work with EF Code First. Short answer: don’t bother, for these reasons:

  1. I couldn’t get EF code first to create my Category table
  2. So then I’d created the table manually myself in SQL Server Express, but EF still wouldn’t recognise it – “Invalid object name dbo.Category”
  3. So then I decided to start with a new .mdf file and yay EF code first created my Category table
  4. run aspnet_regsql.exe on it to get the ASP.NET Membership tables in there – fail
  5. Visual Studio has a lock on the mdf file – delete it from VS’s Server Explorer, run aspnet_regsql, yay
  6. Open DB in SQL Server Management studio to see what my database looks like – fail, locks on file

I’m not sure how to debug EF code first, I mean if it doesn’t work (i.e. your table isn’t created) you have no idea why.

So I guess I got it working in the end, but all the problems with the files being locked made me realise it’s not worth the hassle so I started again with a normal database inside SQL Express.

  1. Add your connection string:
    <add name="SiteDB" 
         connectionString="data source=.\SQLEXPRESS;Initial Catalog=Site;Integrated Security=SSPI;"
         providerName="System.Data.SqlClient" />

I recommend explicitly setting a connection string otherwise your database will have a big silly namespaced name. You don’t need to create the database though, EF will do that much for you.

  1. Run your app and hit the Categories page so that EF will create the tables
  2. Run aspnet_regsql.exe on it to add the membership tables
  3. Change your membership connection string to point to the same DB – or tell the AspNetSqlMembership provider to use that connection string blah blah

Announcing MVC3 starter site

Inspired by Rob Conery’s MVC starter site, I’ve created an MVC3 starter site.

It basically takes Visual Studio’s File -> New Project -> ASP.NET MVC 3 Web Application and builds on that, adding all the things we need to get started – IOC (Unity), Unit of work pattern, Logging (NLog), Reporting, oh and some CSS from freecsstemplates.org.

It’s also my first open source project in a while…

You can download it from codeplex at http://mvc3starter.codeplex.com

mvc3 starter app screenshot