Category Archives: ASP MVC

REST : Simple REST Framework

My wife has just given birth 2 my second son, and I took 2 weeks off to help around the house. Though I did find a little bit of time to try and create a very simple REST framework from scratch using just the base class library of .NET (i.e., no 3rd party libraries), ok I did use JSON.NET for serializing JSON, but other than that it is all my own code.

I found a bit more time this week, and decided to write it up over here:

http://www.codeproject.com/Articles/826383/REST-A-Simple-REST-framework

Now it is no way production quality code, and I would not use it ever, but it was a fun exercise, and I enjoyed the ride, may be interesting read for some of you.

Hope you enjoy

PS : I start a 10 week F# course next week, so you may see some more F# posts from me in the future.

Advertisements

Paper Effect Google Maps

A friend of mine Marlon Grech, has his own business and he has a nice parallax effect web site : http://www.thynksoftware.com/ and over there on his “contact us2 page, it has this very cool folding Google maps thing. I have wondered how it was done for a while now, today I decided to find out. A colleague of mine was like WHY!….Ashic if you are reading this, sorry but I think it is cool, but I promise you I will be back to trying to slay the world with sockets tomorrow, a slight distraction shall we say.

Not Really My Idea – Credit Where Credit Is Due

Now the code I present in this post is not my own at all, I have added the ability to toggle the folding of the map, but that really is all I have done. None the less, I think it is still of interest to describe how it was done, and worth a small write up. I think the original authors have done a great job, but did not really explain anything, so hopefully by the time you get to the end of this post, the effect will be a bit more familiar to you.

The original code that forms the basis to this post is here : http://experiments.bonnevoy.com/foldedmap/demo/

The Basic Idea

The idea is actually not too hard to grasp. There is a master DIV, which contains the actual Google map, this DIV has a VERY low opacity, so low you can’t actually see it. Then there are 6 other DIVS that get a slice of the original DIV, this is done by some clever margins, as can be seen in this image and the code that follows it:

image

The relevant HTML is here:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Folding google maps</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="http://maps.google.com/maps/api/js?sensor=true"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="script.js"></script>
    <!–[if lt IE 9]>
    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script&gt;
    <![endif]–>
</head>
<body>
    <header>
        <a href="/foldedmap/" id="prev">Click to toggle</a>
        <p>Folded Google Maps (Webkit only)</p>
    </header>
    
    <div id="map_master"></div>
    <div id="container">
        <div id="mapContainer1" class="mapContainer odd first"></div>
        <div id="mapContainer2" class="mapContainer even"></div>
        <div id="mapContainer3" class="mapContainer odd"></div>
        <div id="mapContainer4" class="mapContainer even"></div>
        <div id="mapContainer5" class="mapContainer odd"></div>
                        <div id="mapContainer6" class="mapContainer even last"></div>
    </div>
</body>
</html>

Each of these slices is a fixed width of 160px and a height of 400px. The overflow is also hidden, that part is important, as will see in just a minute. The relevant CSS is here:

.mapContainer {
    background:#fff;
    float:left;
    width: 160px;
    height: 400px;
    overflow: hidden;
    border-top: 10px solid #fff;
    border-bottom: 10px solid #fff;
    -webkit-transform-style: preserve-3d;
    -moz-transform-style: preserve-3d;
    -ms-transform-style: preserve-3d;
    -o-transform-style: preserve-3d;
    transform-style: preserve-3d;
    box-shadow:0px 4px 0 #10335C;
}

 

Each slice of the original gets a certain margin applied, where the overflow for the 6 DIVS is hidden. So by moving the slice into the desired position by way of clever margin positioning the remaining portion of the map for that slice would not be seen, thanks to overflow being hidden. Sneaky

mapSync: function() {
    var map_clone = $('#map_master').clone().css('opacity',1).css('z-index',-1);
    $('#mapContainer1').html(map_clone.clone().css('marginLeft','80px'));
    $('#mapContainer2').html(map_clone.clone().css('marginLeft','240px'));
    $('#mapContainer3').html(map_clone.clone().css('marginLeft','400px'));
    $('#mapContainer4').html(map_clone.clone().css('marginLeft','560px'));
    $('#mapContainer5').html(map_clone.clone().css('marginLeft','720px'));
    $('#mapContainer6').html(map_clone.clone().css('marginLeft','880px'));
},

 

The next part of the trick is to apply some web kit 3D transforms. This is done inside a Timeout function so it happens every 10 milliseconds, up to/down from some predetermined values.

Here is the code that does applies the transforms

applyTransforms: function () {
    var prefixes = ['webkit', 'moz', 'ms', 'o', ''];
    for(i in prefixes) {
        $('.odd').css(prefixes[i] + 'Transform', 'rotateX(' +
            this.rotateX + 'deg) rotateY(' + –this.rotateY + 'deg)');
        $('.even').css(prefixes[i] + 'Transform', 'rotateX(' +
            this.rotateX + 'deg) rotateY(' + this.rotateY + 'deg)');
    }
    $('.mapContainer').css('marginLeft',
        -160 * (1 – Math.cos(this.rotateY / 360 * 2 * Math.PI)) + 'px');
},

 

It can be seen above that the applying of the transforms is done for each of the vendor specific flavours, Mozilla, Webkit etc etc

The code below unfolds/folds the 6 DIVS by calling he applyTransforms function outlined above.

unfoldMap: function () {
    if(this.rotateY > 20)
    {
        this.rotateY -= 0.5;
        this.rotateX += 0.1;
        this.applyTransforms();
    }
    else
    {
        clearInterval(this.fold_out);
        this.isOpen = true;
    }
},

foldMap: function () {
    if(this.rotateY < 90)
    {
        this.rotateY += 0.5;
        this.rotateX -= 0.1;
        this.applyTransforms();
    }
    else
    {
        clearInterval(this.fold_in);
        this.isOpen = false;
    }
}

 

Anyway like I say the code is pretty much as seen in the original link, all I added was the ability to toggle between a unfolded map back to a folded one.

Though I have added a bit more of an explanation, so hopefully it added some value

Where Is The Code?

Anyway if you want a small demo project I have created one for VS2013, that you can grab from GitHub here : https://github.com/sachabarber/FoldingCSSGooogleMap

IOS Code Like This

My old WPF Disciple buddy Colin Eberhardt has a similar post using Objective C for IOS development. May be of interest to some of you : http://www.scottlogic.com/blog/2013/09/20/creating-a-custom-flip-view-controller-transition.html

Sketcher 1 of 3

It has been quite a while since I wrote my last article, I have been busy writing a long series of blob posts on “F# For Beginners“, and doing a lot of reading (which I hope to have more blog posts/articles about very soon).

Anyway we digress, so what is this article about. Well it is a small Angular.js Single Page App, demo app that works with ASP MVC / Web Api / SignalR / Azure storage that I have written that is something like twitter but for images.

The basic idea is that you can:

  • Create sketches (a bit like paint, but no where near as advanced) which are stored in Azure blob storage
  • Choose which friends you want to receive information from (like when they post a new sketch)
  • Comment on other peoples sketches

That is it really, but in essence that is all that Twitter is really, you post some text, people follow you, and they may comment on something you posted.

I just like quite visual things, so decided to do it with images instead.

This is NOT a new idea it has been done a million times before (for example Facebook walls / Reddit etc etc), that is not the point of this article.

The point of this article is that it was something fun I could attempt to write that I could share with others. I think there is a fair bit you could get out of it, if you looked into the code. For example the demo code makes use of the following stuff:

  • Azure blob storage
  • Azure table storage
  • Angular.js Single Page app (SPA)
  • SignalR with Angular.js
  • Using Angular.js with ASP MVC
  • Responsive design Bootstrap web site
  • There is enough meat in it to make it quite a neat demo application

 

If this sounds like it could be of interest, you can read the full text for part 1 right here:

http://www.codeproject.com/Articles/806514/Sketcher-One-Of-n

Attribute Routing

One of the new things that seems to have past me by when working with ASP MVC / Web API is Attribute Routing. This is not intended to be a big blog post, but will show you one reason why Attribute Routing is useful.

 

Imagine we have this Web API controller, which are both valid GET urls

image

Which one of these would be chosen when hitting the ValuesController url : http://localhost:1744/api/values

In fact if you look at this screen shot you will see it is an issue for Web API routing

image

 

Mmmm maybe there is something we can do about this.In fact there is we just need to grab the fairly new Nu-Get package for AttributeRouting.WebApi

Install-Package AttributeRouting.WebApi

 

After you have run this you will get a automatically created VB|C# file that sets up your WebApi project to allow AttributeRouting. It looks like this

image

Now that we have the infrastructure in place we can start to use the new RoutingAttribute types.

 

One of the really useful one is HttpRouteAttribute, which I can use like this:

 

image

 

Which now allows me to do go to this url with ease : http://localhost:1744/api/values/last

Which I personally find much easier than defining loads of custom routings manually. Ok they are still manual, but I do find attributes easier to use.

 

image

 

I have just shown one thing that the new AttributeRouting NuGet package allows. For more information you can see these few posts which are more in depth.

 

Responsive Web Site Design Anyone?

At work I do a mixture of ASP MVC /  WCF and WPF. We have pretty much embraced MVxx frameworks for JavaScript such as Knockout.js and Angular.js, but we recently had to do some truly responsive design web work, which needed to target loads of different form factors. We have good software guys where I work for sure, but none of us is exceptional with CSS, as we prefer to code I guess. So we set about hiring some very UI/Ux people. The first thing that the new UI/Ux guys did was introduce the use of Bootstrap which is a html framework made by some of the guys that work for Twitter.

For those that have not heard of a responsive framework, it is typically one for working with HTML, that has lots of helpers, CSS, JavaScript and well tried and tested patterns for developing web sites that behave in a well known manner on different form factors. These responsive frameworks typically have these sorts of things

  • A responsive grid
  • Navigation menus
  • Popups / Dialogs
  • Standard set of CSS controls / styles

They may have more or less, but these are the basic building blocks of these frameworks.

I had obviously heard of Bootstrap and other big responsive framework Foundation.js. However the new guys went down the Bootstrap route. Now not having played with Bootstrap so much, I decided to have a play around with it, and came up with some code I decided to share.

For those that are interested in this sort of thing you can find the full article here:

 

http://www.codeproject.com/Articles/691182/Responsive-Web-Design-Using-Twitter-Bootstrap

ASP MVC 4 : WEBAPI / UNITOFWORK / RESPOSITORY AND IOC

A while back I published an article which discussed how to use the then WCF web API with UnitOfWork/Respository, and one user asked me how to do this with ASP MVC4, where the WCF Web API has now become the WebAPI, which you can use by creating a new ApiController. Its pretty simple. To do this lets consider a simple ApiController which is as follows:

public class ProductsController : ApiController
{
    private readonly IUnitOfWork unitOfWork;
    private readonly IRepository productRepository;

    public ProductsController(IUnitOfWork unitOfWork, IRepository productRepository)
    {
        if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); }
        if (productRepository == null) { throw new ArgumentNullException("productRepository"); }

        this.unitOfWork = unitOfWork;
        this.productRepository = productRepository;
    }

    public IEnumerable GetAllProducts()
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        return productRepository.FindAll().AsEnumerable();
    }

    public Product GetProduct(int id)
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        Product item = productRepository.FindBy(x => x.Id == id).SingleOrDefault();
        if (item == null)
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
        return item;
    }

    public IEnumerable GetProductsByCategory(string category)
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        return productRepository.FindBy(x=> string.Equals(x.Category, category, StringComparison.OrdinalIgnoreCase));
    }

    public HttpResponseMessage PostProduct(Product item)
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        Product newItem = productRepository.Add(item);
        unitOfWork.Commit();
        var response = Request.CreateResponse(HttpStatusCode.Created, newItem);
        string uri = Url.Link("DefaultApi", new { id = item.Id });
        response.Headers.Location = new Uri(uri);
        return response;
    }

    public void PutProduct(int id, Product product)
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        Product item = productRepository.FindBy(x => x.Id == id).SingleOrDefault();
        if (item != null)
        {
            item.Name = product.Name;
            item.Category = product.Category;
            item.Price = product.Price;
            unitOfWork.Commit();
        }
    }

    public HttpResponseMessage DeleteProduct(int id)
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        Product item = productRepository.FindBy(x => x.Id == id).SingleOrDefault();
        if (item != null)
        {
            productRepository.Remove(item);
            unitOfWork.Commit();
        }
        return new HttpResponseMessage(HttpStatusCode.NoContent);
    }

}

Where you can clearly see this guy takes some dependenices which look like this

UnitOfWork
I am using a Entity Framework code first Unit of work abstraction. The general idea I was going for is that my repositories can involve in a UnitOfWork, and the UnitOfWork is committed when the work is well um done.

public interface IUnitOfWork : IDisposable
{
    void Commit();
    void Attach(T obj) where T : class;
    void Add(T obj) where T : class;
    IQueryable Get() where T : class;
    bool Remove(T item) where T : class;
}
public abstract class EfDataContextBase : DbContext, IUnitOfWork
{
    public IQueryable Get() where T : class
    {
        return Set();
    }

    public bool Remove(T item) where T : class
    {
        try
        {
            Set().Remove(item);
        }
        catch (Exception)
        {
            return false;
        }
        return true;
    }

    public void Commit()
    {
        base.SaveChanges();
    }

    public void Attach(T obj) where T : class
    {
        Set().Attach(obj);
    }

    public void Add(T obj) where T : class
    {
        Set().Add(obj);
    }
}
public class ProductContext : EfDataContextBase, IUnitOfWork
{
    public DbSet Products { get; set; }
}

Repository
I went for a generic repository, but you may find that you do need a specific repository if these simple methods do not satisfy your needs.

public interface IRepository where T : class
{
    void EnrolInUnitOfWork(IUnitOfWork unitOfWork);
    int Count { get; }
    T Add(T item);
    bool Contains(T item);
    void Remove(T item);
    IQueryable FindAll();
    IQueryable FindBy(Func<T, bool> predicate);
}
public class Repository : IRepository where T : class
{
    protected IUnitOfWork context;

    public void EnrolInUnitOfWork(IUnitOfWork unitOfWork)
    {
        this.context = unitOfWork;
    }

    public int Count
    {
        get { return context.Get().Count(); }
    }

    public T Add(T item)
    {
        context.Add(item);
        return item;
    }

    public bool Contains(T item)
    {
        return context.Get().FirstOrDefault(t => t == item) != null;
    }

    public void Remove(T item)
    {
        context.Remove(item);
    }

    public IQueryable FindAll()
    {
        return context.Get();
    }

    public IQueryable FindBy(Func<T, bool> predicate)
    {
        return context.Get().Where(predicate).AsQueryable();
    }
}

 

So how do these get satisfied?

Well that is pretty simple, we just need to have a MVC 4 IDependencyScope/IDependencyResolver inheriting class which looks like this. The important thing to note here is that we are creating a new container to service the request for the scope that we get told about by inheriting from IDependencyScope. I went with this approach rather than child containers, which are deprecated in Castle now. I also went for Castle due to its support for open generics, which I needed to have a single generic repository.

class CastleScopeContainer : IDependencyScope, IDependencyResolver
{
    protected IWindsorContainer container;

    public IDependencyScope BeginScope()
    {
        container = new WindsorContainer().Install(new[] { new ApiInstaller() }); ;
        return this;
    }


    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch(Exception ex)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            var results = container.ResolveAll(serviceType);
            if (results == null)
                return new List<object>();

            return results.Cast<object>();
        }
        catch(Exception ex)
        {
            return new List<object>();
        }

    }

    public void Dispose()
    {
        container.Dispose();
    }
}

Where we configure a standard Castle Windsor IOC container using the following installer(s)

public class ApiInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.Register
        (
            Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>)).LifestyleTransient(),
            Component.For().ImplementedBy().LifestyleTransient(),
            Component.For().LifestyleTransient()
        );
    }
}
public class DDDInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.Register
        (
            Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>)).LifestyleTransient(),
            Component.For().ImplementedBy().LifestyleTransient()
        );
    }
}

And the following Global.asax.cs which is used to setup all the IOC requirements for both the WebApi and the ControllerFactory

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        #region IOC
        //IOC : I like Castle because it has open generics

        //Web Api
        GlobalConfiguration.Configuration.DependencyResolver = new CastleScopeContainer();

        //Controllers
        IWindsorContainer container = new ContainerFactory().Install(
                        new DDDInstaller(),
                        new ControllerInstaller()
                    );
        ControllerBuilder.Current.SetControllerFactory(new CastleControllerFactory(container));
        #endregion

        //Seed database
        Database.SetInitializer(new ProductInitializer());

    }
}

We can also run a standard controller using IOC, which is easily achievable using a ControllerFactory something like the following:

public class CastleControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer container;

    public CastleControllerFactory(IWindsorContainer container)
    {
        this.container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, null);
        }

        object controller= container.Resolve(controllerType);
        if (controller != null)
        {
            return (IController)controller;
        }

        return base.GetControllerInstance(requestContext, null);
    }

    public override void ReleaseController(IController controller)
    {
        var disposable = controller as IDisposable;

        if (disposable != null)
        {
            disposable.Dispose();
        }
        container.Release(controller);
    }
}

Where the ControllerInstaller we previously used in global.asax.cs looks like this

public class ControllerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            AllTypes.FromAssembly(Assembly.GetExecutingAssembly())
                .BasedOn().LifestylePerWebRequest());
    }
}

Where we may have a controller that looks like this, where we also have the goodness of IOC resolves dependencies in our controllers.

public class HomeController : Controller
{
    private readonly IUnitOfWork unitOfWork;
    private readonly IRepository productRepository;

    public HomeController(IUnitOfWork unitOfWork, IRepository productRepository)
    {
        if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); }
        if (productRepository == null) { throw new ArgumentNullException("productRepository"); }

        this.unitOfWork = unitOfWork;
        this.productRepository = productRepository;
    }

    private IEnumerable GetAllProducts()
    {
        productRepository.EnrolInUnitOfWork(unitOfWork);
        return productRepository.FindAll().AsEnumerable();
    }

    public ActionResult Index()
    {
        return View();
    }

    public ActionResult ShowDetails()
    {
        ViewData["products"] = string.Format("There are currently {0} Products", GetAllProducts().Count());
        return View();
    }
}

I havea small demo project which shows a work demo of all of this available at : http://dl.dropbox.com/u/2600965/Blogposts/2012/07/ProductStoreMVC4Demo.zip

Hosted CodeStash Is Out There

Some of you who read my articles over at http://www.codeproject.com might have seen that I have been busy working on a web based snippet tool called “CodeStash”. I recently wrote all about “CodeStash” over at http://www.codeproject.com.

 

“CodeStash” actually comes with a Visual Studio 2010 Addin, which was written by http://www.codeproject.com buddy Pete O’Hanlon, which integrates very nicely with Visual Studio.

We are now very pleased to say that a free hosted version of “CodeStash” is available

 

You can start using it at : http://www.codestash.co.uk. There is also a full PDF help document which outlines how to use  “CodeStash”.

And you can download the addin from here : http://visualstudiogallery.msdn.microsoft.com/8fd9a61e-2b3f-489b-b192-f898b2e0ee53

We will be making making changes to “CodeStash” over time to make it better. Right now the list of improvements has these things on it

  1. Allow users to view snippets without being logged in, and only require login for things that actually require linking to specific user (such as Add snippet/Edit snippet/Create team)
  2. Plugin for Eclipse
  3. Plugin for SharpDevelop
  4. Plugin for Blend
  5. Full Snippet support for VS2010 Addin
  6. Allow users to pick their own theme
  7. Make tooltips non animating

 

Anyway we hope that people start to use this free service, and if you can think of anything you think is missing, please let us know

CodeStash

As some of you may have noticed, I have been neglecting my blog for a while and have not been writing many articles of late. Why is this?

Well I have been very busy working on a productivity tool for developers, which I think is a pretty useful tool

This tool is basically a web based code snippet repository which is completely searchable, and it also comes with a Visual Studio 2010 addin which can be used to add/search for snippets which you can work with directly from the VS2010 editor/context menus.

 

This diagram illustrates the basic idea

image

If you want to know more about this project (which I have to say I am completely chuffed with) you can read more at these links:

Better ASP MVC Select HtmlHelper

As a lot of you who have read stuff I have written about before will know that I am mainly interested in desktop development, WPF in fact. You may have also noticed that my blog has been a bit quite lately, which is due to the fact I am working on a pretty large open source project using ASP MVC 3, which is eating all my spare time, as such I have not been writing as many articles as I had been. The open source project is coming along nicely, and I am getting closer to completing it.

During the course of working on this open source project I have been trying to write ASP MVC 3 code using best practices and have an excellent ASP MVC clued up colleague who answers my dumb questions.

Now the other day I asked him why the standard HtmlHelper.DropDown and HtmlHelper.DropDownFor extension methods require you to use some really filthy objects such as SelectListItem.

Now I don’t know who will be reading this post, but I come from WPF land which kind of paved the way for the ModelViewViewModel  pattern, which ASP MVC seems to borrow for its Model approach these days. I think it is fair to say that most ASP MVC devs will be treating their Models and View specific Models AKA ViewModels.

So we have this Model for the View (AKA ViewModel), which gets bound to a Razor view using ASP MVCs Model Binding (assuming you are using Razor view engine and not just some REST/JSON/JQuery Ajax approach where you miss the view engine out altogether, that’s cool stuff too, but not the subject of this post). Ok so far, but now I want to create a DropDownList (HTML Select tag) using one of the existing HtmlHelper.DropDownList and HtmlHelper.DropDownListFor extension methods. This is where the fun starts, I now have to pollute my nice clean view specific model (ViewModel) with crap which in my opinion, is a view concern.

Ok you could argue that the Model is view specific so what is the harm in having a IEnumerable<SelectList> property in there. Well it just doesn’t sound right to me, and lets push the boat out a bit more, and imagine I want to test my Models. They now have UI Specific stuff in them, so my tests now have to know about UI specific stuff. Which again sounds wrong to me.

Ok I don’t know what your tests look like, but mine have separate test suites for models, than I do for my controller tests. In my test suite where I am testing my controllers I half expect a bit of stuff to do with view specifics such as ViewData, Session, ActionResults etc etc, but in my models I just don’t want any UI stuff in there.

So what can we do about it.

Well the answer is surprisingly simple, we just create our own HtmlHelper which does a better job and allows us to maintain a nice clean model and allows better separation of concerns.

Ok lets have a look at the finished code then shall we. Oh one thing before we start, I am using a standard ASP MVC 3 project that you get when you start a new ASP MVC Razor based project from within Visual Studio 2010, so I hope you are familiar with that and the objects it creates.

The Model

This is dead easy, and just exposes the model data. Notice how I do not have an UI stuff like SelectList in here. Its just standard data. Obviously this will vary for your specific requirements

This is a demo model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Models
{
    public class Person
    {
        public int Id { get; private set; }
        public string Name { get; private set; }
        public Person(int id, string name)
        {
            this.Id = id;
            this.Name = name;

        }

        public override string ToString()
        {
            return String.Format("Id : {0}, Name : {1}", Id, Name);
        }
    }


    public class PeopleViewModel
    {
        public int SelectedPersonId { get; set; }
        public List<Person> Staff { get; set; }

        public PeopleViewModel()
        {

        }
        
        public PeopleViewModel(int selectedPersonId, List<Person> staff)
        {
            this.SelectedPersonId = selectedPersonId;
            this.Staff = staff;
        }
    }


}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

The HtmlHelper

This is where the real work is done, and I came up with 2 approaches which are both shown below. The attached source code has them both in but only one is active, the other is commented out. I will leave it to the user to decide which they prefer.

However one thing both approaches have in common is that they use Expression<Func<T,Object>> to obtain all the information required to create the html select using the models properties. This can be seen below where I show a demo of it within the demo view.

Option 1 : Defer to the standard HtmlHelper

This option defers to the standard HtmlHelper.DropDownList but this new HtmlHelper creates the SelectList(s) for the standard HtmlHelper.DropDownList

 

using System;
using System.Text; 
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using MvcApplication1.Helpers;

namespace MvcApplication1.MyHelpers
{
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Provides a better HtmlHelper dropdown list function
        /// that is type safe and does not fill your model with View specific gunk
        /// </summary>
        /// <typeparam name="TModel">The Model type</typeparam>
        /// <typeparam name="TItem">An item type from the source list</typeparam>
        /// <typeparam name="TValue">A value type for one of the items 
        /// in the source list</typeparam>
        /// <typeparam name="TKey">A text type for one of the items 
        /// in the source list</typeparam>
        /// <param name="htmlHelper">The actual HtmlHelper</param>
        /// <param name="selectedItemExpr">An expression which selects 
        /// the selected item from the model</param>
        /// <param name="enumerableExpr">An expression which selects 
        /// the source list from the model</param>
        /// <param name="valueExpr">An expression that selects a 
        /// value from an item</param>
        /// <param name="keyExpr">An expression that selects text 
        /// value from an item</param>
        /// <param name="htmlAttributes">The Html Attributes to apply 
        /// to the overall select string which gets rendered</param>
        /// <returns>A HTML encoded string, which represents a HTML 
        /// select tag with the attributes provided</returns>
        public static MvcHtmlString ComboFor<TModel, TItem, TValue, TKey>(
            this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TValue>> selectedItemExpr,
            Expression<Func<TModel, IEnumerable<TItem>>> enumerableExpr,
            Expression<Func<TItem, TValue>> valueExpr,
            Expression<Func<TItem, TKey>> keyExpr,
            IDictionary<string, object> htmlAttributes) where TValue : IComparable
        {
            TModel model = (TModel)htmlHelper.ViewData.Model;

            string id = ExpressionUtils.GetPropertyName(selectedItemExpr);

            TValue selectedItem = selectedItemExpr.Compile()(model);
            IEnumerable<TItem> sourceItems = enumerableExpr.Compile()(model);

            Func<TItem, TKey> keyFunc = keyExpr.Compile();
            Func<TItem, TValue> valueFunc = valueExpr.Compile();

            List<SelectListItem> selectList =
                (from item in sourceItems
                 let itemValue = valueFunc(item)
                 let itemKey = keyFunc(item)
                 select new SelectListItem()
                     {
                         Selected = selectedItem.CompareTo(itemValue) == 0,
                         Text = itemKey.ToString(),
                         Value = itemValue.ToString()
                     }).ToList();

            return htmlHelper.DropDownList(
                   id.ToString(), selectList, htmlAttributes);
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

Option 2 : Do all the work ourselves

This option means we MUST do everything that a standard HtmlHelper extension method would do, which means taking care of things like “Name” and “Id” for the generated html, and also ensuring its rendering correctly using a html encoded string

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using MvcApplication1.Helpers;
using System.Text;

namespace MvcApplication1.MyHelpers
{
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Provides a better HtmlHelper dropdown list function
        /// that is type safe and does not fill your model with View specific gunk
        /// </summary>
        /// <typeparam name="TModel">The Model type</typeparam>
        /// <typeparam name="TItem">An item type from the source list</typeparam>
        /// <typeparam name="TValue">A value type for one of the items 
        /// in the source list</typeparam>
        /// <typeparam name="TKey">A text type for one of the items 
        /// in the source list</typeparam>
        /// <param name="htmlHelper">The actual HtmlHelper</param>
        /// <param name="selectedItemExpr">An expression which selects 
        /// the selected item from the model</param>
        /// <param name="enumerableExpr">An expression which selects 
        /// the source list from the model</param>
        /// <param name="valueExpr">An expression that selects a 
        /// value from an item</param>
        /// <param name="keyExpr">An expression that selects text 
        /// value from an item</param>
        /// <param name="htmlAttributes">The Html Attributes to apply 
        /// to the overall select string which gets rendered</param>
        /// <returns>A HTML encoded string, which represents a HTML 
        /// select tag with the attributes provided</returns>
        public static MvcHtmlString ComboFor<TModel, TItem, TValue, TKey>(
            this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TValue>> selectedItemExpr,
            Expression<Func<TModel, IEnumerable<TItem>>> enumerableExpr,
            Expression<Func<TItem, TValue>> valueExpr,
            Expression<Func<TItem, TKey>> keyExpr,
            IDictionary<string, object> htmlAttributes) where TValue : IComparable
        {
            TModel model = (TModel)htmlHelper.ViewData.Model;

            string id = ExpressionUtils.GetPropertyName(selectedItemExpr);

            TValue selectedItem = selectedItemExpr.Compile()(model);
            IEnumerable<TItem> sourceItems = enumerableExpr.Compile()(model);

            Func<TItem, TKey> keyFunc = keyExpr.Compile();
            Func<TItem, TValue> valueFunc = valueExpr.Compile();

 

            TagBuilder selectBuilder = new TagBuilder("select");
            selectBuilder.GenerateId(id.ToString());
            selectBuilder.MergeAttribute("name", id, true);
            selectBuilder.MergeAttributes(htmlAttributes, true);

            StringBuilder optionsBuilder = new StringBuilder();

            foreach (TItem item in sourceItems)
            {
                TagBuilder optionBuilder = new TagBuilder("option");
                object itemValue = valueFunc(item);
                object itemKey = keyFunc(item);

                optionBuilder.MergeAttribute("value", itemValue.ToString());
                optionBuilder.SetInnerText(itemKey.ToString());

                if (selectedItem.CompareTo(itemValue) == 0)
                {
                    optionBuilder.MergeAttribute("selected", "selected");
                }
                optionsBuilder.AppendLine(MvcHtmlString.Create(
                    optionBuilder.ToString(TagRenderMode.Normal)).ToString());
            }

            selectBuilder.InnerHtml = optionsBuilder.ToString();

            MvcHtmlString x = MvcHtmlString.Create(
                selectBuilder.ToString(TagRenderMode.Normal).ToString());

            return x;
        }

    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This code also makes use of this utility class to grab the name of a property from an Expression
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Linq.Expressions;
using System.Reflection;

namespace MvcApplication1.Helpers
{
    public static class ExpressionUtils
    {

        public static string GetPropertyName<TModel, TItem>(
           Expression<Func<TModel, TItem>> propertyExpression)
        {
            var lambda = propertyExpression as LambdaExpression;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = lambda.Body as UnaryExpression;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }
            else
            {
                memberExpression = lambda.Body as MemberExpression;
            }

            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

A Demo View

This is a small demo view that goes with the model/controller supplied at the bottom of this post. Again you will need to change this depending on your own requirements

Note the “using” at the top of the view which lets us include our new HtmlHelper extension method

Also note how we use the new HtmlHelper extension method, the selection of the

  • Source list (The IEnumerable<T>)
  • Option key value
  • Option text value

Is all done using Expression<Func<T,Object>> which is nice a type safe.

@model MvcApplication1.Models.PeopleViewModel
@using MvcApplication1.MyHelpers
@{
    ViewBag.Title = "PeopleView";
}


@using (Html.BeginForm("Create", "Home", FormMethod.Post))
{
    <h2>PeopleView</h2>

    <p>Select an item and click "Submit" button</p>
    
    @Html.ComboFor(
        x => x.SelectedPersonId, 
        x => x.Staff, 
        x => x.Id, 
        x => x.Name,
        new Dictionary<string, object> { { "class", "SomeSelectorClass" } })

    <br />
    <br />
    <br />
 
    if (ViewData["result"] != null)
    {
        <p>@ViewData["result"].ToString()</p>
    }
    
    
    <input id="peopleSubmit" type="submit" value="submit" />    
    
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

So when the HtmlHelper presented with this post does its job this is what the Razor view engine outputs

<select class="SomeSelectorClass" id="SelectedPersonId" name="SelectedPersonId">
<option value="1">Sam Brown</option>
<option selected="selected" value="2">Bob Right</option>
<option value="3">Henry Dune</option>
</select>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

A Demo Controller

This is a small demo controller that goes with the model/controller supplied at the bottom of this post. Again you will need to change this depending on your own requirements

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
      
        public ActionResult Create(PeopleViewModel vm)
        {
            List<Person> staff = new List<Person>();
            staff.Add(new Person(1, "Sam Brown"));
            staff.Add(new Person(2, "Bob Right"));
            staff.Add(new Person(3, "Henry Dune"));

            PeopleViewModel vmnew = 
                new PeopleViewModel(
                    vm.SelectedPersonId, staff);

            ViewData["result"] = 
                string.Format("The selected item was {0}",
                (from x in staff 
                 where x.Id == vm.SelectedPersonId 
                 select x).Single().ToString());
            return View("PeopleView", vmnew);
        }


        public ActionResult PeopleView()
        {

            List<Person> staff = new List<Person>();
            staff.Add(new Person(1, "Sam Brown"));
            staff.Add(new Person(2, "Bob Right"));
            staff.Add(new Person(3, "Henry Dune"));

            PeopleViewModel vm = new PeopleViewModel(2, staff);

            return View("PeopleView", vm);

        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

ScreenShot

image

 

As always here is a small demo project. Enjoy

http://dl.dropbox.com/u/2600965/Blogposts/2011/11/SelectMVC.zip