I'm using ASP.NET MVC 4 and have a prepared url in a view like this:
var sectoionUrl = Url.Action("DisplayArticleSection", new { id = sectionId });
Is there any helper to render a partial view using prepared sectionUrl instead of creating it again via helper:
#Html.Action("DisplayArticleSection", new { id = sectionId })
?
Something like this pseudocode:
#Html.RenderUrl(sectionUrl)
Custom HtmlHelper:
public static class RenderUrlHelper
{
public static void RenderUrl(this HtmlHelper helper, string originUrl)
{
var originRequest = helper.ViewContext.RequestContext.HttpContext.Request;
if (!Uri.IsWellFormedUriString(originUrl, UriKind.Absolute))
{
originUrl = new Uri(originRequest.Url, originUrl).AbsoluteUri;
}
int queryIdx = originUrl.IndexOf('?');
string queryString = null;
string url = originUrl;
if (queryIdx != -1)
{
url = originUrl.Substring(0, queryIdx);
queryString = originUrl.Substring(queryIdx + 1);
}
// Extract the data and render the action.
var request = new HttpRequest(null, url, queryString);
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var values = routeData.Values;
string controllerName = values["controller"] as string;
string actionName = values["action"] as string;
var valuesCollection = HttpUtility.ParseQueryString(queryString);
RouteValueDictionary routeValues = new RouteValueDictionary(valuesCollection.AllKeys.ToDictionary(k => k, k => (object)valuesCollection[k]));
helper.RenderAction(actionName, controllerName, routeValues);
}
}
Some code to test custom HtmlHelper:
TestController:
public class TestController : Controller
{
public ActionResult Index(string title, int number)
{
TestModel model = new TestModel { Title = title, Number = number };
return PartialView(model);
}
}
TestController Index view:
#model TestModel
<h1>Title: #Model.Title</h1>
<h2>Number: #Model.Number</h2>
TestModel:
public class TestModel
{
public string Title { get; set; }
public int Number { get; set; }
}
Usage in any view:
#{
var actionUrl = Url.Action("Index", "Test", new { number = 123, title = "Hello Url Renderer" });
Html.RenderUrl(actionUrl);
}
You can use Following HTML Code To Render partail View Using
#Html.RenderPartial("NameOfPartialView")
#Html.Partial("NameOfPartialView")
OR
You can create a custom HTML Helper to Create Functionality like,
#Html.RenderUrl(sectionUrl)
Following url shows step by step how to create Custom HTML Helper.
Create Custom HTML Helper
Related
I have read some similar topics here and on the web, but I don't think I have seen one that would classify this as a duplicate, so I am going to go ahead and post it. I am currently loading my dynamic menus from the database like so:
public void LoadMenus()
{
var dbContext = new ContentClassesDataContext();
var menus = from m in dbContext.Menus
where m.MenuName != "Home" && m.MenuGroup == "RazorHome" && m.RoleID == "Facility"
orderby m.Sequence, m.MenuName
select m;
var html = "";
if (menus.Any())
{
html += "<span/>";
foreach (var menu in menus)
{
html = html + $"<a href='{menu.URL}'>{menu.MenuName}</a><br/>";
}
html += "<hr>";
}
Session["Menus"] = html;
}
LoadMenus() is in my controller class, so I am not able (to my knowledge) to use Razor syntax. I would prefer to load the menus from the view instead, so that I am able to use #Html.ActionLink(linkText, actionName, controllerName). Loading the HTML the way I am currently doing it will generate different link text depending on the current controller, so the links are not always correctly rendered. Is it possible to access the database from the view? Or perhaps to just pass in the content from the database from the controller to the view and then render the menu that way?
You should keep your html in the cshtml views.
You should pass the data through the viewmodel and not through the session.
1)
In the controller, get the menu data (in this example we fetch some fake data).
Create a viewmodel that can hold the menu data and pass it to the view, as shown below:
public class HomeController : Controller
{
public ActionResult Index()
{
var menu = GetMenu();
var vm = new ViewModel() {Menu = menu};
return View(vm);
}
private Menu GetMenu()
{
var menu = new Menu();
var menuItems = new List<MenuItem>();
menuItems.Add(new MenuItem() { LinkText = "Home" , ActionName = "Index", ControllerName = "Home"});
menuItems.Add(new MenuItem() { LinkText = "About", ActionName = "About", ControllerName = "Home" });
menuItems.Add(new MenuItem() { LinkText = "Help", ActionName = "Help", ControllerName = "Home" });
menu.Items = menuItems;
return menu;
}
}
2)
This is the viewmodel
public class ViewModel
{
public Menu Menu { get; set; }
}
This view is an example of how you could render the menu data as a html menu
#model WebApplication1.Models.ViewModel
<ul id="menu">
#foreach (var item in #Model.Menu.Items)
{
<li>#Html.ActionLink(#item.LinkText, #item.ActionName,
#item.ControllerName)</li>
}
</ul>
3)
This is the example menu classes used (representing your entities from the dbcontext)
public class Menu
{
public List<MenuItem> Items { get; set; }
}
public class MenuItem
{
public string LinkText { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
}
Here are some links to get you started:
http://www.codeproject.com/Articles/585873/Basic-Understanding-On-ASP-NET-MVC
http://www.asp.net/mvc/overview/getting-started/introduction/getting-started
I'm trying to write a helper for my ASP.NET MVC3 website which will be able to return a new SelectList containing all the Description attribute tag of an Enum
For example, with the following enum :
public enum Test
{
[Display(Name = "Membre 1")]
Member1,
[Display(Name = "Membre 2")]
Member2
}
I would like to be able to fill a DropDownListFor with something like :
#Html.DropDownListFor(m => m.MyTest, MyHelper(Test))
(with MyTest is a Test variable).
and I expect my DropDownList contains :
Membre 1
Membre 2
I used to use this working helper :
public static string GetEnumDescription(this Enum value)
{
Type enumType = value.GetType();
var enumValue = Enum.GetName(enumType, value);
MemberInfo member = enumType.GetMember(enumValue)[0];
var attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
var outString = ((DisplayAttribute)attrs[0]).Name;
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
outString = ((DisplayAttribute)attrs[0]).GetName();
}
return outString;
}
... but I can't get it work in a SelectList
How can I modify this to directly "incorporate" it directly in my #Html.DropDownListFor helper ?
I have seen some helper over the Internet, especially here or here, but no one works for me. Does anyone is able to share a short and elegant helper which returns all the Display attributes of the members of an Enum in order to put them in a DropDownListFor ?
The following is what I use. It's a slightly modified version of something I found online at one point. I'd give credit where credit is due, but I don't remember where I found it originally at this point:
public static SelectList ToSelectList(this Enum enumeration)
{
var list = (from Enum d in Enum.GetValues(enumeration.GetType())
select new { Value = Enum.GetName(enumeration.GetType(), d), Text = d.GetDescription() }).ToList();
var selectedValue = (int)Enum.Parse(enumeration.GetType(), Enum.GetName(enumeration.GetType(), enumeration));
return new SelectList(list, "Value", "Text");
}
public static string GetDescription(this Enum en)
{
Type type = en.GetType();
System.Reflection.MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((System.ComponentModel.DataAnnotations.DisplayAttribute)attrs[0]).GetName();
}
return en.ToString();
}
In your view, you'd use it:
#Html.DropDownListFor(m => m.MyEnumProperty, Model.MyEnumProperty.ToSelectList())
For implementing Enum type data, I think the easiest way is to use custom Enum helper and Templates. Below is how I implement them in my project.
1) Create Enum Helper
public static class EnumHelper
{
public static IEnumerable<SelectListItem> GetItems(this Type enumType, int? selectedValue)
{
if (!typeof (Enum).IsAssignableFrom(enumType))
{
throw new ArgumentException("Type must be an enum");
}
string[] names = Enum.GetNames(enumType);
IEnumerable<int> values = Enum.GetValues(enumType).Cast<int>();
IEnumerable<SelectListItem> items = names.Zip(values, (name, value) =>
new SelectListItem
{
Text = GetName(enumType, name),
Value = value.ToString(),
Selected = value == selectedValue
}
);
return items;
}
// Get Display Name
private static string GetName(Type enumType, string name)
{
string result = name;
DisplayAttribute attribute = enumType.GetField(name)
.GetCustomAttributes(false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
if (attribute != null)
{
result = attribute.GetName();
}
return result;
}
public static string GetItemName(this Type enumType, int selectedValue)
{
if (!typeof (Enum).IsAssignableFrom(enumType))
{
throw new ArgumentException("Type must be an enum");
}
var itemName = GetName(enumType, Enum.GetNames(enumType)[selectedValue]);
return itemName;
}
}
2) Create folder call "DisplayTemplates" in Shared folder.
3) Create View inside "DisplayTemmplates". The view will look like below:
#using Demo.Web.Helper
#{
var itemName = typeof(Test).GetItemName((int)Model);
}
4) Create floder call "EditorTemplates" in Shared folder.
5) Create View inside "EditorTemplates". The view will look like below:
#using Demo.Web.Helper
#{
var items = typeof (Test).GetItems((int?)Model);
}
#Html.DropDownList("",items)
Here you have finished all of helper and templates, ready for use. When you want to implement Enum Type data, just use it like below:
Model
public class MyModel
{
public int Id { get; set; }
//
public Test Test { get; set; }
}
View
#Html.DisplayFor(m => m.Test)
or
#Html.EditorFor(m => m.Test)
Hope it helps.
I have created the following model object:-
namespace MvcApplication1.Models
{
public class listofpack
{
public string packageName { get; set; }
public string packageId { get; set; }
}
}
Which i am populating its value on the controller as follow:-
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
query["j_username"] = "kermit";
query["hash"] = "9449B5ABCFA9AFDA36B801351ED3DF66";
query["loginAs"] = "admin";
query["loginAs"] = User.Identity.Name;
var url = new UriBuilder("http://localhost:8080/jw/web/json/workflow/package/list");
url.Query = query.ToString();
string json = client.DownloadString(url.ToString());
var model = new JavaScriptSerializer().Deserialize<listofpack>(json);
return View(model);
// return Content(json, "application/json");
}
}
After that on the view i am trying to loop through the model as follow:-
#model IEnumerable<MvcApplication1.Models.listofpack>
#{
ViewBag.Title = "Home Page";
}
#foreach(var item in Model) {
#Html.ActionLink(item.packageId.ToString(), "about", "Home", new { id = "crm"},null)
}
but when i run the view i will get the following error:-
The model item passed into the dictionary is of type 'MvcApplication1.Models.listofpack', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[MvcApplication1.Models.listofpack]'.
BR
:::UPDATE::::
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
i have updated the controller code to be:-
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
query["j_username"] = "kermit";
query["hash"] = "9449B5ABCFA9AFDA36B801351ED3DF66";
query["loginAs"] = "admin";
query["loginAs"] = User.Identity.Name;
var url = new UriBuilder("http://localhost:8080/jw/web/json/workflow/package/list");
url.Query = query.ToString();
string json = client.DownloadString(url.ToString());
var model = new JavaScriptSerializer().Deserialize <List<listofpack>>(json);
return View(model);
}
}
and on the view:-
#model List<MvcApplication1.Models.listofpack>
#Model.Count();
#foreach(var item in Model) {
#Html.ActionLink(item.packageId.ToString(), "about", "Home", new { id = "crm"},null)
}
But the problem is that nothing will be displayed in the view and the .count() will return zero although there should be 5 jason objects passed to the view. So what might be going wrong??
Best Regards
var model = new JavaScriptSerializer().Deserialize<listofpack>(json);
is deserializing into a single listofpack object. Your view expects
#model IEnumerable<MvcApplication1.Models.listofpack>
maybe try something along this lines:
var model = new JavaScriptSerializer().Deserialize<IEnumerable<listofpack>>(json);
Read this question :
No parameterless constructor defined for type of 'System.String' during JSON deserialization
No parameterless constructor defined for type of 'System.Collections.Generic.IEnumerable`1[[MvcApplication1.Models.listofpack, MvcApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
This exception means that the generic type "IEnumerable< listofpack >" u used doesn't have a constructor because it's an interface,so you should use "Dictionary" or "List"
Edit:
Here's an example:
I created a class and name it Employee:
public class Employee
{
public string lastName { get; set; }
public string firstName { get; set; }
}
then i wrote the following statements in the controller :
string jsonEmployees="{'employees': [{ 'firstName':'John' , 'lastName':'Doe' }, { 'firstName':'Anna' , 'lastName':'Smith' }, { 'firstName':'Peter' , 'lastName':'Jones' }]}";
var model = new JavaScriptSerializer().Deserialize<Dictionary<string,List<Employee>>>(jsonEmployees);
return View(model);
so try using "Dictionary< string,List< listOfpack >>".
As the title states. I have an MVC 3 Razor view which i would like to programmatically instantiate and obtain a list of it's Html helpers.
anyone have any good suggestions on how to approach this?
Thanks in advance
I was able to achieve this by executing a custom view result and updating the model from my html helper.
Action Method:
public ActionResult GetListViewHelpers()
{
var model = new TestModel(); // has >>> public List<MyHelper> Helpers { get; set; }
var testContext = new ControllerContext();
testContext.Controller = new MyController();
testContext.HttpContext = HttpContext;
testContext.RouteData.Values["controller"] = "MyController";
testContext.RouteData.Values["action"] = "Index";
var view = new TestViewResult();
view.ViewData.Model = model;
view.ViewName = "TestView"; // i want a list of the helpers from this view
view.ExecuteResult(testContext);
return View(model); // model.Helpers contains a list of helpers within a required view
}
View Result:
public class TestViewResult : ViewResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (String.IsNullOrEmpty(ViewName))
ViewName = context.RouteData.GetRequiredString("action");
ViewEngineResult result = null;
if (View == null)
{
result = FindView(context);
View = result.View;
}
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
using (stream)
using (writer)
{
View.Render(viewContext, writer);
writer.Flush();
}
if (result != null)
result.ViewEngine.ReleaseView(context, View);
}
}
Helper:
public static MvcHtmlString AHelper(this HtmlHelper helper, string name)
{
var model = (TestModel)helper.ViewData.Model;
if(model.Helpers == null)
model.Helpers = new List<MyHelper>();
model.Helpers.Add(new MyHelper()
{
Name = name // add all the info i want from the helpers overloads
});
// .... helper logic here
return null;
}
Hello i am creating a small webpage that give our users an interface to search in our database.
This website must supply 2 webservice functions, Search(string searchString) and GetAccountInfoByInitials(string initials)
I would like to use my controllers to do this, but i can not find out how i can get the html out of a ViewResult.
I have tryed the following, but the result.ToString() only give me the string "System.Web.Mvc.ViewResult"
public class SearchService : ISearchService
{
private readonly ServiceHandlerController _controller;
public SearchService()
{
_controller = new ServiceHandlerController();
}
public string Search(string searchString)
{
var result = _controller.Search(searchString);
return result.ToString();
}
public string GetAccountInfoByInitials(string initials)
{
var result = _controller.Search(initials).ToString();
return result;
}
}
This is an answer to a question I posted similar to this one:
Is there a way to process an MVC view (aspx file) from a non-web application?
public class AspHost : MarshalByRefObject
{
public string _VirtualDir;
public string _PhysicalDir;
public string ViewToString<T>(string aspx, Dictionary<string, object> viewData, T model)
{
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter tw = new HtmlTextWriter(sw))
{
var workerRequest = new SimpleWorkerRequest(aspx, "", tw);
HttpContext.Current = new HttpContext(workerRequest);
ViewDataDictionary<T> viewDataDictionary = new ViewDataDictionary<T>(model);
foreach (KeyValuePair<string, object> pair in viewData)
{
viewDataDictionary.Add(pair.Key, pair.Value);
}
object view = BuildManager.CreateInstanceFromVirtualPath(aspx, typeof(object));
ViewPage viewPage = view as ViewPage;
if (viewPage != null)
{
viewPage.ViewData = viewDataDictionary;
}
else
{
ViewUserControl viewUserControl = view as ViewUserControl;
if (viewUserControl != null)
{
viewPage = new ViewPage();
viewPage.Controls.Add(viewUserControl);
}
}
if (viewPage != null)
{
HttpContext.Current.Server.Execute(viewPage, tw, true);
return sb.ToString();
}
throw new InvalidOperationException();
}
}
}
public static AspHost SetupFakeHttpContext(string physicalDir, string virtualDir)
{
return (AspHost)ApplicationHost.CreateApplicationHost(
typeof(AspHost), virtualDir, physicalDir);
}
}
Then, to render a file:
var host = AspHost.SetupFakeHttpContext("Path/To/Your/MvcApplication", "/");
var viewData = new ViewDataDictionary<SomeModelType>(){ Model = myModel };
String rendered = host.ViewToString("~/Views/MyView.aspx", new Dictionary<string, object>(viewData), viewData.Model);
I do this in almost an identical fashion but actually use the controller iteself to return a partialview to string.
i use the following extension method in my base controller:
public static class ExtensionMethods
{
public static string RenderPartialToString(this ControllerBase controller, string partialName, object model)
{
var vd = new ViewDataDictionary(controller.ViewData);
var vp = new ViewPage
{
ViewData = vd,
ViewContext = new ViewContext(),
Url = new UrlHelper(controller.ControllerContext.RequestContext)
};
ViewEngineResult result = ViewEngines
.Engines
.FindPartialView(controller.ControllerContext, partialName);
if (result.View == null)
{
throw new InvalidOperationException(
string.Format("The partial view '{0}' could not be found", partialName));
}
var partialPath = ((WebFormView)result.View).ViewPath;
vp.ViewData.Model = model;
Control control = vp.LoadControl(partialPath);
vp.Controls.Add(control);
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
return sb.ToString();
}
}
followed by then returning my partialview in the following fashion:
return Content(this.RenderPartialToString("myPartialView", myModel));
This should hopefully sort you out.
The View Result doesn't holds the page by itself. Asp.net MVC uses it, along with the View Engine configured to get the actual page.
You'll hit a roadblock if you are using the default view engine - see link text. Basically because the asp.net view engine is tied to the context. Other view engines won't give you this issue, but if the assets you are trying to reduce are already relying in the default view engine then that defeats the purpose. There are other ways around it, but I'm unsure how convenient those would be for you.