I'm trying to access the ModelMetaData for a Custom HtmlHelper that I'm working on. The HtmlHelper has a signature like so ...
public static MvcHtmlString DataGrid<T>(this HtmlHelper<T> htmlHelper){}
The (Razor) View looks like this ...
#model IEnumerable<LogMyAssets.Models.ContactModel>
....
#Html.DataGrid()
My problem is that I can't access the ModelMetaData for the Model as it's IEnumerable. I thought I
could do the following:
var model = (IEnumerable<T>)htmlHelper.ViewData.Model;
var metaData = model.ElementAt(0).GetMetadata();
public static ModelMetadata GetMetadata<TModel>(this TModel model)
{
return ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel));
}
But stangely enough I get the following error:
Unable to cast object of type 'System.Collections.Generic.List`1[LMA.Models.ContactModel]'
to type 'System.Collections.Generic.IEnumerable`1[System.Collections.Generic.IEnumerable`1
I though I could could cast from Generic List to Generic IEnumerable. Am I missing something?
I don't really understand where the T is defined in (IEnumerable<T>), however my Guess is that T is already IEnumerable<something>, which means you're trying to cast to IEnumerable<IEnumerable<something>>
Related
I want to create a custom HTML Helper where I can pass a LINQ expression as a parameter, like this:
#Html.GetBackgroundColor(model => model.RiskAssessment)
I want to use it to display some custom css in an MVC view, depending on what the RiskAssessment property is.
So I created a helper method like this:
public static string GetBackgroundColor<T, TResult>(this HtmlHelper<T> htmlHelper, Expression<Func<T, TResult>> expression)
{
...
}
However, that won't compile, the error is IEnumerable does not contain a definition for 'RiskAssessment'
So I changed the method to
public static string GetBackgroundColor<T, TResult>(this HtmlHelper<IEnumerable<T>> htmlHelper, Expression<Func<T, TResult>> expression)
{
...
}
which compiles, so now I presumably have all the objects in the collection but I have no idea how to get the object I want as I can't use use the expression on the IEnumerable, there is no Where() method available. I would have thought I could do something like this:
IEnumerable<T> collection = htmlHelper.ViewData.Model;
T obj = collection.Where(expression)
but I just don't know what I am doing wrong here.
Figured it out, simple mistake. The table header row is set up with #Html.DisplayNameFor(model => modelType), and I was trying to call my custom HTML helper with these parameters. I should have been calling the method on each table row, using #Html.GetBackgroundColor(modelItem => item.RiskAssessment), and this works because I can use htmlHelper.ValueFor(expression) within the method to get the property value.
That said, I have no idea how the header row is generated as Html.DisplayNameFor uses the same method signature as my custom method but Intellisense reports that one of the Expression types is unknown. But that is not an issue for me.
Thanks.
I'm creating my first MVC helper method, but can't quite get it. I want to emit the same code as the standard "#Html.TextBoxFor" but with the attribute "title" and its value taken from the model property. This is what I have so far. You can see I'm trying to just use the standard "TextBoxFor" at the end and add my own attribute to it, but that doesn't seem to be the way to do it.
public static MvcHtmlString TextBoxForWithTitle<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return htmlHelper.TextBoxFor(expression, new { #title = metaData.DisplayName });
}
Specifically, I get a compile time error:
'System.Web.Mvc.HtmlHelper<TModel>' does not contain a definition for 'TextBoxFor' and no extension method 'TextBoxFor' accepting a first argument of type 'System.Web.Mvc.HtmlHelper<TModel>' could be found (are you missing a using directive or an assembly reference?) D:\DevData\JWilliams\Code\Sandbox\URIntakeMVC2\URIntake\HtmlHelpers.cs 16 31 URIntake
The TextBoxFor is an extension method for the HtmlHelper<TModel> class which lives in the System.Web.Mvc.Html namespace.
And if you want to use an extension method you need to explicitly import its namespace with the using directive. So you are missing the using:
using System.Web.Mvc.Html;
And don't forget that you are also creating an extension method here (TextBoxForWithTitle), so when you want to use it in your view you also need to using your own TextBoxForWithTitle methods namespace:
#using URIntakeMVC2.URIntake
So I tried searching, but I'm not exactly sure what to search for. I'm trying to make an HtmlHelper extension that takes a model that is of type MyModel. However, the View takes a model type List(Of MyModel). In my extension, which was originally created to take just MyModel, when the View Model is a List(of MyModel) and I use
For Each Item in Model
Html.MyHelper(Item)
Next
I get an exception 'The model item passed into the dictionary is of type 'System.Collections.Generic.List1[MyModel]'`, but this dictionary requires a model item of type 'MyModel'.'
My original Helper is
Public Function FormatTable(Of T As Class)(helper As HtmlHelper, model As T) as OutModel(Of T)
and i want something like
Public Function FormatTable(Of List(of T As Class))(helper As HtmlHelper, model As T,
tableType As RenderType, Optional columns As Integer = 2) As FormatTable(Of T)
or should i create a new helper in the view to pass?
Although I'm not sure how to do a Function MyExtension(of List(Of T as Class))(model as T) or if that's even possible, I did solve my situation. I needed to create an HtmlHelper of a different class type and use a different model and found MvcUtil which does just that.
I came across this post: Stronglytyped html helper with different model for get and post
In my view i create a new htmlhelper of the new model type to call my htmlextension
For Each Item in Model
#Html.HtmlHelperFor(Of MyType)().MyExtension(Item)
Next
and now have a check
If(IsNothing(helper.ViewData.Model)) Then
helper = helper.HtmlHelperFor(Of T)(model)
End If
This solved my problem when looping through a list in my View.
I want to pass htmlAttributes as parameter to my HtmlHelper similar as it created in
Html.ActionLink("linktext", "Home", null, new{width="100px"})
How to pass this new{width="100px"} to my method
public static string SelectCategoryAdminWithAllItem(this HtmlHelper htmlHelper, string name, **???**)
{ }
and how to parse it?
Thanks
Always try to look at sources when interested with this kind of questions. From the implementation of HtmlHelper.TextBox
public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes)
{
return htmlHelper.TextBox(name, value, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
as you see, type of parameter is object as you cant use anonymous types as parameters to methods, and object is choice. And when parsing it, you can use HtmlHelper.AnonymousObjectToHtmlAttributes Method
I looked through the source for MVC2 when trying to figure this one out. In MVC2 they used an overload of RouteValueDictionary in System.Web.Routing to turn an object to a dictionary rather than having a helper method available like in MVC3.
public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes)
{
return htmlHelper.TextBox(name, value, new RouteValueDictionary(htmlAttributes));
}
A bit counter intuitive but that's the standard in 2.
Edit: Updated tags to include mvc2
Is there a way to use something like this: System.Web.Mvc.ViewUserControl<DateTime>? I get an exception that the type is a value type, not a reference type. What is the proper way to resolve this? Thanks.
Edit
What I am trying to accomplish is having a control that takes a DateTime to render a calendar. I want to pass in the DateTime from my ViewData using "dot notation" for MVC.
Edit 2
I heard/seen that some MvcContrib projects might have this capability, but I can't seem to find it again.
There is no way to resolve this - only workarounds. You cannot include Value types as TModel in ViewUserControl as TModel has a constraint to be a reference type.
The easy workaround is to wrap your value type in a class as your model.
class MyModel {
public DateTime? DateTime {get;set;}
}
By defining your own class like MyModel above, you can now pass a DateTime to your views, like so
ActionResult MyActionMethod() {
var db = new MyDataContext();
var dbThing = db.Things.Where(t=> t.DateTimeProperty>=DateTime.Now).First();
return View("myView", new MyModel{DateTime = dbThing.DateTimeProperty});
}
Your view of course will need to define MyModel as it's model type, like so
public partial class MyView:ViewUserControl<MyModel> {
//snip
}
And inside your View, simply refer to the DateTime property to access the DateTime.
<%=Model.DateTime%>