When a controller renders a view based on a model you can get the properties from the ViewData collection using the indexer (ie. ViewData["Property"]). However, I have a shared user control that I tried to call using the following:
return View("Message", new { DisplayMessage = "This is a test" });
and on my Message control I had this:
<%= ViewData["DisplayMessage"] %>
I would think this would render the DisplayMessage correctly, however, null is being returned. After a heavy dose of tinkering around, I finally created a "MessageData" class in order to strongly type my user control:
public class MessageControl : ViewUserControl<MessageData>
and now this call works:
return View("Message", new MessageData() { DisplayMessage = "This is a test" });
and can be displayed like this:
<%= ViewData.Model.DisplayMessage %>
Why wouldn't the DisplayMessage property be added to the ViewData (ie. ViewData["DisplayMessage"]) collection without strong typing the user control? Is this by design? Wouldn't it make sense that ViewData would contain a key for "DisplayMessage"?
The method
ViewData.Eval("DisplayMessage")
should work for you.
Of course after I create this question I immediately find the answer after a few more searches on Google
http://forums.asp.net/t/1197059.aspx
Apparently this happens because of the wrapper class. Even so, it seems like any property passed should get added to the ViewData collection by default.
I really need to stop answering my own questions :(
Related
Currently I have this AcceptedProposals view, which is meant to show the details of a list of proposals, which are coming from the Entity Framework. Here's the code I have in my controller:
public ActionResult AcceptedProposals()
{
var proposals = db.Proposals.Where(p => p.CollegeFundDecision == true);
return View(proposals);
}
In my view I have the following line, but it's giving me this error:
System.InvalidOperationException: The model item passed into the
dictionary is of type
'System.Data.Objects.ObjectQuery`1[URGLibrary.Proposal]', but this
dictionary requires a model item of type 'URGLibrary.Proposal'.
#(Html.Telerik().Grid<Proposal>((IEnumerable<Proposal>)Model))
Any idea how I would be able to this this grid to show up properly? And further more, once I get this working, if I wanted to make it so that there's a dropdown of years to choose what year of proposals I'm looking at, would I be putting this grid code into a partial view?
Thanks for the help!
You havent enumerated your set prior to passing it to the view. Its good practice to use .ToArray or .ToList to pre-enumerate your set in the controller so that you cant accidentally add bits to it in the view. This means your action should look like this:
public ActionResult AcceptedProposals()
{
var proposals = db.Proposals.Where(p => p.CollegeFundDecision == true).ToArray();
return View(proposals);
}
This relates to the ObjectQuery part of the error (a collection which hasnt yet been finalised and retrieved from the database yet).
Try this and let me know if it resolves the issue or gives you a simpler error message.
I am trying to parse data from my ViewData but its not going so well.
Controller:
public ActionResult ListFilm()
{
MediaCatalog mediaCL = new MediaCatalog();
// Retrieve a list of film media's
List<CatalogDb.FilmMedia> listFilmMedia = new List<CatalogDb.FilmMedia>();
listFilmMedia = mediaCL.GetAllFilmMediaTitles();
ViewData["FilmList"] = listFilmMedia.ToList();
return View();
}
View:
<% foreach(var item in (ViewData["FilmList"] as List<CatalogDb.FilmMedia>)) { %>
<%=item.title %>
<% } %>
I noticed that the code written in my view file is parsed directly in the source, so for some reason its not being executed as code?
Since you are using razor view engine, your syntax is incorrect and your code is not parsed.
In razor view engine you would have to write something like
#foreach (var item in (ViewData["FilmList"] as List<CatalogDb.FilmMedia>))
{
#item.title
}
What you've written there is web forms view engine syntax. You could either switch to that view engine or write code in razor syntax for your code to be parsed and executed.
I personally use the ViewBag property, it has been added to ASP.NET MVC3. It uses the dynamic under the hood, which makes the ViewBag also strongly typed. (ViewData uses a dictionary under the hood and thus always returns an object , which you have to cast).
So basically, instead of using ViewData["MyPropery"] as MyType, you can directly call ViewBag.MyProperty and you don't need to cast it (to "MyType").
Good day!
ASP.NET MVC makes a good job by storing values of inputs during GET/POST cycle inside ModelState and automagically putting them into inputs in case of validation errors.
But on my form I have CAPTCHA field which shouldn't be preserved during validation errors (CAPTCHA value is regenerated on each request).
I've tried to achieve this by setting
if (TryUpdateModel(model))
{
// ...
}
else
{
ModelState.Remove("CaptchaValue"); // ModelState does have CaptchaValue
return View(model); // CaptchaValue is empty in model
}
But it doesn't work.
May be there is an attribute which I can apply to my model field to prevent it from preserve in ModelState?
Thanks in advance!
You can use the bind attribute on the action parameter to control model binding behaviour:
public ActionResult YourActionName([Bind(Exclude = "CaptchaValue")]ModelType model)
I've found this in nearby thread MVC - How to change the value of a textbox in a post?:
ModelState.SetModelValue("CaptchaValue", new ValueProviderResult(String.Empty, String.Empty, System.Threading.Thread.CurrentThread.CurrentCulture));
But it seems to be a bit ugly.
I am trying to understand the best way of implementing a DropDownList in ASP.NET MVC 2 using the DropDownListFor helper. This is a multi-part question.
First, what is the best way to pass the list data to the view?
Pass the list in your model with a SelectList property that contains the data
Pass the list in via ViewData
How do I get a blank value in the DropDownList? Should I build it into the SelectList when I am creating it or is there some other means to tell the helper to auto create an empty value?
Lastly, if for some reason there is a server side error and I need to redisplay the screen with the DropDownList, do I need to fetch the list values again to pass into the view model? This data is not maintained between posts (at least not when I pass it via my view model) so I was going to just fetch it again (it's cached). Am I going about this correctly?
Your best bet is to create a SelectList in your Controller - use my extension method here:
http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc
Pop that into ViewData using the same key as your property name:
ViewData["statusid"]=MySelectList
Then just use Html.DropDownFor(x=>x.StatusID) and you're all set.
Answering in parts:
The best way IMHO is to pass the list in the ViewModel like this:
public SelectList Colors
{
get
{
// Getting a list of Colors from the database for example...
List<Color> colors = GetColors().ToList();
// Returning a SelectList to be used on the View side
return new SelectList(colors, "Value", "Name");
}
}
To get a blank or default option like ( -- Pick a color -- ), you can do this on the view side:
#Html.DropDownListFor(m => m.Color, Model.Colors, "-- Pick a color --")
You'll have to fetch/populate the list again if it's part of the ViewModel.
Take a look at the following blog post. It can give you some tips:
Drop-down Lists and ASP.NET MVC
You could do something like:
<%= Html.DropDownListFor((x => x.ListItems), Model.ListItems, "")%>
or
<%= Html.DropDownList("ListItems", Model.ListItems, "")%>
The last param 'optionLabel' makes a blank list item
In this case, you can see ListItems is a property of the model.
I have made the view strongly typed to the model also.
(You know this already!)
Pass the list in your model with a SelectList property that contains the data
Yes, add it when you build the SelectList. (If you build the list using LINQ, Union might come in handy.)
Yes do do, and yes you are.
I find it more intuitive to work with a sequence of SelectListItems (rather than a SelectList).
For example, this would create an IEnumerable<SelectListItem> from a sequence of customer objects that you can pass to the Html.DropDownListFor(...) helper. The 'Selected' property will optionally set the default item in the dropdown list.
var customers = ... // Get Customers
var items = customers.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCustomerId),
Text = c.Email,
Value = c.Id.ToString()
});
I use ASP.NET MVC for serving web application and I want to create something like the following code.
<% using(HTML.Form(Model)) { %>
<% HTML.CreateTextBox('txt1', x => x.Property1);
<% }
From the above code, Form extension method will receive object that represent type of current Model object in current View page. Next, CreateTextBox method will receive type from Form method and I can bind textbox to some property of this model.
Update 1
The following code is code of CreateTextBox method that will create instance of TextBox class.
public static CreateTextBox(string value, Expression<Func<object>> bindedProperty)
{
// T should be receive from HTML.Form method or class
return new TextBox<T>(value);
}
Is it possible to creating some code that doing something like the above code?
Thanks,
It's not necessary. The model is always going to be available, so you will always be able to do this:
<%
using(Html.BeginForm())
{
Html.TextBox('txt1', Model.Property1);
}
%>
The Model property on your page is always typed (assuming you are using System.Web.Mvc.ViewPage<T>) so you will always have access to the properties directly through the Model property.
If you aren't using the generic version (System.Web.Mvc.ViewPage<T>) then you should probably use that instead of the non-generic version.