Change menus depending on controller in ASP.NET MVC - asp.net-mvc

In a master-page, how can I know, which controller I am currently using? Is there some kind of context-object, that can give me that sort of information?
Standard menu
<ul>
<li>Fire</li>
<li>Ice</li>
<li>Water</li>
</ul>
Menu if I am in the water-controller
<ul>
<li>Fire</li>
<li>Ice</li>
<li class="selected">Water</li>
</ul>
If I have a menu in my Site.master, where each menu-item refers to a different controller, how can I highlight each menu-item depending on, which controller I am currently in?
I know I can get the url from request.Servervariables and then work some string-magic, but there must be a better way - some kind of context-object?

The ViewContext property of the ViewMasterPage contains the Controller and RouteData for the request. You could look at the type name of the controller or the controller key in the route data to find out which controller was invoked.

Here are two helper methods I use for checking if its the current controller or even current action. The you can use the helpers to determine whether to add class="selected" or not.
public static bool IsCurrentController(this HtmlHelper helper,
string controllerName)
{
string currentControllerName = (string)helper.ViewContext.RouteData.
Values["controller"];
if (currentControllerName.Equals(controllerName,
StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
return false;
}
public static bool IsCurrentAction(this HtmlHelper helper, string actionName,
string controllerName)
{
string currentControllerName = (string)helper.ViewContext.RouteData
.Values["controller"];
string currentActionName = (string)helper.ViewContext.RouteData
.Values["action"];
if (currentControllerName.Equals(controllerName,
StringComparison.CurrentCultureIgnoreCase) &&
currentActionName.Equals(actionName,
StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
return false;
}

You can do a
ViewContext.Controller.GetType().Name
That should do it.

Related

ASP.NET MVC: handling logic & variables in the _Layout page

In my _layout.cshtml page, I've got some elements that need to be hidden on some pages. I know the pages on which we won't display some parts. For a single page, I could just do this:
#if (ViewContext.RouteData.Values["action"].ToString() != "LogIn") {
<div> .... <div>
}
But that gets messy and long with multiple pages. Someplace, ideally not in the _Layout page, I could build a list of actions, and if the current action is any of them, set a boolean variable (ShowStuff) to false. Then just do this on _Layout:
#if (ShowStuff== true) {
<div> .... <div>
}
I'm just not sure where would be the best-practice way to examine that list of actions and set the boolean. Can the _Layout page have it's own model and controller like a normal view?
Similarly to MikeSW answer, I'd use an action filter, but I would populate ViewData with a specific ViewModel. When you want to display it simply DisplayFor the value, if it's populated the template is used by whatever type the model is, if it's null nothing is displayed. (examples below from memory, may not be exactly correct.)
public BlahModelAttribute : ActionFilterAttribute
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
BlahModel model = Db.GetModel();
filterContext.Controller.ViewData.Set(model);
}
}
ViewData extensions:
public static ViewDataExtensions
{
private static string GetName<T>()
: where T : class
{
return typeof(T).FullName;
}
public static void Set<T>(this ViewDataDictionary viewData, T value)
: where T : class
{
var name = GetName<T>();
viewData[name] = value;
}
public static T Get<T>(this ViewDataDictionary viewData)
: where T : class
{
var name = GetName<T>();
return viewData[name] as T;
}
}
In your view:
#{var blahModel = ViewData.Get<BlahModel>() }
#Html.DisplayFor(m => blahModel)
If devs would stop looking for the 'best way' for every problem they have, that would be great. No best way here, just opinionated solutions. Here's mine: You can create an action filter [ShowNav] and decorate any controller/action you need. That filter will put a boolean into HttpContext.Items . Create then a HtmlHelper which checks for the boolean. Then in _layout, if (Html.CanShowNavig()) { <nav> } . That's the easiest solution that comes to my mind.

Instantiate a helper class but not in view

I need a way to instantiate a class thats supposed to help me with some querystring-building when it comes to my links in the view, and I cant use this methods that i require as static methods since that would cause the querystringbuilder to keep data during the whole life-time of the application (which would cause some serious problems)
So my question to you guys is,
Is it possible to some how be able to instantiate the class/object that i require but not in the actual view itself?
SO to keep the question simple.. is there anyway that I could do something like:
#MyInstantiatedObject.DoStuff()
in my view with out doing this before in my view:
#{
var MyInstantiatedObject = new MyClass()
}
I do get that I some where some how will need to instansiate the object, but my question is if its possible to do it in some other manner (like telling the web.config to handel it..or using some app_code #helper magic or something)
Thanks in advance!
What you are trying to achieve goes against the philosophy of MVC. If you want to keep the query string data between the actions, you can create your custom ActionLink html helper like this:
public static MvcHtmlString ActionLinkWithQueryString(this HtmlHelper htmlHelper,
string linkText, string actionName)
{
var routeValueDictionary = new RouteValueDictionary();
return htmlHelper.ActionLinkWithQueryString(linkText,
actionName, routeValueDictionary);
}
public static MvcHtmlString ActionLinkWithQueryString(this HtmlHelper htmlHelper,
string linkText, string actionName, RouteValueDictionary routeValues)
{
var queryString = HttpContext.Current.Request.QueryString;
if (queryString.Count > 0)
{
foreach (string key in queryString.Keys)
{
routeValues.Add(key, queryString[key]);
}
}
return htmlHelper.ActionLink(linkText, actionName, routeValues);
}
You can also create a custom RedirectToAction method in your Controller or in a Controller Extention like this:
private RedirectToRouteResult RedirectToActionWithQueryString(string actionName)
{
var queryString = Request.QueryString;
var routeValues = new RouteValueDictionary();
foreach (string key in queryString.Keys)
{
routeValues.Add(key, queryString[key]);
}
return RedirectToAction(actionName, routeValues);
}

asp.net MVC 4 - Htmlhelper extensions

I am creating a custom HtmlHelper extension for Html.RenderAction. My Parent view will contain many different partial views which will be rendered byt calling Html.Renderaction. But the the admin can sort off switch of a partial view for a role or he can completely deactivate the action for entire application So i am planning to have an extension method for Html.RenderAction which will in turn check for the role and see if the role has access to a particular action. This role to action mapping is dine in xml and I am planning to load this xml as in memory data structure only once. And have the html helper extension look into that data structure. Is that a good way to go? Any better ways?
#section column2 {
#{Html.RenderActionIfIfAllowed("DashboardItem_Users", "DashBoard",User);}
}
#section column3 {
#{Html.RenderActionIfIfAllowed("DashboardItem_Orders", "DashBoard", User);}
}
I have to render the above partialviews. So i have created a html helper extension called Html.RenderActionIfIfAllowed.
public static class HtmlHelperExtensions
{
public static void RenderActionIfIfAllowed<TModel>(this HtmlHelper<TModel> htmlHelper, string actionName, string controllerName, IPrincipal user)
{
//We can use the layour manager class to check if a particular role has access to an action and also if the action is active.
//Hard coding here just for demo purpose
if (user.IsInRole("Admin") && actionName != "DashboardItem_Users")
{
System.Web.Mvc.Html.ChildActionExtensions.RenderAction(htmlHelper, actionName, controllerName);
}
else if (user.Identity.IsAuthenticated && !user.IsInRole("Admin"))
{
System.Web.Mvc.Html.ChildActionExtensions.RenderAction(htmlHelper, actionName, controllerName);
}
}
}
The reason for doing that way is because we would like to dynamically show or not show a aprtial view to user based on whether the view is active or not. We will read an xml file that will say whether the view is active not for a user and render it accordingly
I used to create ViewModel for this and set bool properties
public class DashBoardViewModel{
public DashBoard dashBoard{get;set;}
bool showItemDashBoard{get;set;}
bool showOrderDashBoard{get;set;}
}
In Controller I validate user role and set those boolean properties.
In View
if(Model.showItemDashBoard){
#Html.RenderAction("Action","Controller")
}

Custom ActionLink helper that knows what page you're on

I have a small MVC site that uses the Html.ActionLink helper for a navbar. One thing I would like to change is that the default ActionLink will render out an html link to a page even if that is the current page.
For example, it creates a link like this:
Some title...
even if you're already on /myUrl. It would be nice if it would disable that link and maybe insert a special CSS class to show the currently visited page, like this:
My Url
Some Other Url
This problem must have been encountered before on loads of MVC sites, so I'm curious to know how other people have tackled it.
This seems like a good scenario to roll a custom HTML helper. So let's roll it:
public static class LinkExtensions
{
public static MvcHtmlString MyActionLink(
this HtmlHelper htmlHelper,
string linkText,
string action,
string controller
)
{
var currentAction = htmlHelper.ViewContext.RouteData.GetRequiredString("action");
var currentController = htmlHelper.ViewContext.RouteData.GetRequiredString("controller");
if (action == currentAction && controller == currentController)
{
var anchor = new TagBuilder("a");
anchor.Attributes["href"] = "#";
anchor.AddCssClass("currentPageCSS");
anchor.SetInnerText(linkText);
return MvcHtmlString.Create(anchor.ToString());
}
return htmlHelper.ActionLink(linkText, action, controller);
}
}
and inside your view:
<%= Html.MyActionLink("hello foo", "Index", "Home") %>
<%= Html.MyActionLink("hello bar", "About", "Home") %>
...
and depending on where you are the helper will generate the proper anchor.

Role-Based Content asp.net mvc

I wish to display content depending on the given role(s) of the active user , in the ASP.NET MVC.
Compare the old fashion way, using WebForms:
protected void Page_Load(Object sender, EventArgs e)
{
if(User.IsInRole("Administrator")) {
adminLink.Visible = true;
}
}
Now how would I go on writing that when using the ASP.NET MVC ?
From my point of view, it would be wrong to place it directly in the View File, and assigning a variable for every single view won't be pretty either.
Create Html helper and check current user roles in its code:
public static class Html
{
public static string Admin(this HtmlHelper html)
{
var user = html.ViewContext.HttpContext.User;
if (!user.IsInRole("Administrator")) {
// display nothing
return String.Empty;
// or maybe another link ?
}
var a = new TagBuilder("a");
a["href"] = "#";
a.SetInnerText("Admin");
var div = new TagBuilder("div") {
InnerHtml = a.ToString(TagRenderMode.Normal);
}
return div.ToString(TagRenderMode.Normal);
}
}
UPDATED:
Or create wrapper for stock Html helper. Example for ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName):
public static class Html
{
public static string RoleActionLink(this HtmlHelper html, string role, string linkText, string actionName, string controllerName)
{
return html.ViewContext.HttpContext.User.IsInRole(role)
? html.ActionLink(linkText, actionName, controllerName)
: String.Empty;
}
}
No you would be placing it in the view file, like so actually:
<% If (User.IsInRole("Administrator")) { %>
<div>Admin text</div>
<% } %>
this worked for me:
<% MembershipUser mu = Membership.GetUser();
if (mu != null)
if (Roles.IsUserInRole(mu.UserName, "Administrator"))
{
%>
<li class="paddingleftThree"><%= Html.ActionLink("User Administration", "GetUsers", "Account")%></li> <%} %>
The separation of concerns approach suggested in ASP.NET MVC 4 How do you serve different HTML based on Role? in my opinion is a better way to go.
Personally I avoid IsInRole check as much as possible in the code and leave it to declarative means to achieve role based restriction as much as possible. This ensures code remains maintainable over time. I am not sure if this is a right or the wrong approach, but has worked well for me.

Resources