Custom DataAnnotation & ViewModels - asp.net

I wrote a custom DataAnnotation that works perfectly fine unless I use a ViewModel rather then the actual Model itself.
With a Model, the data annotation renders something to the effect of:
<label class="tooltip" for="title">Enter a title.</label>
When I use a ViewModel, the data annotation is still rendering the same thing. The problem here is the "for" should be something like "Product_title" instead of "title".
Here's the HTMLHelper I wrote that renders the label from the data annotation:
(Taken from asp.net MVC extending DataAnnotions)
public static MvcHtmlString TooltipFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
var exp = (MemberExpression)expression.Body;
foreach (Attribute attribute in exp.Expression.Type.GetProperty(exp.Member.Name).GetCustomAttributes(false)) {
if (typeof(Tooltip) == attribute.GetType()) {
return MvcHtmlString.Create("<label class=\"tooltip\" for=\"" + exp.Member.Name + "\">" + ((Tooltip)attribute).Description + "</label>");
}
}
return MvcHtmlString.Create("");
}
I dug around in the expression object to figure out which property holds the model property name which would end up being the input ID; "exp.Member.Name".
Now that I'm using a ViewModel, I apparently need something different, because "exp.Member.Name" is still returning "title" instead of "Product_title", which ends up being the ID of the input field.
So, is there a property I can get at that I can use instead of "Member.Name" that will always return the proper ID, or will I have to get around this issue with JavaScript (which I can do if everything else fails)?

I recently implemented something similar, taking the "Description" attribute of the Display annotation and outputting it to the screen:
public static MvcHtmlString DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression) {
var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
var description = metadata.Description;
return MvcHtmlString.Create(description);
}
I output these helpers to tooltip type controls. I considered implementing my own tooltip attribute, but it seemed to be more than I needed...

Related

How to add css to a single mvc Helper method when validation fails - from within the model

Does anyone have a simple way of adding a css class to a html label when validation fails, preferably from within the model, in the public IEnumerable Validate(ValidationContext context) override, not with jQuery or in the Controller.
I have my validationsummary giving me the error message I just want to put * next to the failed input and make its label text bold and red.
#Html.LabelFor(model => model.Name)
<div class="editor-field">
#Html.EditorFor(model => model.Name)<br/><br />
</div>
If you have not yet found a solution, look at http://weblogs.asp.net/imranbaloch/archive/2010/07/03/asp-net-mvc-labelfor-helper-with-htmlattributes.aspx
It codes an HTML Helper extension to LabelFor that supports html attributes. You could use this code as a template to modify for your needs. One option would be to detect whether a validation error has occured. A few days ago I wrote something similar:
public static string IsInvalidFor<TModel, TValue>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TValue>> expression,
string cssErrorClass)
{
if (ValidationExtensions.ValidationMessageFor(htmlHelper, expression) != null)
return cssErrorClass;
else return "";
}
if you want to do it in .cs file Model in this case just append this
string name = //ur name property//;
oppdesc = "";
oppdesc += "<span class ="error"+ "\">" + name+ "</span>";
and u define class error as bold and red in ur css.

Central HTML templates to prevent duplicate presentation code in ASP.NET MVC

In my application I have a lot of UI elements (particuarly buttons) where I am repeating the same code over and over.
For example in multiple views I might have an edit button that is created with the following code:
#Html.ActionLink("Edit", "Edit", "SomeController", null, new { #class="button large blue" });
The problem here is that I am hard coding the label and styling, so if I want to make changes to the button styling or label, I would have to make them in multiple views. This would be tedious to do, as I would have to track down every button.
So now I am looking at creating a templating mechanism, where I can define a button template in a central area and bring it in to any view I want to use it in.
I have considered two options, which I have tried to sketch out in semi-real world code below.
My question is, am I on the right track here? Which option would be better and for which reasons? Is there already something out there I could consider using, or is there another way which I haven't thought of?
Thanks for your help.
Web.Config Templates
Create a custom class that can bring in template configuration from web.config file. For example:
<ui.HtmlTemplates>
<add templateKey="FormCancel" tag="a" class="form-button large black" />
<add templateKey="FormSave" tag="input" type="submit" class="form-button large green" />
</ui.HtmlTemplates>
And then could call them in with syntax such as this (method signature is contrived)
#HtmlTemplates.Build("FormCancel", Url.Action("Index", "Home"))
Partial View Templates
Create strongly typed partial views with the template I want.
ViewModel
public class UiButtonModel
{
public string Url{ get; set; }
}
Partial View
// Assume the file is called "_Button_FormCancel"
#Model path.to.model.directoy.UiButtonModel
Cancel
Use
#Html.Partial("_Button_FormCancel", new UiButtonModel(){Url = Url.Action("Index", "Home"));
Another option is to create extension methods off of HtmlHelper to create prepackaged HTML output using code:
using System.Web.Mvc;
using System.Web.Routing;
public static class MyHtmlExtensions
{
public static string SaveButton(this HtmlHelper helper, string title = "Save", IDictionary<string, object> htmlAttributes = null)
{
var builder = new TagBuilder("button");
builder.Attributes.Add("type", "button");
builder.AddCssClass("form-button");
builder.AddCssClass("large");
builder.AddCssClass("green");
if (htmlAttributes != null) builder.MergeAttributes(htmlAttributes);
builder.SetInnerText(helper.Encode(title));
return builder.ToString();
}
public static string CancelButton(this HtmlHelper helper, string title = "Cancel", string actionName, string controllerName, RouteValueDictionary routeValues = null, IDictionary<string, object> htmlAttributes = null)
{
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection);
var builder = new TagBuilder("a");
builder.Attributes.Add("href", urlHelper.Action(actionName, controllerName, routeValues));
builder.AddCssClass("form-button");
builder.AddCssClass("large");
builder.AddCssClass("green");
if (htmlAttributes != null) builder.MergeAttributes(htmlAttributes);
builder.SetInnerText(helper.Encode(title));
return builder.ToString();
}
}
Then just make sure the namespace of MyHtmlExtensions is either added to your page directly, or included in all pages via web.config, and use it like this in your view (razor syntax):
<div class="form-buttons">
#Html.CancelButton("Index", "Home")
#Html.SaveButton()
</div>
This method is particularly well suited for creating output consistently across several solutions, as all you need to do is reference the containing assembly and import the namespace.
I create these kinds of templates and put them in my Views/Shared folder.
I have templates like:
AddButton.cshtml
DeleteButton.cshtml
SaveButton.cshtml
...
Then, when I need to call one of them in whatever View, I just call this for example:
#Html.Partial("SaveButton");
Using T4MVC, it gets even better with compile time checking (no more literal strings):
#Html.Partial(MVC.Shared.Views.SaveButton)
Doing so I have a common/central place to change a specific button config. No need to go view after view to change something.
This is the problem that css was designed to handle. I fail to understand the problem. If you want to make changes, you change the CSS and it affects all the buttons that have that styling.
Part of your problem is that you're using style like "blue". If you want to change it to red, you have to change it everywhere.
Instead, you should have a class for the button, then you can simply change the button style and you don't have to worry about redefining blue to red.

PartialView as HtmlHelper?

Ok.. here we go.. the weirdest and most confusing question of the month :)
I would like to create a HtmlHelper that some how renders html, but uses a partial view for its template of how the html should be rendered, so to put it more simple.. I would like to do exactly the same as a "normal" Controller and view does.. get some data, pass it to the view and then render the html, but in this case I would like to pass some data to a partial view, and then get the returned html as a string and then return that html from a HtmlHelper method...
In this way I would like to be able to write for instance #Html.HeadMenu, that then would return the html for the headmenu, but I would also be able to at anytime without recompiling be able to change the html.. since its all in a partial view.. and I wont have to worry about any server-side things.. and I will also get the benefit of the intellisense since my method will show up in #Html.
I hope you will understand this..since its kind of hard to explain..
Thanks in advance!
How about the Partial HTML-extension method, it sounds like what you are trying to achive right?
#{
var htmlString = Html.Partial("YourPartialViewName").ToString();
}
It also has an overload so that you can pass a model to the partial view:
#{
var htmlString = Html.Partial("YourPartialViewName", partialViewModel).ToString();
}
You could be looking for the Html.RenderAction(actionName, controllerName, routeValues) method.
I would do it this way
Define data that you imagine to pass to your htmlhelper
public class HeadMenuViewModel
{
public string SomeProperty {get;set;}
}
Define view named HeadMenuViewModel.cshtml in Views/Shared/DisplayTemplates
#model HeadMenuViewModel
<div>
////
</div>
From now, you can display your data using
#Html.DisplayFor(model => model.HeadMenu)
And you could write named shortcut-extension for it
using System.Web.Mvc.Html;
...
public static MvcHtmlString HeadMenu<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
return helper.DisplayFor(expression);
}
Now, change your HeadMenuViewModel.cshtml everytime you need to

specify name of dropdownlist asp.net mvc3 razor

I am wondering why the following code:
#Html.DropDownList("Classification.Nationality.NationalityId", Model.Nationalities, new { #size = 10, #style = "display:none;", #class = "pickList" })
produces the following html, specifically why the name of the element is not "Classification.Nationality.NationalityId".
<select style="display: none;" size="10" name="CollectionCategory.Classification.Nationality.NationalityId" id="CollectionCategory_Classification_Nationality_NationalityId" class="pickList">
where the function signature sure looks like this:
public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, object htmlAttributes);
It seems like the name parameter gots overriden by view model of the parent view. ( This is in a partial view). Does this make sense to anyone?
It's because you are calling this helper inside an editor template or partial for a navigational property called CollectionCategory. It's perfectly normal behavior and ensures that proper value is sent to the controller action when binding. Also I would recommend you using the strongly typed version of this helper to avoid those refactor unfriendly magic strings:
#Html.DropDownListFor(
x => x.Classification.Nationality.NationalityId,
Model.Nationalities,
new {
#size = 10,
#style = "display:none;",
#class = "pickList"
}
)
Of course if you don't want to follow conventions (no idea why wouldn't you) but you could specify a binding prefix in your POST action:
[HttpPost]
public ActionResult Foo([Bind(Prefix = "CollectionCategory")] ClassificationViewModel model)
{
...
}

ASP.NET MVC2: modifying master css property depending on query string parameter

I am migrating a web site to a new one using ASP .NET MVC2.
In the original site, master page has code-behind to check a query string parameter value. Depending on this value, code-behind dynamically modify some CSS property to hide / display master page elements.
As MVC2 has no code-behind because we are supposed to perform everything in the controllers, how should I proceed in this case ?
I see this : asp.net mvc modifying master file from a view
It partially answers my needs but the query string processing is common to all pages. How can I move this processing in a common code section ?
Regards.
A helper method looks like a good place:
public static class HtmlHelperExtensions
{
public static string GetCss(this HtmlHelper htmlHelper)
{
// read some request parameter
// here you also have access to route data so the
// parameter could be part of your custom routes as well
var foo = htmlHelper.ViewContext.HttpContext.Request["foo"];
// based on the value of this parameter
// return the appropriate CSS class
return (foo == "bar") ? "barClass" : "fooClass";
}
}
And somewhere in your master page:
<body class="<%= Html.GetCss() %>">
Or if you are always going to apply it to the body tag only it might be more appropriate to do this in order to reduce the tag soup:
public static class HtmlHelperExtensions
{
public static MvcHtmlString StartBody(this HtmlHelper htmlHelper)
{
var body = new TagBuilder("body");
var foo = htmlHelper.ViewContext.HttpContext.Request["foo"];
var bodyClass = (foo == "bar") ? "barClass" : "fooClass";
body.AddCssClass(bodyClass);
return MvcHtmlString.Create(body.ToString(TagRenderMode.StartTag));
}
}
and in your master page at the place of the body tag:
<%= Html.StartBody() %>
I can think of two solutions to this:
Derive your controllers from one controller base and set the ViewData parameter there depending on posted Form values
Don't use ViewData at all, but simply look for the form value in the view (using HttpContext.Current)
The second method violates the MVC pattern. IMO it is still acceptable in some scenarios, for example I am using this approach to highlight the currently selected item in a navigation menu.

Resources