I have a database menu structure which I would like to add to the site.master file.
I’ve looked at other questions on StackOverflow but cannot get this to work on my website.
How do I add a User Control to the Site.Master file?
Menu.ascx
<%foreach (MainMenuSort mainMenuSort in (List<MainMenuSort>)ViewData["MainMenuSortListDisplay"])
{ %>
<li><%= Html.Encode(mainMenuSort.MainMenuId.MainMenuName)%></li>
<%foreach (SubMenuSort subMenuSort in (List<SubMenuSort>)ViewData["SubMenuSortListDisplay"])
{%>
<%if (mainMenuSort.MainMenuId.Id == subMenuSort.SubMenuId.MainMenu.Id)
{ %>
<li><%= Html.Encode(subMenuSort.SubMenuId.SubMenuName)%></li>
<%} %>
<%} %>
<%}%>
You need to use the Html.RenderPartial method in your master page.
You will need to set the MainMenuSortListDisplay and SubMenuSortListDisplay view data keys in whatever action is calling the view that uses your master page.
In your master use this
<% Html.RenderPartial("~/Views/Shared/Menu.ascx");
The path needs to be the app relative path to the control's folder. Typically these go under Shared. You can make the structure how you want below the Shared folder.
To make this technique stronger, use a strongly typed partial. In the question you would perhaps make a new class (MenuModel) with two generic collections as properties and place it in the models folder of the application. Then in the model's constructor call a method that populates the lists.
public class MenuModel
{
public IEnumerable<MainMenuSort> OuterList {get; set;}
public IEnumerable<SubMEnuSort> InnerList {get; set;}
public MenuModel()
{
VoidThatFillsTheInnerAndOuterList();
}
This will mean that you can do this in your controller
public ActionResult ShowAForm()
{
ViewData["MenuPartialData"] = new MenuModel();
return View();
}
Having set this key, your master page can use the overload of RenderPartial, like this
<% Html.RenderPartial(
"~/View/Shared/Menu.ascx",
(MenuModel)ViewData["MenuPartialData"]); %>
This assumes that your partial is strongly typed to the MenuModel class. Then in the partial you can use the model which rewrites your code slightly
<% foreach (MainMenuSort mainMenuSort in Model.OuterList) { %>
<li><%= Html.Encode(mainMenuSort.MainMenuId.MainMenuName)%></li>
<% foreach (SubMenuSort subMenuSort in Model.InnerList) {%>
<%if (mainMenuSort.MainMenuId.Id == subMenuSort.SubMenuId.MainMenu.Id)
{ %>
<li><%= Html.Encode(subMenuSort.SubMenuId.SubMenuName)%></li>
<%} %>
<%} %>
<%}%>
Hope that helps
Try something like
<% Html.RenderPartial("Menu") %>
EDIT: Corrected a typo
You could also do it as a HTMLHelper and in the MasterPage just call <%= Html.Menu() %>. Then in your HTMLHelper you have the code to get the database records and loop through them. Here is a link I found to get you started. Note my comments as there is a bug in the code example provided. I'm still having issues handling subitems of menus, I guess I need a recursive function or something??
With the help of this link. I was able to display a menu in the site.master page.
Related
I have a usercontrol named "LoginUserControl.ascx" which I have placed in a master page.
Header of "LoginUserControl.ascx"
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MultiTechnologyWeb.Models.loginmodel>" %>
Then I used the below code to show the usercontrol in the masterpage.
<% Html.RenderPartial("LoginUserControl"); %>
On first run the page "index" is loaded.
Notice the header of the "index" page, no model is specified. Thus page load successfully
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/MT.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
Now I click on the link to open register.aspx. I got the below error
The model item passed into the dictionary is of type 'MultiTechnologyWeb.Models.registermodel', but this dictionary requires a model item of type 'MultiTechnologyWeb.Models.loginmodel'.
Header of "register.aspx" page
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/MT.Master" Inherits="System.Web.Mvc.ViewPage<MultiTechnologyWeb.Models.registermodel>" %>
So to my understanding model is being interchanged, so anybody can please help me on how to resolve this issue
More Explanation.............LATEST
I have debug, i know that the crash is occuring after the actionresult for register is finished execution.
Code below is for actionresult "register"
public ActionResult register()
{
registermodel model;
//some code here
return View("register",model);
}
So i'm just returning one type of model that is "registermodel", Would it be possible to return another model such as "loginmodel" by using a list or array to return multiple models in the same view.
You should use <% Html.RenderAction("Logon","Account"); %> in your MasterPage instead of using RenderPartial and in this action you just return the login partial you want to use in the header
public ActionResult Logon(){
// do your stuff
return PartialView("LoginUserControl");
}
By this way you could pass the loginmodel to the LogInPartial and pass registermodel to the register page
Please not that RenderAction and RenderPartial are not the same.
RenderPartial will render only the view. While RenderAction will make a new MVC roundtrip, by making a new instance of the controller etc and returning the result.
To solve your issue you could pass in the MultiTechnologyWeb.Models.loginmodel where you call <% Html.RenderPartial("LoginUserControl"); %>. It would look like this:
<% Html.RenderPartial("LoginUserControl", new MultiTechnologyWeb.Models.loginmodel()); %>
Or:
<% Html.RenderPartial("LoginUserControl", Model.LoginModel); %>
If you're not wanting to send a model to your partial view, which I've wanted to do in the past, you do have to at least pass something to the RenderPartial method.
This was the only method I could find that allowed me to now have to pass a model. I tried passing null and it continued to pass the parent model
<% Html.RenderPartial("LoginUserControl", new ViewDataDictionary()); %>
trying to put a strongly typed partial view on a homepage in asp.net but it wont seem to work it, here is my code
new to asp.net and partial views.
Controller :
public ActionResult VenuePartial()
{
ViewData["Message"] = _entities.VenuePartialList();
return View();
}
Repository :
public IEnumerable<Venue> VenuePartialList()
{
var list = from s in _entities.Venue
orderby s.name ascending
select s;
return list.ToList();
}
IRepository :
IEnumerable<Venue> VenuePartialList();
Index Page :
<%Html.RenderPartial("~/Views/Venue/VenuePartial.ascx");%>
Any help would be grateful asap please
regards T
Maybe you need to pass a model to this partial:
<% Html.RenderPartial("~/Views/Venue/VenuePartial.ascx", ViewData["Message"]); %>
And by the way WTF are you using ViewData["Message"] to pass a model instead of using a model and a strongly typed view:
public ActionResult VenuePartial()
{
return View(_entities.VenuePartialList());
}
and then:
<% Html.RenderPartial("~/Views/Venue/VenuePartial.ascx", Model); %>
This obviously assumes that your partial is strongly typed to IEnumerable<Venue>. If it is typed to a single Venue you might also consider using Editor/Display Templates. So in your main view:
<%= Html.DisplayForModel() %>
and in the corresponding display template (~/Views/Shared/DisplayTemplates/Venue.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.Venue>" %>
<span>
<%= Html.DisplayFor(x => x.SomePropertyOfVenue) %>
</span>
and now the display template will be rendered for each item of the model collection.
I have feature on my website (some UI and associated functionality) that I want to be able to reuse on multiple pages. For the purposes of this question, let's say it's a "Comments" feature.
There is an area in my application for Components and within the area are a controller: /Controllers/CommentController, and two partial views: /Views/Comment/Index.ascx (for listing comments) and /Views/Comment/Create.ascx (for creating comments).
CommentController looks something like this:
public class CommentController : Controller
{
[ChildActionOnly]
public ActionResult Index()
{
return PartialView(GetComments());
}
[HttpGet]
[ChildActionOnly]
public ActionResult Create()
{
return PartialView(); //this is wrong.
}
[HttpPost]
[ChildActionOnly]
public ActionResult Create(FormCollection formValues)
{
SaveComment(formValues);
return RedirectToAction("Index"); //this is wrong too.
}
}
Index Partial View:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% foreach (var item in Model) { %>
<div>
<%: item.Comment %>
</div>
<% } %>
<%: Html.ActionLink("Add a Comment", "Create", "Comment", new { area = "Components" }, null) %>
</div>
Create Partial View:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% using (Html.BeginForm())
{%>
Enter your comment:
<div>
<input type="text" name="comment" />
</div>
<p>
<input type="submit" value="Create" />
<% //also render a cancel button and redirect to "Index" view %>
</p>
<% } %>
</div>
The Index partial view is included in a view with RenderAction, like so:
<% Html.RenderAction("Index", "Comment", new { area = "Components" }); %>
This code doesn't work because the forms within the partial views submit to actions on the CommentsController that are marked [ChildActionOnly] (this is by design, I don't want the "Components" to be requested independently of a hosting page).
How can I make this "component" approach work, i.e. have a partial view that submits a form to change the state of a component within a page without losing the hosting page itself?
EDIT:
To clarify, the use of [ChildActionOnly] is not my problem here. If I remove the attribute from my action methods, my code only "works" in that it doesn't throw an exception. My "component" still breaks out of its hosting page when its form is submitted (because I'm telling the form to submit to the partial view's URL!).
You are making MVC fight itself by asking a form to target an action that is marked as ChildActionOnly.
My solution to this problem when I was designing a highly reusable wizard framework, was to NOT mark the actions as ChildActionOnly but instead to detect if the request was an ajax one or just a plain vanilla request.
The code for all this is packaged into a base controller class. In your derived controllers, you do something like:
[WizardStep(4, "Illness Details")]
public ActionResult IllnessDetails()
{
return Navigate();
}
Where the Navigate() method of the base controller has decided whether to return the full view or just the partial view, depending on whether it is, or isn't, an ajax request. That way, you can never return the partial view in isolation.
To ascertain if it is an Ajax request, I used a combination of Request.IsAjaxRequest() and TempData. The TempData is needed because my wizard framework implements the PRG pattern out of the box, so I need to persist the fact that the original post was an ajax one.
I guess this is just one solution and it took a bit of trial and error to get it right. But now I live happily ever after developing wizards like I was JK Rowling...
Use Ajax to post the partial.
The standard MVC example to draw an item with the appropriate View Template is:
Html.DisplayFor(m => m.Date)
If the Model object has a property named Date of type DateTime, this returns a string with the HTML from the Display/DateTime.ascx template.
Suppose you wanted to do the same thing, but couldn't use the strongly-typed version - you didn't know the Model's type for this View at compile time. You use the older:
Html.Display("Date");
So here's the hard part.
Suppose the Model is IEnumerable. You don't know what those objects are at compile-time, but at run-time they happen to be objects with a Date of type DateTime again, like:
public class ModelClass
{
public DateTime Date { get; set; }
}
Now suppose you want your View to iterate over those objects and render each out. If all you cared about was the value you could do this:
<%
StringBuilder sb = new StringBuilder();
foreach(object obj in (IEnumerable<object>)Model)
{
Type type = obj.GetType();
foreach(PropertyInfo prop in type.GetProperties())
{
// TODO: Draw the appropriate Display PartialView/Template instead
sb.AppendLine(prop.GetValue(obj, null).ToString());
}
}
%>
<%= sb.ToString() %>
I'm obviously taking some shortcuts to keep this example focused.
Here's the point - how do I fulfill that TODO I've written for myself? I don't just want to get the value - I want to get it nicely formatted like Html.Display("Date"). But if I just call Html.Display("Date"), it inspects the Model, which is an IEnumerable, for a property named Date, which it of course does not have. Html.Display doesn't take an object as an argument to use as the Model (like Html.Display(obj, "Date"), and all the classes and methods I can find that lie underneath appear to be internal so I can't tweak and call into them directly.
There must be some simple way to accomplish what I'm trying to do, but I can't seem to find it.
Just to make sure I'm being clear - here's an example of the code of DateTime.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime>" %>
<%= Model.ToString("MM/dd/yyyy") %>
And so, ideally, the output from this View that can take any Model, but in this case a list of 3 of these ModelClass objects above, would be:
11/10/2001
11/10/2002
11/10/2003
Because the code would find the Display PartialView for DateTime and render it appropriately for each.
So - how do I fulfill the TODO?
Have a look at the template code in this excellent post from Phil Haack. It seems to come close to what you are looking for: http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx
I've found one potential solution to this but I'm not in love with it; it requires using several file-based templates, meaning you can't abstract this easily into a code library for use in multiple projects.
The View:
<%
StringBuilder sb = new StringBuilder();
Type itemType = Model.GetType().GetGenericArguments()[0];
sb.AppendLine("<table>");
// Pass in the Model (IEnumerable<object>)'s generic item type as
// the Model for a PartialView that draws the header
sb.Append(Html.Partial("DisplayTableHead", itemType));
foreach(object item in (IEnumerable<object>)Model)
{
sb.Append(Html.Partial("DisplayTableRow", item));
}
sb.AppendLine("</table>");
%>
<%= sb.ToString() %>
Views/Shared/DisplayTableHead.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Type>" %>
<tr>
<%
foreach (PropertyInfo prop in Model.GetProperties())
{
%>
<th><%= prop.Name %></th>
<%
}
%>
</tr>
Views/Shared/DisplayTableRow.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<tr>
<%
Type modelType = Model.GetType();
foreach (PropertyInfo modelField in modelType.GetProperties())
{
%>
<td><%= Html.Display(modelField.Name) %></td>
<%
}
%>
</tr>
But I now see the major flaw in this solution, which is that Clicktricity's posted solution acknowledges details in the ModelMetadata - like whether that particular property is set for display, whether it's complex or not, etc.
I am getting my content from a database. How can i use partial views to show content on page using that database?
database table: Content
[Id, Content] these are 2 fields
i want to get the content from db using partial views.
How i will pass an id to a partial view and show the content in view page?
You could use Html.RenderAction:
public class MyController
{
[ChildActionOnly]
public ActionResult Foo(int id)
{
var content = GetContentFromDatabase(id);
return Content(content, MediaTypeNames.Text.Html);
}
}
And in your view include the partial:
<%= Html.RenderAction("foo", "mycontroller", new { id = 5 }) %>
Remark: RenderAction is part of the now released ASP.NET MVC 2 RTM. For ASP.NET MVC 1 you may take a look at the Futures assembly containing this extension method.
Inside your view, use the Html.RenderPartial function. There are a few different uses:
You can pass in a model to the partial view: <% Html.RenderPartial("partialName", model); %>
Or you can pass in a whole new ViewDataDictionary: <% Html.RenderPartial("partialName", viewData); %>
For the full documentation, see here.
EDIT: (Answer to comment):
I would include that data as part of you're view's model. For example, let's say in your model you have:
List<Person> People;
In your view, you want to loop through each one of these, and use a PartialView to display the details:
<% foreach( var p in Model.People){ %>
<p> <% Html.RenderPartial("personPartial", p); %> </p>
<%}%>
Now, your PartialView might look like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Person>" %>
<%=Model.PersonName%>