IEnumerable ViewModel doesn't allow me to access Model properties - c#

I'm having trouble accessing the properties of Model inside my view, when it's an IEnumerable type.
My controller code:
//
// GET: /Home/
public ActionResult Index()
{
List<CourseworkQuestion> courseworkQuestions = repository.GetCourseworkQuestions(repository.GetUsername()).ToList();
List<HomeViewModel> model = new List<HomeViewModel>();
foreach (CourseworkQuestion courseworkQuestion in courseworkQuestions)
{
HomeViewModel hvm = new HomeViewModel(courseworkQuestion);
model.Add(hvm);
}
return View(model);
}
And in my view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<InternalAssessmentViewer.ViewModels.HomeViewModel>>" %>
I'm sure I've done this before, but I'm not sure if I'm missing something obvious.

You need to include a using to get the System.Collections.Generic namespace in your page:
<%# Import namespace="System.Collections.Generic" %>
<%# Import namespace="System.Linq" %> //To get all the LINQ extension methods
Or fully qualify the reference to IEnumerable<T>:
System.Web.Mvc.ViewPage>

Related

ViewBag RuntimeBinderException (MVC 4, ASPX)

I started up a Default MVC 4 project using the ASPX engine (not Razor), and everything works fine.
I can use
<% Model.Name %>
and
<% Model.Rating %>
for book ratings (instead of the Restaurant Review that is up on the asp.net/mvc tutorials site). However, my code errors on the
<% ViewBag.Message %>
...so for some reason, when inheriting from the "BookReviewer" Class within the BookReview model, I can view the Model information but not the Viewbag? Am I missing something? Code below.
[Controller]
HomeController.cs:
public class HomeController : Controller
{
var model = new BookReview()
{
Name = "Pro jQuery For Noobs",
Rating = 7
};
return View(model);
}
[Model]
BookReview.cs
namespace Bookreview.Models
{
public class BookReview
{
public string Name { get; set; }
public int Rating { get; set; }
}
}
[View]
Index.aspx:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<BookRevew.Models.BookReviewer>" %>
<asp:Content ID="indexFeatured" ContentPlaceHolderID="FeaturedContent" runat="server">
<h2>
<% ViewBag.Message %>
<% Model.Name %>
<% Model.Rating %>
</h2>
</asp:content>
As we can see by your comments, you are setting the ViewBag.Message in another controller. The ViewBag is only for data for the view that was set by the controller that served the view.
If you need to send it from another controller, you should use a parameter in your action method, then you can set the ViewBag.Message to the passed in parameter.

How to pass an object from a view based on checkboxes?

i was wondering if its possible to pass an object based on a checkbox selection. Ill explain myself: If there is a selected value, that really means that I need to pass the object that the selection represents in order to use it.
I have this code in my view:
<% foreach (var _client in ViewData["channels"] as List<DigiTV.Models.CANAL>) { %>
<%= Html.CheckBox(_client.NOM_CANAL) %> <%= Html.Encode(_client.NOM_CANAL) %> <br />
<% } %>
As you can see, I have a list of the object type that I want to pass to the controller (List)
Do someone has any suggestions?
thanks
I would very strongly recommend you using view models, strongly typed views and editor templates.
So as always start by defining a view model which will contain all the necessary data your view might need:
public class CanalViewModel
{
public string Name { get; set; }
public bool Selected { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new CanalViewModel { Name = "canal 1", Selected = false },
new CanalViewModel { Name = "canal 2", Selected = true },
new CanalViewModel { Name = "canal 3", Selected = false },
new CanalViewModel { Name = "canal 4", Selected = false },
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<CanalViewModel> model)
{
return View(model);
}
}
and next comes the ~/Views/Home/Index.aspx view:
<%# Page
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<AppName.Models.CanalViewModel>>"
%>
<% using (Html.BeginForm()) { %>
<%= Html.EditorForModel() %>
<input type="submit" value="OK" />
<% } %>
and finally you will need an editor template for a canal which will be executed for each element in the model (~/Views/Home/EditorTemplates/CanalViewModel.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.CanalViewModel>"
%>
<div>
<%= Html.HiddenFor(x => x.Name) %>
<%= Html.CheckBoxFor(x => x.Selected) %>
<%= Html.LabelFor(x => x.Selected, Model.Name) %>
</div>
Now when you submit the form, inside the POST action you will get a list of all canals along with their selected property being depending on which checkboxes the user select.
As you can see we don't need any ViewData which will require you to perform some ugly casts in your views and you don't need to write any foreach loops in your views. Everything is handled automatically by the framework following well established conventions.
you can make the using javascript by collecting all checked checkboxes values(comma separated) in a hidden field then read this values from your controller and split it.
<% foreach (var _client in ViewData["channels"] as List<DigiTV.Models.CANAL>) { %>
<%= Html.CheckBox(_client.NOM_CANAL) %> <%= Html.Encode(_client.NOM_CANAL) %> <br />
<% } %>
<%=Html.HiddenField("AllValues")%>
javascript(I am using jquery)
var allvalues='';
$('input[type=checkbox]').each(function(index){
if($(this).is(':checked'))
{
allvalues+=$(this).val();
}
});
$('#AllValues').val(allvalues);
in you controller
public ActionResult MyAction(FormCollection form)
{
String[] AllValues = form["AllValues"].Split(",");
}

Using the same code in different (partial) views

Maybe this question is quite simple because I'm new to MVC2. I have a simple demo MVC project.
(1) A weak-typed view: Index.aspx
<% Html.RenderPartial("ArticalList", ViewData["AllArticals"] as List<Artical>); %>
(2) A strong-typed partial view: ArticalList.ascx
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<Artical>>" %>
<% foreach (Artical a in Model) { %>
<%= Html.ActionLink(a.Title, "About", new { id = a.ID })%><br />
<%} %>
(3) Here is the HomeController.cs
public ActionResult Index()
{
ViewData["AllArticals"] = Artical.GetArticals();
return View();
}
public ActionResult ArticalList()
{
return PartialView(Artical.GetArticals());
}
Sorry I'm using a Web-Form "angle", because if I'm using a Web-Form, when I visit Index.aspx, rendering ArticalList.ascx will call public ActionResult ArticalList(). But here I need to write Artical.GetArticals() twice in two actions. How can I put them in one?
From what I understand, as a recent newbie in MVC too, is that the partial view does not use a action method in a controller. The "ArticalList" is a reference to the partial view file only and does not make another request for an action method. The partial view gets all of it's data from the view it is called from.
Html.RenderAction might be the behavior you're getting confused with.

ASP.NET MVC ViewData.Model requires casting

I'm working on my first ASP.NET MVC application and have a strange issue. All the tutorials in regards to using strongly typed ViewData don't require casting/eval of ViewData / Model object but I get compilation errors if I don't cast to the ViewData object
ViewData class:
public class CategoryEditViewData
{
public Category category { get; set; }
}
Controller Action:
public ActionResult Edit(int id)
{
Category category = Category.findOneById(id);
CategoryEditViewData ViewData = new CategoryEditViewData();
ViewData.category = category;
return View("Edit", ViewData);
}
Works:
<%=Html.TextBox("name",
((Project.Controllers.CategoryEditViewData)Model).category.Name)) %>
Doesn't Work:
<%=Html.TextBox("name", Model.category.Name)) %>
Is there something that I am doing incorrectly - or do I have to cast to the object in the view all the time?
First, you should move the CategoryEditViewData class out of your controllers namespace, and into your models namespace. Create a new class under the Models folder to see what it should look like. It is good practice to put your models under the models folder.
Then your Control directive should look like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.CategoryEditViewData >" %>
Wait, just thought of something. In your view are you inheriting from you model????
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<namespace.Controllers.YourFormViewModel>" %>

Adding Html Action Links to a List in ASP.NET MVC

I'm trying to add a navigational menu for my project which uses the ASP.NET framework and C# programming language. My solution is to create a widget which can populate a partial view when called from the master page.
In the widget's action method, how do I add Links or Controller-Action combinations to the ViewDataDictionary?
--- Edit : Here is my code, after your suggestion
public class NavController : Controller
{
public ActionResult Menu()
{
List<ActionLink> navLinks = new List<ActionLink>();
navLinks.Add(new ActionLink() { Text = "Home", ActionName = "Index", ControllerName = "Home" });
return View(navLinks);
}
}
The partial view file looks like the following:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<Microsoft.Web.Mvc.Controls.ActionLink>>" %>
<% foreach(var link in Model) { %>
<%= link %>
<%} %>
But the output of all this is simply 'Microsoft.Web.Mvc.Controls.ActionLink' and not the link which I want.
Thanks.
The actionlink is a mvc control of some sort that would be the result of parsing a tag and would then be added to the controls collection of the page.
You have 2 options to achieve your goal, pass in the complete links ( i.e. the fully formed anchor tag ) which you could generate in your controller using Html.ActionLink( linkText, actionName) or pass the details of the link to your partial and use those details to generate the links ( i.e. pass an enumeration of elements containing the details of your links ).
Personally I prefer the second approach as it separates the navigation logic from the rendering.
Somewhere in your code ...
public class NavigationLink
{
public string Text;
public string Controller;
public string Action;
// ... any other properties you want to pass
}
Somewhere in your controller ...
public ActionResult Menu
{
var links = new List<NavigationLink>
{
new NavigationLink
{
Text = "Home",
Controller = "Home",
Action = "Index"
},
new NavigationLink
{
Text = "Logout",
Controller = "Authentication",
Action = "Logout"
}
};
return View( links );
}
Somewhere in your view ...
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<NavigationLink>>" %>
<% foreach(var link in Model) { %>
<%= Html.ActionLink( link.Text, link.Action, link.Controller ) %>
<%} %>
Is it what you're looking for?
List<ActionLink> actions = new List<ActionLink>(); //or = ViewData["actions"] for adding
actions.Add(new ActionLink() { ActionName = "actionname", ControllerName = "controller" });
ViewData["actions"] = actions;
Ok, I figured out a straightforward approach by adding ViewData directly to the dictionary.
public ActionResult Menu()
{
ViewData.Add("one", "Home");
ViewData.Add("two", "Log Off");
return View();
}
In the partial view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<ul>
<% foreach (KeyValuePair<string, object> pair in ViewData) { %>
<li>
<%=pair.Value %>
</li>
<%} %>
</ul>
May not be the most clever way to do this, but it works.

Categories