2008-10-18 7 views
34

W domyślnym ASP.NET MVC projektu w pliku Site.Master znajduje się lista menu nawigacyjne:Dodanie „aktywny” znacznik do listy nawigacji w ASP.NET MVC głównej stronie

<div id="menucontainer"> 
    <ul id="menu">    
     <li><%= Html.ActionLink("Home", "Index", "Home")%></li> 
     <li><%= Html.ActionLink("About Us", "About", "Home")%></li> 
    </ul> 
</div> 

ten renderuje w przeglądarce, aby:

<div id="menucontainer"> 
    <ul id="menu">    
     <li><a href="/">Home</a></li> 
     <li><a href="/Home/About">About Us</a></li> 
    </ul> 
</div> 

chcę, aby móc dynamicznie ustawić aktywny element listy, opiera się na poglądzie, że jest nazywany. Oznacza to, że gdy użytkownik patrzy na stronie głównej, chciałbym następujące HTML mają być utworzone:

<div id="menucontainer"> 
    <ul id="menu">    
     <li class="active"><a href="/">Home</a></li> 
     <li><a href="/Home/About">About Us</a></li> 
    </ul> 
</div> 

Spodziewam się, że sposobem na to byłoby coś takiego:

<div id="menucontainer"> 
    <ul id="menu">    
     <li <% if(actionName == "Index"){%> class="active"<%}%>><%= Html.ActionLink("Home", "Index", "Home")%></li> 
     <li <% if(actionName == "About"){%> class="active"<%}%>><%= Html.ActionLink("About Us", "About", "Home")%></li> 
    </ul> 
</div> 

Kluczowym bitem jest tutaj linia <% if(actionName == "Index"){%> class="active"<%}%>. Nie wiem, jak określić, czym jest bieżące actionName.

Wszelkie sugestie, jak to zrobić? Lub, jeśli jestem na zupełnie niewłaściwym torze, czy jest lepszy sposób na zrobienie tego?

Odpowiedz

0

Fakt, że Twój widok musi wiedzieć o działaniach kontrolera, zrywa z wzorcem MVC. Być może kontroler może przekazać do widoku jakieś "kontrolne" informacje, aby ostatecznie pozwolić mu osiągnąć to samo, jedyna różnica polega na tym, kto jest odpowiedzialny za to.

Podobnie jak w akcji kontrolera za mogłeś:

public ActionResult Index(){ 
    ViewData["currentAction"] = "Index"; 
    //... other code 
    return View(); 
} 

Następnie nad Państwa zdaniem można było:

<% if(((string)ViewData["currentAction"]) == "Index" {%> <!- some links --><% } %> 
<% if(((string)ViewData["currentAction"]) == "SomethingElse" {%> <!- some links --><% } %> 

Jednak im więcej o tym myślę, tym bardziej mam pytanie dlaczego używasz ten sam widok dla wielu akcji. Czy widok jest podobny do tego?

Jeśli uzasadnia to przypadek użycia, przejdź do powyższej sugestii. Ale w przeciwnym razie może uda ci się przełamać różne widoki (po jednym dla każdej akcji kontrolera) i problem sam się rozwiązuje.

-1

Spróbuj

powinien działać dobrze !!!

EDIT: USUNIĘTO beta1

Usunięto właściwość VIEWNAME z klasy ViewContext.

10

Wewnątrz widzenia, można uzyskać nazwę bieżącego działania z:

ViewContext.RouteData.Values["action"].ToString() 
0

na podstawie poprzedniej odpowiedzi, o to co moje obecne rozwiązanie jest dla tego samego problemu:

na stronie głównej Podaję każdemu li identyfikator odpowiadający kontrolerowi i akcji, ponieważ powinien on być znany z ActionLinka. Wcześniej robiłem to z tytułem strony, ale to pomaga w organizacji.

Witryna.Master:

<ul id="menu"> 
    <li id="menuHomeIndex" runat="server"><%= Html.ActionLink("Home", "Index", "Home") %></li> 
    <li id="menuHomeAbout" runat="server"><%= Html.ActionLink("About Us", "About", "Home") %></li> 
</ul> 

Site.Master.cs:

// This is called in Page_Load 
private void SetActiveLink() 
{ 
    string action = "" + ViewContext.RouteData.Values["controller"] + ViewContext.RouteData.Values["action"]; 
    var activeMenu = (HtmlGenericControl)Page.Master.FindControl("menu" + action); 

    if (activeMenu != null) 
    { 
     activeMenu.Attributes.Add("class", "selected"); 
    } 
} 

Jest więcej pracy niż kod inline ale myślę, że jest czystsze, a także pozwala mieć działania o tej samej nazwie w różnych kontrolerów. Jeśli dodasz więcej pozycji menu z różnymi kontrolerami, nie wszystkie akcje o nazwie Indeks zostaną podświetlone w menu.

Jeśli ktoś zauważy problemy z tym podejściem, proszę dać mi znać.

10

Można również spróbować wykryć, która jest aktualnie wybraną zakładką, od nazwy kontrolera i nazwy widoku, a następnie dodać atrybut klasy.

public static string MenuActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName) 
{ 
    var htmlAttributes = new RouteValueDictionary(); 

    if (helper.ViewContext.Controller.GetType().Name.Equals(controllerName + "Controller", StringComparison.OrdinalIgnoreCase)) 
    { 
     htmlAttributes.Add("class", "current"); 
    } 

    return helper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), htmlAttributes); 
} 
+3

Jeśli otrzymujesz błąd po powrocie helper.ActionLink Dodaj to do twoich referencji: używając System.Web.Mvc.Html; – Mike

+2

powinien zwrócić MvcHtmlString, a nie ciąg – BjarkeCK

1

To powinno działać przy użyciu jQuery po stronie klienta rzeczy, korzysta z Google, aby służyć najnowszą bibliotekę jQuery:

<script src="http://www.google.com/jsapi" type="text/javascript" language="javascript"></script> 
<script type="text/javascript" language="javascript">google.load("jquery", "1");</script> 

<script language="javascript" type="text/javascript"> 
     $(document).ready(function(){ 
      var str=location.href.toLowerCase(); 
     $('#menucontainer ul#menu li a').each(function() { 
       if (str.indexOf(this.href.toLowerCase()) > -1) { 
         $(this).attr("class","current"); //hightlight parent tab 
        } 
       }); 
     }); 
    </script> 
+5

Nie rób tego. – Brady

+0

@Brady czemu nie? Wygląda na solidne rozwiązanie. –

37

zrobiłem sobie sposób pomocniczy do obsługi tego typu rzeczy. W kodzie z tyłu mojej strony wzorcowej (może być popchnięta do metody rozszerzenia ... prawdopodobnie lepsze podejście), wstawiam następujący kod.

protected string ActiveActionLinkHelper(string linkText, string actionName, string controlName, string activeClassName) 
{ 
    if (ViewContext.RouteData.Values["action"].ToString() == actionName && 
      ViewContext.RouteData.Values["controller"].ToString() == controlName) 
     return Html.ActionLink(linkText, actionName, controlName, new { Class = activeClassName }); 

    return Html.ActionLink(linkText, actionName, controlName); 
} 

Potem wystarczy zadzwonić go w moją stronę tak:

<%= ActiveActionLinkHelper("Home", "Index", "Home", "selected")%> 
+0

dziękuję, użyję twojej metody ... –

+1

nowa {Class = "selected"} powinna być nowa {Class = activeClassName}; P –

+0

Awesome. Szukałem tego właśnie. – Stevus

0

Korzystanie MVC3 brzytwą View, można wdrożyć to tak:

<ul id="menu"> 
@if (ViewContext.RouteData.Values["action"].ToString() == "Index") 
{ 
<li class="active">@Html.ActionLink("Home", "Index", "Home")</li> 
} 
else 
{ 
<li>@Html.ActionLink("Home", "Index", "Home")</li> 
} 
@if (ViewContext.RouteData.Values["action"].ToString() == "About") 
{ 
<li class="active">@Html.ActionLink("About", "About", "Home")</li> 
} 
else 
{ 
<li>@Html.ActionLink("About", "About", "Home")</li> 
} 
</ul> 

oraz dostosować do styl Twojej ".aktywnej" klasy, takiej jak:

ul#menu li.active 
{ 
text-decoration:underline; 
} 
9

W MVC 3 Razor View Engine, można to zrobić jak:

@{string ctrName = ViewContext.RouteData.Values["controller"].ToString();} 

<div id="menucontainer"> 
    <ul id="menu"> 
    <li @if(ctrName == "Home"){<text> class="active"</text>}>@ Html.ActionLink("Home", "Index", "Home")</li> 
    <li @if(ctrName == "About"){<text> class="active"</text>}>@ Html.ActionLink("About Us", "About", "Home")</li> 
    </ul> 
</div> 

Moja próbka pracował kiedy mam dwie strony jak: Home/O i jego regulator ma sama nazwa Index, więc mam kontrolera Nazwa wyróżnienia insteed działania. Jeśli chcesz uzyskać działanie, po prostu zamień na:

@{string ctrName = ViewContext.RouteData.Values["action"].ToString();} 
+0

dzięki proste i skuteczne! – castors33

+0

Świetne rozwiązanie, dzięki! –

+0

prawdopodobnie warto też zrobić coś w stylu: @if (ctrName.ToLower() == "home") aby uwzględnić każdego, kto ręcznie pisze w adresach URL. W przeciwnym razie okaże się, że ktoś może odwiedzić twoją stronę w/home (zamiast/Home), a menu nie jest wyświetlane jako aktywne. Uwaga: jeśli używasz "ToLower()", upewnij się, że zmienna "ctrName" nigdy nie może być pusta, w przeciwnym razie możesz rzucić wyjątek. Na przykład. zdefiniuj "ctrName" jak: string ctrName = ""; if (ViewContext.RouteData.Values ​​["action"]! = Null) { ctrName = ViewContext.RouteData.Values ​​["akcja"]. ToString(); } –

0

Oto wersja zgodna z aktualną wersją MVC4.
Napisałem kod Adama Carra jako metodę rozszerzenia.

using System; 
using System.Web.Mvc; 
using System.Web.Mvc.Html; 
using System.Web.Routing; 

namespace MyApp.Web { 
    public static class HtmlHelpers { 
     /// <summary> 
     /// Returns an anchor element (a element) that contains the virtual path of the 
     /// specified action. If the controller name matches the active controller, the 
     /// css class 'current' will be applied. 
     /// </summary> 
     public static MvcHtmlString MenuActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName) { 
      var htmlAttributes = new RouteValueDictionary(); 
      string name = helper.ViewContext.Controller.GetType().Name; 

      if (name.Equals(controllerName + "Controller", StringComparison.OrdinalIgnoreCase)) 
       htmlAttributes.Add("class", "current"); 

      return helper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), htmlAttributes); 
     } 
    } 
} 
+2

helper.ActionLink nie kompiluje się –

+0

@AndyBrudtkuhl przy użyciu System.Web.Mvc.Html; dostaje go do kompilacji – m4chine

6

Aby przyczynić się własną odpowiedź (sprawdzone MVC4), wziąłem kilka najlepsze kawałki innych odpowiedzi, naprawiono kilka problemów, i dodaje pomocnika do pracy z adresów URL, które nie muszą być rozwiązane za pomocą kontrolera & Akcja (np. jeśli masz wbudowany CMS do czynienia z kilkoma linkami stronie, etc.)

Kod jest również forkable na github: https://gist.github.com/2851684

/// 
/// adds the active class if the link's action & controller matches current request 
/// 
public static MvcHtmlString MenuActionLink(this HtmlHelper htmlHelper, 
    string linkText, string actionName, string controllerName, 
    object routeValues = null, object htmlAttributes = null, 
    string activeClassName = "active") 
{ 
    IDictionary htmlAttributesDictionary = 
     HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); 

    if (((string)htmlHelper.ViewContext.RouteData.Values["controller"]) 
      .Equals(controllerName, StringComparison.OrdinalIgnoreCase) && 
     ((string)htmlHelper.ViewContext.RouteData.Values["action"]) 
      .Equals(actionName, StringComparison.OrdinalIgnoreCase)) 
    { 
     // careful in case class already exists 
     htmlAttributesDictionary["class"] += " " + activeClassName; 
    } 

    return htmlHelper.ActionLink(linkText, actionName, controllerName, 
            new RouteValueDictionary(routeValues), 
            htmlAttributesDictionary); 
} 

/// 
/// adds the active class if the link's path matches current request 
/// 
public static MvcHtmlString MenuActionLink(this HtmlHelper htmlHelper, 
    string linkText, string path, object htmlAttributes = null, 
    string activeClassName = "active") 
{ 
    IDictionary htmlAttributesDictionary = 
     HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); 
    if (HttpContext.Current.Request.Path 
     .Equals(path, StringComparison.OrdinalIgnoreCase)) 
    { 
     // careful in case class already exists 
     htmlAttributesDictionary["class"] += " " + activeClassName; 
    } 
    var tagBuilder = new TagBuilder("a") 
          { 
           InnerHtml = !string.IsNullOrEmpty(linkText) 
               ? HttpUtility.HtmlEncode(linkText) 
               : string.Empty 
          }; 
    tagBuilder.MergeAttributes(htmlAttributesDictionary); 
    tagBuilder.MergeAttribute("href", path); 
    return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal)); 
}
1

chciałem mieć nieco większą kontrolę nad moim układ, i to jest to, co zrobiłem.

Tworzenie LayoutModel że inne modele dziedziczą:

public abstract class LayoutModel 
{ 
    public CurrentPage CurrentPage { get; set; } 
} 

Tworzenie LayoutAttribute która dziedziczy ActionFilterAttribute tak:

public class LayoutAttribute : ActionFilterAttribute 
{ 
    private CurrentPage _currentPage { get; set; } 

    public LayoutAttribute(
     CurrentPage CurrentPage 
    ){ 
     _currentPage = CurrentPage; 
    } 

    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     var result = filterContext.Result as ViewResultBase; 
     if (result == null || result.Model == null || !(result.Model is LayoutModel)) return; 

     ((LayoutModel)result.Model).CurrentPage = _currentPage; 
    } 
} 

obecnie na działaniu lub poziomu sterownika można ustawić bieżącą stronę (i inne rzeczy, jeśli chcę) w następujący sposób:

[Layout(CurrentPage.Account)] 
public class MyController : Controller 
{ 

} 

W moim ayout view Mam teraz dostęp do bieżącej strony i cokolwiek jeszcze dodam do LayoutModel.

3

Korzystanie MVC3 brzytwą View oferuje inną opcję:

_Layout.cshtml:

<li class="@ViewBag.NavClassHome">@Html.ActionLink("Home", "Index", "Home")</li> 
<li class="@ViewBag.NavClassAbout">@Html.ActionLink("Disclaimer", "About", "Home")</li> 

HomeController:

public ActionResult Index() { 
    ViewBag.NavClassHome = "active"; 
    return View(); 
} 

public ActionResult About() { 
    ViewBag.NavClassAbout = "active"; 
    return View(); 
} 

Jeśli chcesz zachować to na odświeżenie strony jak również, tu również musisz przypisać wartość ViewBag:

[HttpPost] 
public ActionResult Index() { 
    ViewBag.NavClassHome = "active"; 
    return View(); 
} 

[HttpPost] 
public ActionResult About() { 
    ViewBag.NavClassAbout = "active"; 
    return View(); 
} 

Testowane i działa dobrze dla mnie, ale będziesz mieć nazwę klasy css w swoim kodzie po stronie serwera.

4

Stare pytanie, ale mam nadzieję, że ktoś może uznać to za bardzo pomocne.

  1. umieścić kilka rzeczy, które można wykorzystać do identyfikacji strony w ViewBag, użyłem ViewgBag.PageName

Na przykład w index.cshtml, umieścić coś

@{ 
    ViewBag.PageName = "Index"; 
} 
  1. Dodaj zajęcia do każdego elementu linku z warunkiem oświadczenie, aby zwrócić aktywną, jeśli odwiedzana strona ma wymaganą wartość lub zwraca pusty łańcuch w przeciwnym razie. Zobacz szczegóły poniżej:

<li class="@((ViewBag.PageName == "Index") ? "active" : "")"><a href="@Url.Action("Index","Home")">Home</a></li> 
 
<li class="@((ViewBag.PageName == "About") ? "active" : "")"><a href="@Url.Action("About","Home")">About</a></li> 
 
<li class="@((ViewBag.PageName == "Contact") ? "active" : "")"><a href="@Url.Action("Contact","Home")">Contact</a></li>

nie tylko przetestować go, używam tej metody w moich projektach

+0

To była jedyna rzecz, którą znalazłem, która działała. Dziękuję Ci. Używam MVC4. –

Powiązane problemy