Wednesday, December 05, 2007 -
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:
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:
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:
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:
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 :).
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=... >
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.
%lt; %=Html.RenderUserControl("~/UserControls/ForumList.ascx", new{GroupID=this.ViewData.ForumCollection })% >
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
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)...???
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...
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.
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?
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..
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
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.
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!!!!
I've followed your samples and have one minor problem ... my VS2008 does not seem to want to recognise the
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
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
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
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
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.
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.
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.
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.
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
Juergen
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.
Any ideea of a date of a next version (preRelease or release)?
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,
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,
.
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 :)
I'm not sure if I explained myself well enough.
I know that most browsers are case insensitive in terms of URL's. What I want is some way of overriding the default behavior of the rendering of Html.Form actions, and Html.ActionLink url's so that they do not use the convention used in the Controllers and Action names. Since those are classes and method names our convention is to use CamelCasing (or PascalCasing) which means that the url's and actions written to the output are also CamelCasing. I would prefer to have some configuration setting available so that the output is written in lowercase always, regardless of what convention individual controllers and action names follow.
Best regards,
I've been working on an article series on my blog related to MVC and the Html Helpers. Don't be upset, but I have found some drawbacks to using the Html Helpers. I'd really love for you to read through the articles (3 posted so far) and let me know what you think about what I'm illustrating as an alternative.
Here's the beginning of the series, which has links to the other articles that have been posted:
http://blog.jeffhandley.com/archive/2008/02/13/custom-controls-everywhere-and-asp.net-mvc-part-0.aspx
Thanks,
Jeff Handley
First, let me apologise for raising this issue on your blog - I'm not sure where else I should raise it.
I've been using ASP.NET MVC Preview 2 and I noticed something odd with CheckBoxes and BindingHelperExtensions.UpdateFrom(...)
The logic is slightly wrong, I think. If a box is checked, then the HTML form will send a value back (albeit an empty one). If the box is *not* checked, then the HTML form will *not* send back a value at all. So your logic falls down because it attempts to parse the value (which will *never* be a valid boolean) and so the value can not be retrieved.
I changed the code (as shown below) which works for me but presumably, this is not the intended behaviour.
---
if (values[httpKey] == null) {
httpKey = objName "_" property.Name;
}
object thisValue = values[httpKey];
if (property.PropertyType == typeof(bool)) {
bool thisBool = (null != thisValue);
property.SetValue(value, thisBool, null);
}
else if (values[httpKey] != null) {
TypeConverter conv = TypeDescriptor.GetConverter(property.PropertyType);
---
Also, I notice that there's quite a bit of difference between your stuff that's in ASP.NET MVC Preview 2, and the MVCToolkit. Which is the most up-to-date? I can't find .ToFormattedList(...) in the Preview 2 stuff which is somewhat tricky for radio and check box lists.
Thanks for the great work, Rob.
TTFN,
Damian.
Note: if your tag is a reserved work, like “class”, use the standard “_” reserved word prefix: “_class”
Resulted in the html having an _ infront of the attribute. Using the @ symbol worked a treat:
Html.ActionLink<MvcTest.Controllers.NewsController>(c => c.Comment(), "Comment", new {@class = "comment"})
Thanks!
I'm new to ASP.NET MVC and i'm working on unit test using .NET 2008 -- VB.NET
Can you please help to resolve this problem! In order to work with session object in unit test project i've created fake session object which is working fine, now what i need is i want to set the value which can read in controller durig debug, like let say in controller i've code somthing like....
PageID = Request("hdnPageID")
This "hdnPageID" actually set in .aspx page with hidden field ie
How can i pass it from TestMethod in unit test?
Thanks in advance
Venkatesh
http://www.altafkhatri.com/Altaf/How-to-get-Ilist-value-from-ASP-net-mvc-postback/Bind-list-to-ASP-NET-MVC-object/Postback-object-list-binding
http://www.altafkhatri.com/Technical/How_to_bind_IList_with_MVC_Dropdownlist_box
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?