As a base for discussion. Create a standard ASP.NET MVC Web project.
It will contain two menu items in the master page:
<div id="menucontainer">
<ul id="menu">
<li>
<%= Html.ActionLink("Home", "Index", "Home")%></li>
<li>
<%= Html.ActionLink("About", "About", "Home")%></li>
</ul>
</div>
How can I set the visual CSS style indicating the current page.
For example, when in the About page/controller, I essentially would like to do this:
<%= Html.ActionLink("About", "About", "Home", new {class="current"})%></li>
And, of course, when on the home page:
<%= Html.ActionLink("Home", "Index", "Home", new {class="current"})%></li>
(Having a CSS style names current that visually indicates in the menu that this is the current page.)
I could break out the menu div from the master page into a content place holder, but that would mean that I must put the menu on every page.
Any ideas, is there a nice solution to this?
The easiest way is to get the current controller and action from the ViewContext's RouteData. Note the change in signature and use of # to escape the keyword.
<% var controller = ViewContext.RouteData.Values["controller"] as string ?? "Home";
var action = ViewContext.RouteData.Values["action"] as string ?? "Index";
var page = (controller + ":" + action).ToLower();
%>
<%= Html.ActionLink( "About", "About", "Home", null,
new { #class = page == "home:about" ? "current" : "" ) %>
<%= Html.ActionLink( "Home", "Index", "Home", null,
new { #class = page == "home:index" ? "current" : "" ) %>
Note that you could combine this an HtmlHelper extension like #Jon's and make it cleaner.
<%= Html.MenuLink( "About", "About", "Home", null, null, "current" ) %>
Where MenuActionLink is
public static class MenuHelperExtensions
{
public static string MenuLink( this HtmlHelper helper,
string text,
string action,
string controller,
object routeValues,
object htmlAttributes,
string currentClass )
{
RouteValueDictionary attributes = new RouteValueDictionary( htmlAttributes );
string currentController = helper.ViewContext.RouteData.Values["controller"] as string ?? "home";
string currentAction = helper.ViewContext.RouteData.Values["action"] as string ?? "index";
string page = string.Format( "{0}:{1}", currentController, currentAction ).ToLower();
string thisPage = string.Format( "{0}:{1}", controller, action ).ToLower();
attributes["class"] = (page == thisPage) ? currentClass : "";
return helper.ActionLink( text, action, controller, new RouteValueDictionary( routeValues ), attributes );
}
}
I recently created an HTML Helper for this that looks like:
public static string NavigationLink(this HtmlHelper helper, string path, string text)
{
string cssClass = String.Empty;
if (HttpContext.Current.Request.Path.IndexOf(path) != -1)
{
cssClass = "class = 'selected'";
}
return String.Format(#"<li><a href='{0}' {1}>{2}</a></li>", path, cssClass, text);
}
The Implementation looks like this:
<ul id="Navigation">
<%=Html.NavigationLink("/Path1", "Text1")%>
<%=Html.NavigationLink("/Path2", "Text2")%>
<%=Html.NavigationLink("/Path3", "Text3")%>
<%=Html.NavigationLink("/Path4", "Text4")%>
</ul>
If you are using T4MVC, you can use this:
public static HtmlString MenuLink(
this HtmlHelper helper,
string text,
IT4MVCActionResult action,
object htmlAttributes = null)
{
var currentController = helper.ViewContext.RouteData.Values["controller"] as string ?? "home";
var currentAction = helper.ViewContext.RouteData.Values["action"] as string ?? "index";
var attributes = new RouteValueDictionary(htmlAttributes);
var cssClass = (attributes.ContainsKey("class"))
? attributes["class"] + " "
: string.Empty;
string selectedClass;
if(action.Controller.Equals(currentController, StringComparison.InvariantCultureIgnoreCase)
{
selectedClass = "selected-parent";
if(action.Action.Equals(currentAction, StringComparison.InvariantCultureIgnoreCase))
selectedClass = "selected";
}
cssClass += selectedClass;
attributes["class"] = cssClass;
return helper.ActionLink(text, (ActionResult)action, attributes);
}
It might just be that it's the 5th parameter, so slot a null before your html attribute. This post here describes it as such, though you can pass in some stuff on the 4th arguement, the 5th is specifically for HTMLattributes
<script type="javascript/text">
$( document ).ready( function() {
#if (Request.Url.AbsolutePath.ToLower() == "/")
{
#Html.Raw("$('.navbar-nav li').eq(0).attr('class','active');")
}
#if (Request.Url.AbsolutePath.ToLower().Contains("details"))
{
#Html.Raw("$('.navbar-nav li').eq(1).attr('class','active');")
}
#if (Request.Url.AbsolutePath.ToLower().Contains("schedule"))
{
#Html.Raw("$('.navbar-nav li').eq(2).attr('class','active');")
}
});
</script>
Chucked this together in 5mins, I could probably refactor it, but should give you the basic idea, its probably most useful for smaller sites.
Related
I have a 'webshop' where you can buy all sorts of fruits, vedgetables and more. this website can be used in multiple languages.
when the user is looking for a specific item he's using a variable to filter through the items. the url will look like this localhost/Products?item=AARB.
If the user changes languages it will return the returnUrl. the returnUrl only returns the action method looking like localhost/Products. I want it so that the returnUrl also contains the query parameter as it is a lot more use friendly to go back to your searched item when changing languages.
My ProductsController has the following Index Method:
[HttpGet]
public ActionResult Index(string item = "APPE", int amount = 0)
{
var CurrentCulture = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
ViewData["amount"] = amount;
//Uses the Logged in Username to match the items related to it's RelatieNummer
var LoggedUser = Convert.ToInt32(User.Identity.Name);
UserCheck ApplicationUser = _context.ApplicationUser.Where(x => x.CompanyNumber == LoggedUser).First();
var itemCheck = item != null ? item.ToUpper() : "";
try
{
var currentCat = _context.Categories.Where(x => x.CategoryCode.Contains(itemCheck)).First();
ViewData["Main_Items"] = FillMainItems(); //This is a different Method which fills Commodity names
var SearchItem = _context.ZzProducts
.Where(x => x.RelatieNummer == LoggedUser)
.Where(x => x.HoofdSoortCode.Contains(itemCheck));
return View(SearchItem);
catch (Exception ex)
{
ModelState.AddModelError("Error", ex.Message);
return View("Error");
}
The querystring item is the users input. for example: He wants to look for banana's(BANA) or Oranges(MAND). when the user changes the language of the website, this querystring needs to be sent back as well, returning a full url of localhost/Products?item=BANA from, what I assume is my HomeController
In my HomeController I have made a method that changes the CultureInfo for the user.
[HttpGet]
public IActionResult SetLanguage(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
Console.WriteLine("The new CultureInfo is now: " + CultureInfo.CurrentCulture);
return LocalRedirect(returnUrl);
}
Hopefully I've made my intentions and code clear enough to help find a solution to my problem :)
Edit
I forgot to post my PartialView to show where I send my returnUrl back to the HomeController
#{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var cultureItems = LocOptions.Value.SupportedUICultures
.Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
.ToList();
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~" : $"~{Context.Request.Path.Value}";
}
//Clickable Flags for CultureInfo
<div id="SelectLanguage" title="#Localizer["Language"]">
<a asp-action="SetLanguage" asp-route-returnUrl="#returnUrl" asp-route-culture="nl-NL" asp-controller="Home">
<img src="~/css/Languages/nl.png" />
</a>
<a asp-action="SetLanguage" asp-route-returnUrl="#returnUrl" asp-route-culture="en-US" asp-controller="Home">
<img src="~/css/Languages/en.png" />
</a>
<a asp-action="SetLanguage" asp-route-returnUrl="#returnUrl" asp-route-culture="de-DE" asp-controller="Home">
<img src="~/css/Languages/de.png" />
</a>
<a asp-action="SetLanguage" asp-route-returnUrl="#returnUrl" asp-route-culture="da-DK" asp-controller="Home">
<img src="~/css/Languages/dk.png" />
</a>
</div>
You can add the value of the query string to the end of the path.
Here is the modified code:
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~"
: $"~{Context.Request.Path.Value+ Context.Request.QueryString.Value}";
in your View you have a Codepart where you define the returnUrl you then proceed to give this to your HomeController where you set the language.
you can useContext.Request.Path to also find the value of your querystring.
//Checks if the path is empty. If not, check the value of your QueryString
var parameters = string.IsNullOrEmpty(Context.Request.Path) ? "" : $"{Context.Request.QueryString.Value }";
<a asp-action="SetLanguage" asp-route-returnUrl="#returnUrl" asp-route-parameters="#parameters" asp-route-culture="nl-NL" asp-controller="Home">
<img src="~/css/Languages/nl.png" />
You can create a Variable Parameters and do the same thing you did with your returnUrl.
Then in your HomeController you can use both your returnUrl and Parameters.
[HttpGet]
public IActionResult SetLanguage(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
//If parameters is null, only return returnUrl, else return both
if (parameters != null)
{
var item = returnUrl + parameters;
return LocalRedirect(item);
}
else { var item = returnUrl;
return LocalRedirect(item);
}
Console.WriteLine("The new CultureInfo is now: " + CultureInfo.CurrentCulture);
return LocalRedirect(returnUrl);
}
this reference helped me understand how to use the Context.Request.Path combined with looking at my own code.
I need to highlight current link in menu, But structure of HTML is look like below:
<li class="active">
<a href='#Url.Action("MainPage", "Ticket")'>
<i class="fa fa-fw fa-home"></i>
HomePage
</a>
</li>
I know there are many different solutions to do that (+, +):
public static MvcHtmlString MenuLink(
this HtmlHelper helper,
string text, string action, string controller)
{
var routeData = helper.ViewContext.RouteData.Values;
var currentController = routeData["controller"];
var currentAction = routeData["action"];
if (String.Equals(action, currentAction as string,
StringComparison.OrdinalIgnoreCase)
&&
String.Equals(controller, currentController as string,
StringComparison.OrdinalIgnoreCase))
{
return helper.ActionLink(
text, action, controller, null,
new { #class = "active" }
);
}
return helper.ActionLink(text, action, controller);
}
But my HTML structure is a little bit different.
How can I do that?
Any idea?
Finally I changed the code this way and it works: (Thanks to #Stephen Muecke)
public static MvcHtmlString MenuLink(
this HtmlHelper helper,
string content, string action, string controller)
{
var routeData = helper.ViewContext.RouteData.Values;
var currentController = routeData["controller"];
var currentAction = routeData["action"];
var builder = new TagBuilder("li")
{
InnerHtml = content
};
if (String.Equals(action, currentAction as string,
StringComparison.OrdinalIgnoreCase)
&&
String.Equals(controller, currentController as string,
StringComparison.OrdinalIgnoreCase))
{
builder.AddCssClass("active");
return MvcHtmlString.Create(builder.ToString());
}
return MvcHtmlString.Create(builder.ToString());
}
I need to highlight active link in the menu. My menu is in the master page by the way. I'm looking for the best way to implement this? Any ideas?
The best way to handle this is to write an HTML helper. You could use RouteData.Values["action"] to get the currently executing action and compare to the menu name and if they match apply a CSS class that will highlight it.
public static MvcHtmlString MenuItem(
this HtmlHelper htmlHelper,
string action,
string text
)
{
var menu = new TagBuilder("div");
var currentAction = (string)htmlHelper.ViewContext.RouteData.Values["action"];
if (string.Equals(
currentAction,
action,
StringComparison.CurrentCultureIgnoreCase)
)
{
menu.AddCssClass("highlight");
}
menu.SetInnerText(text);
return MvcHtmlString.Create(menu.ToString());
}
And then use this helper to render the menu items:
<%: Html.MenuItem("about", "About us") %>
<%: Html.MenuItem("contact", "Contact us") %>
...
I am always using this solution
Html Part
<ul>
#Html.ListItemAction("Home Page","Index","Home")
#Html.ListItemAction("Manage","Index","Home")
</ul>
Helper Part
public static class ActiveLinkHelper
{
public static MvcHtmlString ListItemAction(this HtmlHelper helper, string name, string actionName, string controllerName)
{
var currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
var currentActionName = (string)helper.ViewContext.RouteData.Values["action"];
var sb = new StringBuilder();
sb.AppendFormat("<li{0}", (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) &&
currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase)
? " class=\"active\">" : ">"));
var url = new UrlHelper(HttpContext.Current.Request.RequestContext);
sb.AppendFormat("{1}", url.Action(actionName, controllerName), name);
sb.Append("</li>");
return new MvcHtmlString(sb.ToString());
}
}
I use this solution:
First - add attribute data-menuPageId to your menu links
<ul class="menu">
<li data-menuPageId="home">
#(Html.Link<HomeController>(x => x.Index(), "Home"))
</li>
<li data-menuPageId="products">
#(Html.Link<ProductsController>(x => x.Index(), "Products"))
</li>
</li>
Next - add hidden field for current page Id to Layout.cshtml
<input type="hidden" id="currentPageId" value="#(ViewBag.Page ?? "home")" />
Add ViewBag.Page initializtion at your pages like Home or Products
#{
ViewBag.Page = "products";
}
And last thing you should reference this javascipt from yor Layout.cshtml
$(function() {
var pageIdAttr = "data-menuPageId";
var currentPage = $("#currentPageId").attr("value");
var menu = $(".menu");
$("a[" + pageIdAttr + "]").removeClass("selected");
$("a[" + pageIdAttr + "=\"" + currentPage + "\"]", menu).addClass("selected");
});
In layout try like following:
#{
string url = Request.RawUrl;
}
#switch (url)
{
case "/home/":
#Html.ActionLink("Home", "../home/", null, new { #style = "background:#00bff3;" })
#Html.ActionLink("Members", "../home/MembersList/")
break;
case "/home/MembersList/":
#Html.ActionLink("Home", "../home/")
#Html.ActionLink("Members", "../home/MembersList/", null, new { #style = "background:#00bff3;" })
break;
default:
#Html.ActionLink("Home", "../home/")
#Html.ActionLink("Members", "../home/MembersList/")
break;
}
The simplest solution:
1) Connect jQuery to #RenderBody ()
2) On Layout
...
<li class="nav-item">
<a class="nav-link text-dark" id="navItemPortfolio" asp-area="" asp-controller="Home" asp-action="Portfolio">Portfolio</a>
</li>
...
3) Write something similar on your page (View)
<script>
$(function () {
$("#navItemPortfolio").addClass("active");
});
</script>
I'm pretty sure you can do this with pure CSS. It involves class selectors and faffing around with the body tag. I would go with the helper method every day of the week.
I have created a custom helper for tracking the current menu item in my MVC application and would now like to replace the icons I was using before the change and set the relevant href. Previously my list items looked like this:
<li><i class="icon-lock"></i>Admin</li>
<li><a class="active" href="#Url.Action("Index", "Home")"><i class="icon-home"></i>Home</a></li>
I now have created the generic menu item by using the following code:
using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace MetaLearning.Helpers
{
public static class MenuExtensions
{
public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper,
string text, string action,
string controller,
string iconClass,
object routeValues = null,
object htmlAttributes = null)
{
var li = new TagBuilder("li");
var routeData = htmlHelper.ViewContext.RouteData;
var currentAction = routeData.GetRequiredString("action");
var currentController = routeData.GetRequiredString("controller");
li.InnerHtml = #"<a class=""active"" href=""/""><i class=""icon-home""></i>Home</a>";
if (currentController.Contains(text))
{
li.InnerHtml = #"<a class=""active"" href=""/""><i class=""icon-home""></i>"+ text + "</a>";
}
else
{
li.InnerHtml = #"<i class=""icon-home""></i>" + text + "";
}
return MvcHtmlString.Create(li.ToString());
}
}
}
How can I set the href to the "/controller" and the name of the icon to iconClass. I have tried the following:
li.InnerHtml = #"<a class=""active"" href=""/""><i class=""icon-home""></i>"+ text + "</a>";
With this I am successfully showing the correct text but when I try to replace the href or icon class names with the relevant variables my string formation is incorrect
Try not to concat strings with '+' operator this is not good behavior here you can read more yourself.
How can I set the href to the "/controller" and the name of the icon
to iconClass.
li.InnerHtml = string.Format("<i class=\"{2}\" /i>{1}", controller, iconClass, text);
I am new to ASP.net mvc could any body help me out how to convert this whole code to use html.Actionlink
<div class="MenuItemContainer">
<a href="/Home">
<div class="MenuItemContent">
<div>
<img src="/Content/TopMenu/Icons/Home.png" alt="Home" />
</div>
<div>
Home
</div>
</div>
</a>
</div>
Thanks
You could write a custom html helper:
public static class HtmlExtensions
{
public static IHtmlString MyActionLink(
this HtmlHelper htmlHelper,
string linkText,
string action,
string controller,
string imageUrl,
string alt
)
{
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var div = new TagBuilder("div");
div.AddCssClass("MenuItemContainer");
var anchor = new TagBuilder("a");
anchor.Attributes["href"] = urlHelper.Action(action, controller);
var itemContent = new TagBuilder("div");
itemContent.AddCssClass("MenuItemContent");
var img = new TagBuilder("img");
img.Attributes["src"] = urlHelper.Content(imageUrl);
img.Attributes["alt"] = alt;
itemContent.InnerHtml = string.Format(
"<div>{0}</div><div>{1}</div>",
img,
htmlHelper.Encode(linkText)
);
anchor.InnerHtml = itemContent.ToString();
div.InnerHtml = anchor.ToString();
return new HtmlString(div.ToString());
}
}
and then use it:
<%= Html.MyActionLink(
"Hello",
"Index",
"Home",
"~/Content/TopMenu/Icons/Home.png",
"Home"
) %>
You need to create custom a custom Html Helper, such as Helpers.MenuLink("LinkText", "View", "ImagePath/Image.png").
Here is a link that explains how to do this:
http://www.asp.net/mvc/tutorials/older-versions/views/creating-custom-html-helpers-cs
I would make a partial or a display template for this which renders a View Model. Your HtmlHelper extension method would create your view model and call DisplayFor or RenderPartial.
This has the advantage of keeping your template as HTML, this makes it easier to read as opposed to messing around with the tagbuilders.