I develop a web application using ASP.NET MVC4 in VS2012, in that a HomeController as Views/Home/Index.aspx.
I created a user control as UC/UC_Menu.ascx in Views Folder.
In Index.aspx page, I coded in below and run ok.
<% Html.RenderPartial("~/Views/UC/UC_Menu.ascx"); %>
Now, I want to UC/UC_Menu.ascx load data in database. How could I do?
In ASP.NET MVC, views and partials are not supposed to be retrieving any data. They are designed for displaying data that has been retrieved by the corresponding controller under the form of a view model.
So basically you should create a view model that will be a projection of your data. For example:
public class MyViewModel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
and then have your controller action fetch the data from the database and project it to the view model:
public class HomeController: Controller
{
public ActionResult Index()
{
MyViewModel model = ... go fetch from db
return View(model);
}
}
and now your Index.aspx view will be strongly typed to this view model and pass the model to your partial view:
<%# Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<MyNs.MyViewModel>" %>
and then pass the model to the partial for displaying:
<% Html.RenderPartial("~/Views/UC/UC_Menu.ascx", Model); %>
and your partial can now display the data:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyNs.MyViewModel>" %>
<%= Html.DisplayFor(x => x.Foo) %>
<%= Html.DisplayFor(x => x.Bar) %>
Invoking "Child Actions" gives you encapsulation similar to "UserControls".
Note: "UserControls" is a term used in ASP.NET Web Forms. In MVC, you would call it a "PartialView".
You could create a child action on your controller, for example:
public class HomeController : Controller
{
[ChildActionOnly]
public ActionResult Menu()
{
var model = BuildModelFromDB();
return PartialView(model);
}
}
Then invoke it in your view, passing in the ActionName and ControllerName:
<%= Html.Action("Menu", "Home") %>
Related
I have a asp.net mvc application. One of it's controller calls an aspx page instead of normal razor view page. I followed this https://www.hanselman.com/blog/MixingRazorViewsAndWebFormsMasterPagesWithASPNETMVC3.aspx
Now the problem is I need to send some data from my controller to that aspx page which I can't do using Viewbag.
Any idea how can I send data from my regular controller to my aspx page?
Passing data using model to view(aspx engine) from controller:
Model:
public class Product
{
public string ProductID { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public int Price {get; set;}
}
Controller:
public ActionResult Index()
{
List<Product> productLst = new List<Product>{
new Product{ProductID="P01",ProductName="Pen",Quantity=10,Price=12},
new Product{ProductID="P02",ProductName="Copy",Quantity=12,Price=20},
new Product{ProductID="P03",ProductName="Pencil",Quantity=15,Price=22},
new Product{ProductID="P04",ProductName="Eraser",Quantity=20,Price=27}
ViewData["Message"] = "Your message comes here";
return View();
}
ASPX View:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Product>" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Index</title>
</head>
<body>
<div>
<h3>Passing Data From Controller To View using ViewData</h3>
<h3><%= Html.Encode(ViewData["Message"]) %></h3>
<%foreach (var item in Model)
{ %>
<p><%=item.ProductID %></p>
<p><%=item.ProductName %></p>
<p><%=item.Quantity %></p>
<p><%=item.Price %></p>
<%} %>
</div>
</body>
</html>
References for model binding in aspx engine,
https://www.codeproject.com/Articles/391289/Implementing-ASP-NET-MVC-Views-in-three-different
https://weblogs.asp.net/gunnarpeipman/asp-net-mvc-3-using-multiple-view-engines-in-same-project
Assign dynamic HTML attribute for any HTML tags:
<input checked="#isRazor" type="checkbox"><!-- Razor engine -->
<input checked="<%:isASPX%>" type="checkbox"><!-- ASPX engine -->
You can do more mixing of HTML tags with Razor and ASPX View Engine, Following code block shows how you can do that.
Your Sample Html Code or Text #RazorCode (#AnotherRazorCode)
Your Sample Html Code or Text <%: ASPXCode %> (<%:AnotherASPXCode %>)
Instead of that, You can go with creating your own Custom Model to passing the data from Controller to View.
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<YourModel>" %>
<% foreach(var item in Model) { %>
<tr>
<td><%: item.Name %></td>
</tr>
Have you tried passing it through ViewData, like so?
in the action: ViewData["myvar"] = "realvalue";
in the view: string parl = ViewData["myvar"];
NOTE: Alternatively you can use Session like this example:
in MVC action:
Session["UserName"] = "Test";
in WebForms:
string UserName = (string)Session["UserName"];
That is all!
I very new to .NET and Entity Framework, and I have a problem with my code (below). I am getting the following error:
Unable to cast object of type '<>f__AnonymousType1`2[
SamWinInterface.Models.tbl_interface_category,
SamWinInterface.Models.tbl_interface_menu]' to type
'SamWinInterface.Models.tbl_interface_menu'.
This is my code:
public ActionResult Index(int id=-1)
{
ViewBag.Menus = from menu in _db.tbl_interface_menu
join cat in _db.tbl_interface_category on
menu.fld_category_id equals cat.id where
cat.fld_customer_id == id select new { cat, menu };
return View();
}
I'm trying to get menus depending on which category is chosen.
Something like:
<% foreach (tbl_interface_menu m in (IEnumerable)ViewBag.Menus)
{ %>
<%= m.fld_section2_title %>
<% } %>
but I'm getting the above error. How can I get the menus?
You cannot pass anonymous objects to views. This doesn't work because anonymous types are emitted as internal. And because ASP.NET views are compiled into a separate assembly at runtime they cannot access those times because they reside in a different assembly. This basically means that an anonymous object that you have defined in your controller action cannot be accessed in your view.
So as always in an ASP.NET MVC application start by defining view a model:
public class MyViewModel
{
public Category Category { get; set; }
public Menu Menu { get; set; }
}
then have your controller action fill this view model and pass it to the view:
public ActionResult Index(int id=-1)
{
var model =
from menu in _db.tbl_interface_menu
join cat in _db.tbl_interface_category
on menu.fld_category_id equals cat.id
where cat.fld_customer_id == id
select new MyViewModel { Category = cat, Menu = menu };
return View(model);
}
and finally have a strongly typed view:
<%# Page
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<AppName.Models.MyViewModel>>"
%>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach (var item in Model) { %>
<%= item.Menu.fld_section2_title %>
<% } %>
</asp:Content>
As Darin said, you cannot pass anonymous types to views, but you could convert them to Expando objects, and that would prevent you from having to define viewmodels.
Personally I would probably just define viewmodels, but this option is handy in a pinch.
I created a drop down list in a partial view and I am trying to render that on my aspx page. I am getting an error:
{"Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'."}
This is my aspx page where I am using the ascx control:
<td>
<% Html.RenderAction("getFilterdData");%>
</td>
My ascx control looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewPage<IEnumerable<assist>>" %>
<%=Html.DropDownList("Assists", (SelectList)ViewData["Assists"], "--Select One--")%>
and my controller code is like this:
public ActionResult getFilterdData()
{
scorerep sc = new scorerep();
ViewData["Assists"] = new SelectList(sc.FilterData(), "assist_a","");
return View();
}
Why am I getting this error and how can I fix it?
It is difficult to help without seeing the entire exception stacktrace. Here are a few tips:
Make sure that your partial Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<assist>>" and not Inherits="System.Web.Mvc.ViewPage<IEnumerable<assist>>". You are using an ASCX partial and inheriting from System.Web.Mvc.ViewPage which is wrong.
Make sure that your partial view is called exactly the same as the controller action: getFilterdData.ascx (I see a typo here)
Make sure that the Assist class contains a property called assist_a as that's what you are using when rendering the dropdown
Make sure there is no exception being thrown inside the getFilterdData controller action while you are fetching the data.
Here's a working example:
Model:
public class Assist
{
public string Id { get; set; }
public string Value { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult GetFilteredData()
{
// TODO: replace with your repository logic
ViewData["Assists"] = new SelectList(new[] {
new Assist { Id = "1", Value = "Assist 1" },
new Assist { Id = "2", Value = "Assist 2" },
new Assist { Id = "3", Value = "Assist 3" },
}, "Id", "Value");
return View();
}
}
View (~/Views/Home/Index.aspx):
<% Html.RenderAction("GetFilteredData"); %>
Partial: (~/Views/Home/GetFilteredData.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Assist>>" %>
<%= Html.DropDownList("Assists", (SelectList)ViewData["Assists"], "--Select One--") %>
I have a view model with a collection of other objects in it.
public ParentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildViewModel> Child { get; set; }
}
public ChildViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
}
In one of my views I pass in a ParentViewModel as the model, and then use
<%: Html.EditorFor(x => x) %>
Which display a form for the Id and Name properties.
When the user clicks a button I call an action via Ajax to load in a partial view which takes a collection of Child:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
<%: Html.EditorFor(x => x) %>
which then uses the custom template Child to display a form for each Child passed in.
The problem I'm having is that the form created by the Child custom template does not use the naming conventions used by the DefaultModelBinder.
ie the field name is (when loaded by Ajax):
[0].FirstName
instead of:
Child[0].FirstName
So the Edit action in my controller:
[HttpPost]
public virtual ActionResult Edit(int id, FormCollection formValues)
{
ParentViewModel parent = new ParentViewModel();
UpdateModel(parent);
return View(parent);
}
to recreate a ParentViewModel from the submitted form does not work.
I'm wondering what the best way to accomplish loading in Custom Templates via Ajax and then being able to use UpdateModel is.
Couple of things to start with is that you need to remember the default ModelBinder is recursive and it will try and work out what it needs to do ... so quite clever. The other thing to remember is you don't need to use the html helpers, actual html works fine as well :-)
So, first with the Model, nothing different here ..
public class ParentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildViewModel> Child { get; set; }
}
public class ChildViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
}
Parent partial view - this takes an instance of the ParentViewModel
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ParentViewModel>" %>
<h2>Parent</h2>
<%: Html.TextBox("parent.Name", Model.Name) %>
<%: Html.Hidden("parent.Id", Model.Id) %>
<% foreach (ChildViewModel childViewModel in Model.Child)
{
Html.RenderPartial("Child", childViewModel);
}
%>
Child partial view - this takes a single instance of the ChildViewModel
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ChildViewModel>" %>
<h3>Child</h3>
<%: Html.Hidden("parent.Child.index", Model.Id) %>
<%: Html.Hidden(string.Format("parent.Child[{0}].Id", Model.Id), Model.Id)%>
<%: Html.TextBox(string.Format("parent.Child[{0}].FirstName", Model.Id), Model.FirstName) %>
Something to note at this point is that the index value is what is used for working out the unique record in the list. This does not need to be incremental value.
So, how do you call this? Well in the Index action which is going to display the data it needs to be passed in. I have setup some demo data and returned it in the ViewData dictionary to the index view.
So controller action ...
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Parent"] = GetData();
return View();
}
private ParentViewModel GetData()
{
var result = new ParentViewModel
{
Id = 1,
Name = "Parent name",
Child = new List<ChildViewModel>
{
new ChildViewModel {Id = 2, FirstName = "first child"},
new ChildViewModel {Id = 3, FirstName = "second child"}
}
};
return result;
}
In the real world you would call a data service etc.
And finally the contents of the Index view:
<form action="<%: Url.Action("Edit") %>" method="post">
<% if (ViewData["Parent"] != null) { %>
<%
Html.RenderPartial("Parent", ViewData["Parent"]); %>
<% } %>
<input type="submit" />
</form>
Saving
So now we have the data displayed how do we get it back into an action? Well this is something which the default model binder will do for you on simple data types in relatively complex formations. So you can setup the basic format of the action which you want to post to as:
[HttpPost]
public ActionResult Edit(ParentViewModel parent)
{
}
This will give you the updated details with the original ids (from the hidden fields) so you can update/edit as required.
New children through Ajax
You mentioned in your question loading in custom templates via ajax, do you mean how to give the user an option of adding in another child without postback?
If so, you do something like this ...
Add action - Need an action which will return a new ChildViewModel
[HttpPost]
public ActionResult Add()
{
var result = new ChildViewModel();
result.Id = 4;
result.FirstName = "** to update **";
return View("Child", result);
}
I've given it an id for easy of demo purposes.
You then need a way of calling the code, so the only view you need to update is the main Index view. This will include the javascript to get the action result, the link to call the code and a target HTML tag for the html to be appended to. Also don't forget to add your reference to jQuery in the master page or at the top of the view.
Index view - updated!
<script type="text/javascript">
function add() {
$.ajax(
{
type: "POST",
url: "<%: Url.Action("Add", "Home") %>",
success: function(result) {
$('#newchild').after(result);
},
error: function(req, status, error) {
}
});
}
</script>
<form action="<%: Url.Action("Edit") %>" method="post">
<% if (ViewData["Parent"] != null) { %>
<%
Html.RenderPartial("Parent", ViewData["Parent"]); %>
<% } %>
<div id="newchild"></div>
<br /><br />
<input type="submit" /> add child
</form>
This will call the add action, and append the response when it returns to the newChild div above the submit button.
I hope the long post is useful.
Enjoy :-)
Hmm... i personally would recommend to use a JSON result, instead of a HTML result, that you fiddle in the page...
makes the system cleaner. and your postback working ;-)
I found another way to accomplish this which works in my particular situation.
Instead of loading in a partial via via Ajax that is strongly typed to a child collection like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
I created a strongly typed view to the parent type and then called EditorFor on the list like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Parent>" %>
<%: Html.EditorFor(x => x.ChildList) %>
This then calls a Custom Display Template and the result is that all the HTML elements get named correctly and the Default Model binder can put everything back together.
In ASP.Net MVC I would like to render a different partial view depending on the renderview query string parameter.
Therefore providing the facility for the user to choose to view products by thumbnail or by details.
I have access to the chosen parameter in the controller but I do not know how to or, if I should be passing this to the view along with the products list so the view can implement the logic for deciding which partial view to display?
public ActionResult Products(string id, int? renderview)
{
var products = productRepository.GetProducts(id).ToList();
return View("Products", products);
}
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MLBWebRole.Models.Product>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Products
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Products</h2>
<p>This is the Products page</p>
<p>thumbnails details</p>
<% if (renderview == 1)
{%>
<% Html.RenderPartial("ProductsDetailList"); %>
<% }
else
{ %>
<% Html.RenderPartial("ProductsThumbnailList"); %>
<% } %>
</asp:Content>
Your View Should be something like:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Models.MyModel>" %>
Then in MyModel
Expose Property:
public bool RenderDetailView {get;set;}
In your controller action:
public ActionResult Products(string id, int? renderview)
{
var products = productRepository.GetProducts(id).ToList();
return View("Products", new MyModel {RenderDetailView = renderview.HasValue});
}
Then in your view, make check like:
<% if (Model.RenderDetailView)
Ideally, all the properties or parameters or data which a View needs in order to present itself should be part of Model.
I hope it helps.
An alternative approach would be to use Restful Urls to invoke the appropriate controller action and view.
This makes the urls reflect what you are seeing on the screen and makes the design more extensible; should you need to add other views of the data in the future (summary, latest, etc) you add the new view, no need for partials unless the main body of the view gets more complicated and has to be factored out to a partial view.
The URLs would look like:
~/product/1/detail
~/product/1/thumbnail
And correspond to ProductController methods:
public ActionResult Detail(String id)
{
var products = productRepository.GetProducts(id).ToList();
return View("Detail", products);
}
public ActionResult Thumbnail(string id)
{
var products = productRepository.GetProducts(id).ToList();
return View("Thumbnail", products);
}
You enable the routing with a route like:
{controller}/{id}/{action}
Paul's method is good, but if you decide you want to pass the int, you need to create a view model.
In your controller add this
public class ProductsFormViewModel
{
// Properties
public Products Products { get; private set; }
public int? Renderview { get; private set; }
// Constructor
public ProductsFormViewModel(Products p_products, int? p_renderView)
{
Products = p_products;
Renderview = renderView;
}
}
Then pass this into the view
return View(new ProductsFormViewModel(products, renderview);
And then in the view
Inherits="System.Web.Mvc.ViewPage<YourNamespace.Controllers.ProductsFormViewModel>"