Home MVC Storefront

ASP.NET MVC Preview: Using The MVC UI Helpers

For the last month or so I've been working on the MVC UI Helpers (aka the MVC Toolkit) that will help developers work with HTML in their Views. These helper methods are primarily Extension Methods, and are designed to encapsulate UI code in much the same way that Server Controls do with Web Forms.

 

It's All Extension Methods Baby
When you create a View using ASP.NET MVC, you set it to inherit from System.Web.Mvc.ViewPage<>, where the passed-in type is a data class shared between the Controller and ViewPage (read more on this here). At that point it's all up the UI (the HTML View), and we haven't forgotten you :). To that end we've created a set of Extension Methods, currently called the "MVC Tookit", that you can use to push the data from your controller to your screen, wrapped in some nice HTML controls.

The UI Helper library (currently called System.Web.Mvc.Toolkit) extends the HtmlHelper class, which hangs from ViewPage.Html. When you download the MVC bits (later this week), in there will be a binary file which you can add to your project in order to use the new UI bits (this stuff is slated to be combined with the core MVC bits in the next drop). To use it, you simply reference the Toolkit dll, and then in your Web.Config, add a reference to the namespace:

<pages>
    ...
    <namespaces>
        <add namespace="System.Web.Mvc"/>
    </namespaces>
</pages>

Now you're ready to rock!

 

Enough Chatter - Get To The Code!
Accessing the helpers from your view is pretty straightforward once you have the namespace available to your ViewPage:

1

As you can see here, there are an abundance of methods for you to choose from - I'll go over each one here, and show some examples of each. You asked for more code... you got it!

First, however, you might be asking:

If I can't use server controls, and there's no PostBack, why then can't I just use plain old HTML?

... and the answer is that you can. It's up to you as to what you think is more maintainable and easier to work with.

For the samples below, I'll be working with a sample Forums application for context.

 

Anonymous Hash (Queue Midnight Run Soundtrack)
When looking over the Toolkit code you'll see a lot of "object htmlAttributes" as an argument in the method signatures. We have a nice hashing system we're using which allows you to pass in things we haven't thought of in terms of tag attributes. This system works on the concept of Anonymous Types - the ability to declare, as needed, an object type:

object o=new{name="Rob", type="quack"}

We have an Extension Method (which you can use too!) called "ToAttributeList()", which will transform this object into a set of attrbute values:

//This evaluates to "name=\"Rob\" type=\"quack\" "
string myList=o.ToAttributeList()

So with all of the methods below, you can use htmlAttributes to send in whatever extra tags you like using the Anonymous Type declaration.

Note: if your tag is a reserved work, like "class", use the standard "_" reserved word prefix: "_class"

 

User Controls
The first helper method I want to show off is the RenderUserControl() method. This method allows you to dynamically call/render a UserControl to a page as a string. These controls can be simple standalones, or they can share the ViewData with the controller. So to render a basic UserControl, we use:

<%=Html.RenderUserControl("~/UserControls/ForumList.ascx")%>

This will output the rendered control to the page, right where I put the call. But what if there are properties set on the ForumList? Like ForumGroupID for instance (which tells the ForumList.ascx which forums to display)? We can handle that too by using an Anonymous Type, sent in as a Hash, which will apply each setting to the UserControl:

<%=Html.RenderUserControl("~/UserControls/ForumList.ascx", new{GroupID=2})%>

If the UserControl is typed (ViewUserControl<MyControllerData>) then the ViewData from the controller will be passed down to the control.

To take it one step farther, let's pretend we're building a Forums application and we want to create a skinning engine which applies certain styles (or "skins") to our application based on a setting somewhere and for this we've decided to leverage UserControls.

Normally we'd have to jump through some hoops (small ones) with the BuildManager and some reflection to get this to work properly (note that that since we're not using Server Controls, we also can't use the ASP.NET Skinning Engine).

With MVC we simply setup a Themes folder, and inside of that put our Theme directories. We can then define a set of user controls for each "skin" or Theme, and then call them as needed, based on the current theme. But how do we know what that theme is? And how do we call it?

Since we don't want all this logic in our UI (deciding on which theme and how to call it), we create a Helper Class that will decide for us what theme we're using. Helpers are essentially static classes, designed to contain the UI logic that otherwise clutters up your UI. Think of these as UI utilities.

Since Theme logic is application-wide, I'm going to create an AppHelper class (as opposed to a helper specific to my controller), and put it in my Helpers folder, using the Global namespace. In here I can now put shared View Logic:

apphelper

And now I can create a method to help me decide which themed control to use. I'll call it GetThemedControl():

/// <summary>
/// Gets the root of the site we're working with
/// </summary>
/// <returns></returns>
public static string GetSiteRoot() {
    
    string appPath = System.Web.HttpContext.Current.Request.ApplicationPath;
    if (appPath == string.Empty)
        appPath = "/";

    return appPath;
}
/// <summary>
/// Gets a user control based on the current theme
/// </summary>
/// <param name="controlName">The name of the control</param>
/// <returns></returns>
public static string GetThemedControl(string controlName) {
    string appPath = GetSiteRoot();
    if (!controlName.EndsWith(".ascx"))
        controlName += ".ascx";
    
    //all themes are kept in ~/Themes/ThemeNAME
    string result = appPath+"Themes/" + GetTheme() + "/" + controlName;
    return result;
}

One thing you'll notice here is that I can no longer call "Page.ResolveClientUrl()" because, well, we have no Page! I'll show you below how you can get around that with our LinkExtensions class.

The GetTheme() method above can do something as simple as check the Web.Config's appSettings, or it can shoot to the DB to see which theme the user likes - this parts up to you.

So using GetThemedControl(), I can now rewrite the call to be:

<%=Html.RenderUserControl(AppHelper.GetThemedControl("ForumList"),new {GroupID=2})%>

Nice and tight :).

 

Forms
Working with forms takes on a whole new meaning with ASP.NET MVC since we are no longer bound to one form per page. For those of you who have used Web Forms almost explicitly, this might come as a bit of a Strange New Thing.

You can work with many forms on a page - there is no limit - and often it's nice to be able to split things up like this. Each form submits itself to a Controller/Action, and that action is responsible for doing whatever needs done.

The toolkit allows you to create forms in a lot of ways, including the most transparent way (using our Url helper):

<form action="<%=Url.Action(new{controller="Home", action="Index"})%> method=post>

If all of the "new{thing=1}" code is freaking you out - this is new to C# 3.5 and it's anonymous typing. Basically when you see this, know that in the background we're setting properties based on what you send in. This is typical in dynamic languages like Ruby, and you'll see this a lot in the code below (specifically when creating HTML attributes for HTML tags).

The <form> tag above works fine, but it's a tad verbose. It's also worth mentioning at this point that

All you know and think about URLs and ASP.NET is out the window. URLs no longer route to a physical file - they are (essentially) Remote Procedure Calls. Given that, you generally don't want to "hard-code" URLs into a page since it makes your site pretty brittle. Use ViewPage.Url to resolve the URL for you.

You can set routes to your controllers as you see fit, and it's very customizable. You're no longer constrained to folder structure so keep that in mind as you work with MVC - you may decide to change your routes as you develop your application - this might very well break your links if you don't use our helpers (or some of your own).

You can also use a nice, type-safe way to create a form (this was Phil's brain child):

<%using(Html.Form<HomeController>(action=>action.Index()))}%>
...
<%}%>

This is a nice way to create a form control since you can work with intellisense, and have type-safety associated with your form tag. It's also nice because you get a compiler error if you don't close off your Form tag :).

If you don't like all that code, you can also do this:

<%using(Html.Form("Home","Index"))%>
...
<%}%>

If you want to use GET, you can (for either methods):

<%using(Html.Form("Home","Index"), FormExtensions.FormMethod.get)%>
...
<%}%>

To submit the form, you can use:

<%=Html.Submit()%>

or

<%=Html.Submit("Save")%>

or use an image

<%=Html.SubmitImage("~/Images/SaveButton.gif")%>

 

Using Form Data In Your Controller
Once you post your form data back, we have a helper that you can use to gather it from Request.Form. There are two such methods:

Object.UpdateFrom(Request.Form)

and

Controller.ReadFromRequest(string key)

UpdateFrom basically introspects your object and then tries to match your object's properties to the keys passed in from Request.Form - in other words "auto-binding" it. So if you're working with the Northwind.Product class (using any object type - it doesn't matter), you could use:

Product p=new Product();
p.UpdateFrom(Request.Form);

This will bind your object to the values passed-in on the form. If there is a type-conversion error (or other error), we wait until the end of the method, and then throw a PopulateTypeException. This exception gathers all the exceptions into a collection for you to evaluate in your code. The important thing here is that if there is an error - the binding doesn't stop, it continues to "do it's best" and then lets you know there was a problem.

Currently we support 3 naming conventions (i.e. the name for your controls in your HTML page) that UpdateFrom() uses to "sniff out" the passed-in form values for binding:

  • Property Name ("ProductName")
  • Object.PropertyName("Product.ProductName")
  • Object_PropertyName("Product_PropertyName")

Controller.ReadFromRequest() is another extension method that you can call in your controller to pull a value that has been passed in using HTTP GET or POST (or the QueryString):

string myName=this.ReadFromRequest("name");

 

Select, ListBox, CheckBoxList, RadioButtonList
We very much wanted to create an experience with these methods much like you had with their Web Form counterparts.

Each control works in much the same way: you specify a name, a data source, selected values, etc. The dataSource, currently, can be:

  • IEnumerable
  • DataSet
  • DataTable
  • IQueryable (Linq)
  • IDataReader

For CheckBoxList and ListBox, the selectedValues parameter is also IEnumerable.

Samples:

<%
//Sample Data
string [] songs=new string[]{"Robot Rock (Daft Punk)","Stairway to Heaven (Zeppeling)", 
    "New Slang (Shins)"};
string [] movies=new string[]{"Tron","Big Trouble In Little China", 
    "Say Anything"};
string [] zodiac =new string[]{"Aries", "Taurus","Gemini","Cancer","Leo",
    "Virgo","Libra","Scorpio","Sag","Capricorn","Aquarius","Haack"};
%>
Sign:<br />
<%=Html.Select("myZodiac",zodiac) %><br />

Sign (select Haacked):<br />
<%=Html.Select("myZodiac",zodiac,"Haacked") %><br />

        
Favorite Movie:<br />
<%=Html.CheckBoxList("favMovie",movies).ToFormattedList("<li>{0}</li>") %>
<br />

Favorite Movie, List :<br />
<%=Html.ListBox("favMovie",movies,new string[]{"Say Anything"}) %>
<br />

Favorite Movie, List, Long, Mult (select "Say Anything" and "Tron")i:<br />
<%=Html.ListBox("favMovie",movies,20,true,new string[]{"Say Anything", "Tron"}) %>
<br />

Favorite Songs (Select Shins):<br />
<%=Html.CheckBoxList("favSongs",songs,new string[]{"New Slang (Shins)"})
    .ToFormattedList("<li>{0}</li>") %>
<br />

Favorite Songs:<br />
<%=Html.CheckBoxList("favSongs", songs).ToFormattedList("<li>{0}</li>")%>
<br />

Favorite Songs, Radio:<br />
<%=Html.RadioButtonList("favSongs", songs,"Robot Rock (Daft Punk)")
    .ToFormattedList("<li>{0}</li>")%>
<br />

One super-groovy thing to note here is how CheckBoxList() calls "ToFormattedList()". This is another Extension Method that hangs off of "IEnumerable" and allows you to enumerate the values to a list.

CheckBoxList and RadioButtonList return String[] - not strings! Each string is a single <checkbox> or <radio> tag.

The reason we did it this way was to allow you as much freedom as possible in terms of laying out your page, but if you don't want to write a foreach loop each time, we included the ToFormattedList() method to make things easier.

 

TextBox, TextArea, Password, Hiddens
These methods work in the same way as above. Here are some samples:

Name:<br />
<%=Html.TextBox("txtName",20) %><br />
Name, with maxlength:<br />
<%=Html.TextBox("txtName","My Value",20,20) %><br />

Thoughts:<br />
<%=Html.TextArea("txtBlob","Lorem Ipsum la la la") %><br />

Thoughts, with maxlength, rows, cols:<br />
<%=Html.TextArea("txtBlob","Lorem Ipsum la la la",20,10,40) %><br />

Password<br />
<%=Html.Password("myPassword",50)%><br />
With size, value
<%=Html.Password("myPassword",50,"My Password")%><br />
With size, value, style attribute
<%=Html.Password("myPassword", 50,"",new { style = "width:100px" })%><br />

Links, Buttons, and Navigation
We augmented the Url.Action() method to include a nice, type-safe way of navigating between controllers and actions on your site:

<%=Html.ActionLink<HomeController>(x=>x.Index(),"Home, using Action<T>") %>

Notice that in the linkText argument (second position, after the Lambda) that I'm outputting <T>? It's worth mentioning that we scrub the settable text bits using Html.Encode (another extension method).

You can also navigate between controllers using Button<>():

<%=Html.Button<HomeController>(x=>x.Index(),"cmdNav2","Home") %>

You can also navigate around the internets using NavigateButton():

<%=Html.NavigateButton("cmdNav","GoTo MSN","http://www.msn.com") %>

If you have some custom javascript to run, you can do that with Button():

<%=Html.Button("cmdJS","Click Me","DoTheClickThang()")%>

If you like using Images as buttons, you can do that too, using ImageButton():

<%=Html.Button("cmdJS","~/Images/Clicky.png","DoTheClickThang()")%>

Note: with this method we make sure the cursor hand appears when hovering to denote a clickable element.

 

Images, ResolveUrl, and MailTo
We really want to make things as easy as can be for you with the new MVC Toolkit. So we've also thrown in an Image method:

A nice picture of a wave:
<%=Html.Image("~/Images/bigwave.jpg") %><br />

Set the name and width
<%=Html.Image("~/Images/bigwave.jpg", "myWave", new { width = "30px" })%><br />

Since you're ViewPage no longer has access to Page proper, we've included a method to help you resolve URLs on your page:

<%=Html.ResolveUrl("~/HALP!")%>

If you want to embed an email link, you can do so using MailTo():

Simple Email:
<%=Html.MailTo("robcon@microsoft.com","Email Me!") %>

With Subject and Body
<%=Html.MailTo("robcon@microsoft.com","Email Me!","Sending you a note"
    ,"Hey - wanted to say hi!") %>

MailTo() contains all the overloads needed to send subject, body, cc, bcc, to the client's email program.

 

Summary
Long, long post - but I thought it was worth it to get as much out there as possible. The CTP will be landing very soon, and with it will be the MVC Toolkit. We've tried to pack as much UI help in there as possible - and please know that these things will likely change as we keep building and tweaking.

As always - would love to hear your thoughts!

PS: YES, there is lots of room for SubSonic here, and I've already started putting together the UI "Sugar" that we're going to offer on top of MVC. Look for that in the next post :).

Dave Savage avatar
Dave Savage says:
Wednesday, December 05, 2007
All I want for Christmas is my MVC!!

Luke Foust avatar
Luke Foust says:
Wednesday, December 05, 2007
I am so jealous you get to work on this! It looks like fun coming up with ways make it easier to output html. Great job so far.

Murad avatar
Murad says:
Wednesday, December 05, 2007
Hi Rob, I see you use Macbook pro for your development, do you do dual boot or use software like Parallel desktop. If you use VM, did you find any performance issues? specially using VS 2005 and how is your experience so far?, Thanks

Jake Scott avatar
Jake Scott says:
Wednesday, December 05, 2007
Hi Rob, I noticed your note about the Html.Button "we make sure the cursor hand appears when hovering to denote a clickable element" surely this would be something that you would worry about in your css and not inline in your html. Are you setting default inline css styles??

Rob Conery avatar
Rob Conery says:
Wednesday, December 05, 2007
Jake: you surely can set this in CSS if you want and use a normal image, etc. This is a convenience method that uses "onmouseover" to set the style. If you wanted to do this with an image, you could: <%=Html.Image("myimage",new{_class="cssImageButton", onclick="myOnClick()"})%> Of particular note is that there are many different ways of doing this, including the tried and true: < img src="myimage" onclick=... class=... >

Eric Hexter avatar
Eric Hexter says:
Wednesday, December 05, 2007
Great to see this work being done, Cant wait till I can get my hands on it! I would like to see a little more support around the typed UserControls being rendered from type Pages. In this case the Page would be typed and the UserControl would be a different type. I would like to see something like this Is that supported ? On another note. I dislike the guidance about have a call to web.config or to a database in the GetTheme() sample. I feel this is going against the entire point of the separation of concerns, and this is a huge benefit of the MVC framework. To have helper methods in the view make calls out to external resources is work that the controller should be performing and passing into the view.

Eric Hexter avatar
Eric Hexter says:
Wednesday, December 05, 2007
So I do not know how to encode comments properly.. here is the example I wanted to show...

Eric Hexter avatar
Eric Hexter says:
Wednesday, December 05, 2007
one more try.. %lt; %=Html.RenderUserControl(“~/UserControls/ForumList.ascx”, new{GroupID=this.ViewData.ForumCollection })% >

Rob Conery avatar
Rob Conery says:
Thursday, December 06, 2007
@Eric - RE the UserControl - this is precisely how it works - no type is required (it's just an added bonus). In terms of the "guidance" RE the skinning thing - sure you can send it down from the Controller - but this IS indeed UI logic and the Controller is biz logic so I set it up that way. Having said that - it's wide open to pass in as you need. Helpers are all about the UI and should know nothing of the controller.

Ibleif avatar
Ibleif says:
Thursday, December 06, 2007
As long as you don't mess with my precious Web Forms! :-)

mike avatar
mike says:
Thursday, December 06, 2007
I kind of like to refer to user controls by name: That's why we can register them in web.config right? Also, that prevents us from having to search/replace in multiple files if we change the name of location of a user control. Please consider, thanks! Reading this, it feels like we can't use webcontrols at all, it's all methods now. Is that true? If so, I'm a bit sad, because I love to declare controls using the HTML-like syntax.

Trevor avatar
Trevor says:
Thursday, December 06, 2007
Hi Rob, If I have the tried and true

Ben avatar
Ben says:
Thursday, December 06, 2007
Great stuff. I hadn't seen anything like the Monorail binder in the MVC framework until now.

Torkel avatar
Torkel says:
Thursday, December 06, 2007
Nice, a CTP is released soon? =) I hope that all this usage of extension methods is not going to make unit testing harder.

Dave Neeley avatar
Dave Neeley says:
Thursday, December 06, 2007
I probably just haven't been developing in .NET long enough, but why MVC is so exciting in comparison to the existing ASP.NET stuff is just not obvious to me yet. Any good resources on that front?

Vijay Santhanam avatar
Vijay Santhanam says:
Thursday, December 06, 2007
It's looking nice Rob. There's some very elegant extension methods. Are postback declarative controls completely dead in MVC? I would like to see postback declarative controls modified to keep drag n drop goodness. I guess they could stay if you could pass them URL building delegates for postback URLs.

Podge avatar
Podge says:
Thursday, December 06, 2007
A couple of questions, How is client side validation done? do you just use the old classic asp way of validation (ie onclick method for the submit button), or is there a helper method for validation. Is caching implemented or do you have to do your own caching? Like a databound listbox or even the page. You mention user controls, how are their postbacks handled, do wrap them up in there own form tag? Final one are the databound controls supported, such as grids, viewlists etc. thanks

Eric avatar
Eric says:
Thursday, December 06, 2007
Hooray!!! Death to the VS.NET designer! So, originally... it seems like Microsoft went the declarative route (with server and HTML controls) in order to enable the use of a WYSIWYG designer and drag-and-drop, but now we are going back (full circle?) to a designerless, code-view only way of doing things. I personally couldn't care less about the designer. I hated it since day one (and the absolute positioning of controls), I just wonder how the masses are going to react when they figure this out? Oh well... they can continue to use WebForms, can't they? Could we get an optimized version of VS.NET with the designer turned off? Unless... I'm totally wrong about this and the designer will still be valid in the MVC bits, but I just cannot imagine it. The designer would have to substitute place holders for all the method calls, or it would have to execute the method and spit out the result. That would make the designer even slower than it is today. I guess that is still a possibility (because that is what the designer is essential doing, calling the Render method of each server or UserControl to get it's HTML markup)...???

Ian avatar
Ian says:
Thursday, December 06, 2007
@Eric: I can't remember the last time I saw the designer in Visual Studio. I am not sure what you mean by absolute positioning of controls either. I believe Scott Guthrie has been depicting MVC as "another option", rather than a replacement for the Web Form model. Either way I do not think designer is required ever.

mike avatar
mike says:
Thursday, December 06, 2007
Wait, what is this designer you are talking about... Just kidding of course, I know it's there, I just never (never ever) use it.

Rob Conery avatar
Rob Conery says:
Thursday, December 06, 2007
@mike: this is where you could use a Helper to get you the control you wanted. You don't want static file references in your app - the architecture is such that URLs can change easily - your app shouldn't break if they do. @Torkel - why would extensions make TDD harder? @Dave - see my next post. Unit Tests are the primary reason, but a clean approach is the other. @Vijay: No, controls aren't dead - but PostBack is. See Scott's post today on using a Reader. In terms of Drag N Drop goodness - it doesn't have to be a server control for that :). @Podge: CSV is done by you :). Or you can put it on the model and serve it back up. Currently we don't have this built in. You can cache the page just like you could before. In terms of PostBack - there is no PostBack so UserControls should really be called "Partials" if you ask me. Yes - databound controls are still supported. @Eric: Microsoft isn't "coming full circle" - this is just an option that some will like, others may not (a lot people like their designer - I'm not one of em - but they're out there).

Jake Scott avatar
Jake Scott says:
Friday, December 07, 2007
Hi Rob, So.. do methods such as Html.Button Html.Image pump out javascript "onclick" and "onmouseover" attributes by default? I prefer to use unobtrusive javascript to attach click and mouseover events to elements. So by default can we leave these "convenience" attributes out, and if you MUST use this technique then you still can by doing Word up though, this is going to dominate RoR! Jake

Rob Conery avatar
Rob Conery says:
Friday, December 07, 2007
@Jake: they do indeed - and as I mention these are just for convenience. If you prefer to use javascript you can always just use good ol HTML (with ID's that YOU define :) and jQuery if you want to wire up some code. If you do this with navigation, you'll want to be sure to use Url.Action() if you nav to another controller (don't hard-code /MyController/ActionName)

Dmitriy Nagirnyak avatar
Dmitriy Nagirnyak says:
Friday, December 07, 2007
Object.UpdateFrom(Request.Form) What if Object has some secure but public properties, like User.HashedPassword? It means that everybody who can update ONE field of User with Object.UpdateFrom(Request.Form) can also update all its properties. It seems such automatic-databinding is unsafe. Just add another hidden field to HTTP request from client and that's all. No way to control it (or is?). As far as I can see - at the end - it will be needed to set object properties manually querying Form, QueryString etc. Workarounds I can see: 1. Give list of properties that *are allowed* to be updated (like in MonoRail). 2. Wrap business objects with their clone-proxies and expose needed binding properties. 3. Set object properties manually. 4. Set some .NET attributes for properties of business objects to recognise it secure prop or not. Disadvantages: 1. If a property is changed, new secure one added etc, it cannot be found at compile time exposing bugs and/or security issues. 2. Too much cloned code. 3. Lots of code to set/convert values. 4. Just a horrible solution. Business objects should never know where they are going to be used. Also secure properties on one page might be allowed to update on the other. Thoughts? Also what about following things (out of the box) in MS MVC: * Caching. * Validation (Client and server). * Handling unhandled exceptions (like Rescues in MonoRail). * Available control suites (like grids with filtering, grouping, paging). Any plans to support ExtJS? Probably answer is NO because of license issues. But just curious. Regards, Dmitriy.

Rob Conery avatar
Rob Conery says:
Friday, December 07, 2007
@Dmitry: What's unsafe is exposing a user password :). Although I see what you mean - there are times when you want to protect your data on your objects from "spoofing". I would say if you're worried about this scenario - you can set the properties "by hand" using ReadFromRequest(). The other thing is to make these properties ReadOnly (which in the case of a password should be the norm). These helper methods are for you to use when applicable. Caching works as always Validation is up to you, as always. Scott has a great post RE LinqToSql and Validation (server-side). SubSonic has this built into it (you can't save something that violates your DB logic). Currently we don't have client-side validators built in, but this may change. We don't intercept any exceptions - that's up to you (I'm not familiar with MonoRail's Rescue). No "control suites" yet - though I think it's pretty simple to setup paging/sorting using MVC. I'll try to put together some posts on this at a later time.

Vijay Santhanam avatar
Vijay Santhanam says:
Friday, December 07, 2007
Why are you using the underscore to prefix C# keywords that are also html attributes? Doesn't C# use @ for that? like @class, @new, @public? What did you mean by the Scott's "Reader" w.r.t. postback controls? Scott demoed the use of PagedList and other useful paging classes in his MVC demo. What other useful paging/sorting helpers are we going to see?

Dmitriy Nagirnyak avatar
Dmitriy Nagirnyak says:
Friday, December 07, 2007
Hi Rob, Thanks for your answer. Looking forward to see some grouping, filtering, sorting etc sample grids. Preferably using one of available grids from Infragistics, DevExpress or Telerik. "I would say if you’re worried about this scenario" - Yes, I am. This basically means that automatic data-binding is not suitable for most applications and IS unsecure. In most of web apps you are going to expose business objects that client has not access to all of its properties. It is not necessaryly read-only prop, but it might be user security role or something like that. BTW, how can it bind lists? If it can do it also it exposes much more security issues. It can navigate and modify all the model. Please pay your attention at that. *I think* you should have a look at http://www.castleproject.org/monorail/documentation/trunk/index.html and get all the best it has. I believe you did, but I really want to have MS MVC better than MonoRail and get all the best from it. Everybody: please give your opinion on MVC here: http://dcportal.argocomputing.com.au/CompleteForm.aspx?form=4:115 Regards, Dmitriy.

Matt Blodgett avatar
Matt Blodgett says:
Friday, December 07, 2007
Rob, When did "Haack" become part of the zodiac? Or should I say, the zodiHAACK? ;)

Josh avatar
Josh says:
Friday, December 07, 2007
So... how about you guys push me out some MVC so I can waste my weekend playing with it. :) :)

Peter avatar
Peter says:
Saturday, December 08, 2007
Hi there, Great article. I would definately like to see some MVC style control methods. In a smilar fashion to server controls. i.e

David Fauber avatar
David Fauber says:
Sunday, December 09, 2007
"If I can’t use server controls, and there’s no PostBack, why then can’t I just use plain old HTML?" Whenever I've had to write methods that return html, I've been tempted to abstract the html elements into an object model. This is one of those things I've gone back and forth on, but have always ended up deciding against doing it. I figured that whoever had to maintain it would probably be someone fluent in html and the abstraction would actually slow them down, even though it seems more correct. Anyway, as geeky as it sounds, I've often wondered how others would handle that situation, so this is really interesting stuff to me.

Liviu avatar
Liviu says:
Monday, December 10, 2007
Hi Rob, Could you please shed some light on a component issue: Let's say i have an ViewUserControl, i would like that logic (and postback ) to be handled in a method of ViewUserControl. Is this possible? Normally i would say that i need a ViewUserController...

Liviu avatar
Liviu says:
Monday, December 10, 2007
Hi Rob, Another thought? Are many of these extension helpers not useless? I mean what is the gain of not having design view for your pages? This was a thing i hated in monorail, and i never used this feature.

David Fauber avatar
David Fauber says:
Monday, December 10, 2007
"I mean what is the gain of not having design view for your pages? This was a thing i hated in monorail, and i never used this feature." There will be division on this one, personally I feel the opposite way although I know many of my coworkers feel the same way you do. I can't stand designer views in general, including the VS html designer. In fact, I scream like I stubbed my toe on something if I accidentally click the "designer" instead of "source" view.

Bob avatar
Bob says:
Monday, December 10, 2007
I've referenced MVCToolkit.dll in to my project, completed the build and tried

Stephen avatar
Stephen says:
Tuesday, December 11, 2007
In terms of these anon types used as 'dictionarys', I kind of like the idea but I feel a little dirty naming propertys with underscores and not being cased correctly.. I guess it doesn't matter at all since the only thing that will ever receive it is the method being called, and it will simply reflect it and build a string.. But aren't attributes suposed to be lower case? couldn't you just ensure that: new { Class = "myclass" }; Came out as class="myclass" ?? Or am I confused and the lower case thing is just an XHTML specific thing regarding elements and attribute case?

Rob Conery avatar
Rob Conery says:
Tuesday, December 11, 2007
@Stephen - don't want to make you feel dirty :). Just updated the source so that all attributes are ToLower() :).

Stephen avatar
Stephen says:
Wednesday, December 12, 2007
Hey great! but now everyone knows who to blame! ;) I need to get the MVC installed and checked out more, I've been wondering the last few days how much the designer (even the html source one) is tied in to the concept of web forms (such as being anal about it generating its own ids for elements).. I mean, I'm interested in trying to create a lightweight control library that is specific to the MVC concept (i.e. not stateful).. I know I could use the webforms ones, but I think they would have more overhead on processing data that isn't useful.. and are designed in a way to take advantage of stateful considerations etc, making them appear somewhat 'odd' in an MVC world.. Not saying the control idea is the way to go, but I think its worth a test.. I was just wondering how low I can get in terms of my own control base, while having the designer based functionality I want, vs what I don't want.. I get the feeling that asp.net is tied into the webforms control system too much, such as the tag registration and such..

Matthew avatar
Matthew says:
Tuesday, December 18, 2007
I can't wait to actually use this. ASP.NET is something I have used for a few years but it hasn't been something I was really excited about until now. Big thanks to all the ASP.NET MVC team.

Santos Ray Victorero, II avatar
Santos Ray Victorero, II says:
Saturday, December 22, 2007
I was taking the MVC Framework for a test drive this weekend and I found a little problem with the TextBox extension method of the MVC UI Helpers. (I apologize if someone already reported this) If the ViewData has any null values it will give an "object not found exception". for example if you pass: and ViewData.DBA is null it will raise the exception. I modified the code to workaround the problem: (See the MODIFIED line below) /// /// Creates a text box for form input /// /// The name and ID of the control /// The text for the control /// The size of the textbox /// The maximum characters allowed in the textbox /// Any attributes you want set on the tag. Use anonymous-type declaration for this: new{class=cssclass} public static string TextBox(this HtmlHelper helper, string htmlName, object value, int size, int maxlength, object htmlAttributes) { string textBoxFormat = ""; string atts = string.Empty; if (htmlAttributes != null) atts = htmlAttributes.ToAttributeList(); if (maxlength > 0) { atts = "maxlength=\"" maxlength.ToString() "\""; } // MODIFIED 12/22/2007 @ 6:30pm value = value==null ? string.Empty : value.ToString(); return string.Format(textBoxFormat, htmlName, size.ToString(), atts, value); } Regards, Santos

jeff avatar
jeff says:
Thursday, December 27, 2007
"Currently we support 3 naming conventions..." * Property Name (”ProductName”) * Object.PropertyName(”Product.ProductName”) * Object_PropertyName(”Product_PropertyName”) What about multi-part naming, like "OrderDetail.Order.Customer.CustomerName"?

jrnail23 avatar
jrnail23 says:
Saturday, December 29, 2007
Hi Rob, Great work on all this stuff... I'm really looking forward to seeing the MVC framework mature into something special. One thing, though... one of the commenters above mentioned casing of HTML attributes and XHTML compliance -- please keep XHTML compliance in mind with these helper methods. I noticed on one of my form tags generated by the helper methods that the form method was rendered as method=post (instead of the XHTML compliant method="post" -- with quotes). One of the big knocks on MS's web technologies has been a lack of adherence to standards, and I'd love to see you guys shed that reputation. Enough whining now, you guys are really doing some impressive work with this MVC product.

Bruce Mcleod avatar
Bruce Mcleod says:
Monday, December 31, 2007
Hang On, I'm going to simply state this once. This approach will never take off if you walk away from Web Controls. UIPAB does not require such an horrific deprecation of user interface functionality and as a result is a better implementation of MVC. It also has a considerable user base which listened to Microsoft evangelists urging adoption of an MVC standard. Where did that get us? :-) Dont go back to .asp!!!!

Dragan Panjkov avatar
Dragan Panjkov says:
Monday, December 31, 2007
Hi, Rob... I am trying to reproduce steps from Scottgu's first part in MVC series... I encountered some problems with Html.ActionLink, I.E. When I use it to render link, it is not converting value returned from Eval to string, for example Html.ActionLink(Eval("CategoryName"), new {action="List", category= Eval("CategoryName")}) will not implicitly cast returned values in eval from object to string, but I need to Explicitly call ToString() on them... Is this feature or bug? Thanks!

Rob Conery avatar
Rob Conery says:
Friday, January 04, 2008
>>>This approach will never take off if you walk away from Web Controls. UIPAB does not require such an horrific deprecation of user interface functionality and as a result is a better implementation of MVC.<<< There are many who believe ASP is not used on many "Web 2.0" - y sites (like Social Networking) because of the abstraction of the UI bits and how "formy" it's all become.

Angus McDonald avatar
Angus McDonald says:
Tuesday, January 08, 2008
Rob, I've followed your samples and have one minor problem ... my VS2008 does not seem to want to recognise the

Angus McDonald avatar
Angus McDonald says:
Tuesday, January 08, 2008
Ugh ... I was meaning to say that it did not recognise the "Html." section of Html.ActionLink, despite my web.config being properly updated. In fact when I compare my code to MvcApplication5 (one of the sample apps) it looks almost identical, but in that project Html. is recognised although intellisense still does not work. I've checked global.asax, web.config and my references and they all seem the same as the sample project. One difference is that I am using VB.NET. Is there anything else I should be looking at? Thanks, Angus

raffaeu avatar
raffaeu says:
Wednesday, January 09, 2008
Hi everyone, you forgot an important tips into the installation steps. You can build an MVC application with NET Framework 2 runtime and stay alive with your old DataModel but if you want to work with the cool ToolKit you must convert the web app in NET 3.5 runtime ... Why don't you build also a Toolkit version for the 2 Framework?

UIPScrewed avatar
UIPScrewed says:
Monday, January 14, 2008
>>> UIPAB does not require such an horrific deprecation of user interface functionality and as a result is a better implementation of MVC. It also has a considerable user base which listened to Microsoft evangelists urging adoption of an MVC standard

Mio avatar
Mio says:
Wednesday, January 16, 2008
Hi Rob, great job with the toolkit. I had only a short time to give it a spin and although it looks really good I did notice a few problems. The drop down list example trying to select Phil Html.Select("myZodiac", zodiac, "Haacked") does not work [ and it's not Phil's fault :) ]. The problem is the method signature (expecting object not a string), and since there is already a method with the same signature as in your example, it gets invoked and nothing gets selected. To get an item selected in a dropdown using current implementation one needs to call it as following: Html.Select("myZodiac", zodiac, (object)"Haacked") Although the above works it doesn’t really look nice, so maybe method signatures need a bit of adjusting. I also noticed that CheckBoxList and RadioButtonList render input tags only, no labels, so selecting items in those lists means chasing small boxes and circles with the mouse. To enable selection also by clicking on the text they should be rendered with 'label for' afterwards and this yields unique IDs for the inputs being rendered. Hope this was helpful. Cheers, Mio

Jeff Handley avatar
Jeff Handley says:
Thursday, January 17, 2008
I am having a slight problem with the CheckBoxList method. I am trying to hand it an IList (or a List) as its data source, and it barfs on me saying "Parameter Count Mismatch". Here's the detail: Parameter count mismatch. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Reflection.TargetParameterCountException: Parameter count mismatch. If I use a string[] instead of a List, it works fine. Any ideas? Great stuff though -- thanks for the help, Jeff Handley

mio avatar
mio says:
Friday, January 18, 2008
@Jeff - this is due to reflection used in System.Web.Mvc.MvcControlDataBinder when PropertyInfo.GetValue is called on indexed properties without the index. Example of indexed default properties is String.Chars. Rob, please correct me if I am wrong here. So if you do have something like IList and you want to pass it to the Html.CheckBoxList just cast it to array: IList _myList = new List { "first", "second", "third" }; Html.CheckBoxList("places", _myList.ToArray()) The following works just fine with passing IList (no need to cast it to an array) IList _theList = new List { new {id="1", value="first value"}, new {id="2", value="second value"} }; Html.RadioButtonList("myFavSongs", _theList, "value", "id") The same of course applies for RadioButtonList. Cheers, m.

Nathan avatar
Nathan says:
Friday, January 18, 2008
I'm using Html.Select, but I need to change the text displayed & value (it's picking up the wrong one by default). I'm passing in the table through ViewData ala ScottGu's tutorial. How do I specify different columns for the textField & valueField? I have this: Html.Select("actvty_typ", ViewData.WorkoutTypes, ViewData.WorkoutTypes.Select(wt => wt.descr), ViewData.WorkoutTypes.Select(wt => wt.actvty_typ).ToString()) And I receive a NullReferenceException. I know there's some kind of LINQy goodness I'm missing... can anyone help? Thanks.

mio avatar
mio says:
Friday, January 18, 2008
@Nathan You should just pass the names of the text and value columns. Try this: Html.Select("actvty_typ", ViewData.WorkoutTypes, "descr", "actvty_typ") Cheers, m.

Nathan avatar
Nathan says:
Friday, January 18, 2008
When I do that, I get a duplicate name error like the original poster received. This happens even when I use "asdlkjgsalgkjg".

mio avatar
mio says:
Sunday, January 20, 2008
@Nathan That’s just plain weird. Could you provide some more info on that ViewData you’re passing in (when you say ‘Table’ I assume you’re talking about System.Data.Linq.Table, right?). I made a small experiment using the blog website that comes with the MVCToolkit, just to try to repro your problem. Nothing happened, it worked just fine. What I did is: 1. added a class for passing the data (ala ScottGu, as you say): public class HomeViewData { public System.Data.Linq.Table Comments { get; set; } } 2. in the HomeController I edited the Index action to pass the HomeViewData to the view. HomeViewData’s ‘Comments’ property gets populated with the comments table from the datacontext: [ControllerAction] public void Index() { BlogDataContext _blogDB = new BlogDataContext(); HomeViewData _homeViewData = new HomeViewData(); _homeViewData.Comments = _blogDB.Comments; RenderView("Index", _homeViewData); } 3. In the code behind of the Index view I changed the class declaration to be public partial class Index : ViewPage and the in the Index.aspx I just added: Works like a charm. When I run this thing I get a drop-down with comment titles as text and with comment ids as values. Cheers, m.

mio avatar
mio says:
Sunday, January 20, 2008
Sorrty, forgot the tags get stripped, 3. should've read: 3. In the code behind of the Index view I changed the class declaration to be public partial class Index : ViewPage<HomeViewData> and the in the Index.aspx I just added: Html.Select("comments", ViewData.Comments, "Title", "ID") Works like a charm. When I run this thing I get a drop-down with comment titles as text and with comment ids as values

Nathan avatar
Nathan says:
Tuesday, January 22, 2008
Thanks everyone. I rebooted and everything worked. I must have had something cached that shouldn't have been.

Juergen avatar
Juergen says:
Wednesday, January 30, 2008
I created a project on codeplex.com, called "Validator Toolkit for ASP.NET". It's a solution to validate a form on client and server-side. You may check out the source code to see if this helps you. http://www.codeplex.com/MvcValidatorToolkit Juergen

southwo8 avatar
southwo8 says:
Tuesday, February 05, 2008
I've got the latest 3.5 Extensions and the MVCToolkit downloaded, but I'm having issues with the generic version of ActionLink The method signature pops up ok in my intellisense, sort of, when I start typing my lambda expression though it can't figure out the type and I get errors when I run the page. Using a Northwind example: Html.ActionLink(p=>p.Edit(p.ProductID), "edit") doesn't work for me, visual studio can't figure out what type "p" is, it just shows "T" as the type in the intellisense popup instead of "ProductsController" so when I type "p." I don't see any of the controller action methods that I have defined....

southwo8 avatar
southwo8 says:
Tuesday, February 05, 2008
ok, the angle brackets didn't come through on my comment, but i'm using the generic Html.ActionLink< > method signature

southwo8 avatar
southwo8 says:
Tuesday, February 05, 2008
as in Html.ActionLink<ProductsController>(p=>p.Edit(p.ProductID), “edit product”) where the "ProductsController" class has a method "public void Edit(int productId)"

Andrei Rinea avatar
Andrei Rinea says:
Saturday, February 09, 2008
I like the MVC Toolkit but there are things I hate about the checkbox generator : 1. The text is rendered directly at the right of the checkbox not within a label for="id". This really sucks from a usability point of view. 2. The checked attribute's value is rendered WITHOUT quoutes (or double quotes) !!! this makes the output not XHTML compliant! It really spoils the joy of the toolkit.

Rob Conery avatar
Rob Conery says:
Saturday, February 09, 2008
@Andrei - the checkbox bits have been fixed in terms of using a label - thanks for the feedback. I also created a XHTML Validation Test for making sure the tags are compliant so this is fixed for the next drop. There are a few other issues (see the forums) that we've also dealt with (I forgot to quote up the form method and close off some text inputs), and more that I found when I started running XHTML validations. Point is - this is a CTP and we've listened to the feedback and it's in there :)

Andrei Rinea avatar
Andrei Rinea says:
Sunday, February 10, 2008
I'm happy to hear that our feedback is useful. :) Any ideea of a date of a next version (preRelease or release)?

Jorgas avatar
Jorgas says:
Tuesday, February 12, 2008
I have a general "question" about type safe c# code in the views. I know that both type safe code, using helper methods, as well as pure html works fine. And that it's up to any developer to choose their own flavor (I probably will use a lot of helper methods for instance). But, in terms of best practices and total project performance in larger projects (where often some people have responsibility for interface design solely), isn't it a bit like fighting part of the purpose of dividing a page into a model, a view and a controller when using a lot of c# code in the aspx-view? I mean, if a developer needs to go over whatever html he receives from an interface designer, and replace pure html with helper methods every time, it seems to me like a lot of time will be added into the project? Or, is there going to be a tool-kit available where html can be converted into type safe code in the views? But again, I do think it's a great thing that you guys give us developers the best of two worlds. :-) Regards,

Jorgas avatar
Jorgas says:
Wednesday, February 13, 2008
Another little "observation", or whish perhaps. When using the helper methods, like for example Html.Form, the action attribute value will always be written out with in the same casing as the corresponding controller and action are named. I think the same goes for Html.ActionLink. Well, a lot of people seems to think that really pretty url's should be in lower case, always. So my question, or whish, is that there will be some configuration setting available in the MVC toolkit or in the standard MVC distribution, that says "lowerCaseUrls=true|false" or something similar. Or, perhaps I have missed some already available setting or way of using the helper methods for this? Regards,

Rob Conery avatar
Rob Conery says:
Wednesday, February 13, 2008
@Jorgas: I've honestly never heard anyone argue for a cased URL. It used to be common practise to make sure your page file names were always lowered because in the good old days the web servers were case sensitive (and some still are).

steve avatar
steve says:
Wednesday, February 13, 2008
Hi, I have a CheckboxList decendent, where I take over the Render method (the base method is not called). For whatever reasons, after selecting some checkboxes and then counting them in code (item.selected = true), I always get zero. Any help would be appreciated. Thanks

Mohammad Azam avatar
Mohammad Azam says:
Saturday, February 16, 2008
Hi Rob, What about if I only wanted to create a list of items using the "li" tag? I don't see an option to that.

Rob Conery avatar
Rob Conery says:
Saturday, February 16, 2008
There is an extension method in there for List and Array called "ToFormattedList()". To use it for list tags, you could: MyList.ToFormattedList("< li >{0} < /li >")

Mohammad Azam avatar
Mohammad Azam says:
Monday, February 18, 2008
Thanks!

Shawn Oster avatar
Shawn Oster says:
Tuesday, February 19, 2008
Hey Rob is there a version of the Html.Form that makes it easy to tack on the needed arguments needed by the controller? My Update controller method needs the id of the record I'm updating and I can't seem to find a clean way to do that using this pattern: … I'm sure the type-safe method might be able to get to it but man is it ever butt-ass ugly and I'm trying to avoid calls that look like keyboard train-wrecks as much as possible :)

Rob Conery avatar
Rob Conery says:
Tuesday, February 19, 2008
@Shawn: You can pass the args right in the lambda. It only looks whacky cause you may not be used to it :) but i I know what you mean... < % using(Html.Form< MyController >(x => x