What do you find to provide the best menu for an ASP.Net 2.0 - 3.5 web application? Suggestions do not have to particularly be ASP.Net controls, but could be other menu's that work well within an ASP.Net web application.
I would like for the suggestions to be options that do not require purchasing or royalty fees. OpenSource suggestions would be even better.
I've found that the most flexible is to use CSS to style an unordered list like this:
<div id="nav_main" >
<ul>
<li id="current">Button 1</li>
<li>Button 2</li>
<li>Button 3</li>
<li>Button 4</li>
<li>Button 5</li>
</ul>
</div>
You'll find many CSS ways to style this kind of list with hover background images, etc.
Now if you are using Webforms and you want to use your sitemap, then I would suggest using a Repeater and NOT use the menu control. You will have the most control of your generating your list this way.
Similarly, if you are using ASP.NET MVC, you can do a foreach on your sitemap to create your list.
This of course is just for simple menus, but it can be expanded to include more complicated menus. I've found the following CSS-styled menu to be very good: http://www.lwis.net/free-css-drop-down-menu/
YUI buttons or menu controls (works with existing HTML):
http://developer.yahoo.com/yui/examples/button/btn_example07.html
An ASP.NET library that wraps these controls very nicely, released in December 2008:
http://www.codeplex.com/YUIAspNet/
JQuery suckerfish menu, works by using ul,li elements:
http://be.twixt.us/jquery/suckerFish.php
I use jQuery Superfish: http://users.tpg.com.au/j_birch/plugins/superfish/. Highly recommended by others as well.
I also like to create unordered lists. It lets the designer be flexible with the menus. They can use their own js+css solution to create drop down menus or style it nicely for static menus. The same html can easily become left hand, across the top, drop down, or even a full site map with css changes.
To that note, I like to store the site map data in a hierarchal data structure and use a recursive lambda to generate it. For an example see this small console app below with its output.
output html
<li>First<li>FirstSub</li><li><a hr
ef="/secondsub.aspx">SecondSub</a></li></li><li><a href="/second.aspx">Second</a
></li>
the source c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SiteMapDemo
{
class MenuItem
{
public Guid ID {get; set;}
public Guid? ParentID{get;set;}
public string Name { get; set; }
public string Path { get; set; }
public int Rank { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<MenuItem> menu = new List<MenuItem>(new[]{
new MenuItem{ID = Guid.NewGuid(), Name = "First", ParentID=null, Path="/", Rank=0},
new MenuItem{ID = Guid.NewGuid(), Name = "Second", ParentID=null, Path="/second.aspx",Rank=1},
});
menu.AddRange(new[] {
new MenuItem{ID = Guid.NewGuid(), Name = "FirstSub", ParentID=menu[0].ID, Path="/firstsub.aspx",Rank=0},
new MenuItem{ID = Guid.NewGuid(), Name = "SecondSub", ParentID=menu[0].ID, Path="/secondsub.aspx",Rank=1},
});
Func<List<MenuItem>, Guid?, string> renderMenu = null;
renderMenu = (menus, Parent) =>
{
var sub = menus.Where(m => m.ParentID == Parent).OrderBy(s=>s.Rank).ToList();
if (sub.Count > 0)
{
StringBuilder sb = new StringBuilder();
sub.ForEach(s => { sb.Append(String.Format("<li>{1}{2}</li>", s.Path, s.Name, renderMenu(menus, s.ID))); });
return sb.ToString();
}
return "";
};
Console.Write(renderMenu(menu, null));
Console.ReadLine();
}
}
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
We have recently decided to switch from asp.net webforms to asp.net mvc for some new projects. As a long time webforms developer I have watched and read quite a lot of introductory tutorials and how to switch articles and video's. However some things still bug me (no pun intended) a litte bit. And I'm hoping I could get some answers from the community here.
First, we have a cms-system that we are required to use for a client. In the system they obviously manage the navigation (menu) for the webproject. My first question is, how would I go about to adding this dynamic navigation to all the pages (views). In webforms I would simply create a custom user control and throw it onto the masterpage or template. I suspect the answer to be viewmodels? Or partial views?
The second question is related. Often we would have a few pages that show some news for example in a side panel. Others would not. Would I create a different viewmodel for the different scenarios, work with sections?
I don't expect a definite answer, but more some guidelines/best practices. Any input is appreciated.
Your question is extremely broad so I'll only give a high level way of achieving this.
For the menu system you can create two Div elements, one for the menu and one for the content. The menu div should contain an Action that calls JavaScript to replace the content of the main Div with a Partial View returned by your Controller.
In your _Layout page you can something like this
<body>
#Html.Action("RetrieveSideBar", "SideBar", new { area = "" })
<div id="mainScreenDiv">
#RenderBody()
</div>
</body>
RetrieveSideBar renders the menu items
public class SideBarController : MyControllerBase {
/// <summary>
/// Retrieves the side bar.
/// </summary>
/// <returns></returns>
public ActionResult RetrieveSideBar() {
var menuItems = new List<MenuItem>();
return PartialView("_SideBar", MenuItems());
}
private List<MenuItem> NotAuthenticatedMenuItems() {
var menuItems = new List<MenuItem>();
menuItems.Add(new MenuItem() { Title = "Register Firm", ActionUrl = Url.Action("Create", "FirmPreRegistration") });
return menuItems;
}
}
Where MenuItem is
public class MenuItem {
public string Title { get; set; }
public string ActionUrl { get; set; }
}
_SideBar renders the menu as follows
#model List<MenuItem>
<ul class="nav nav-sidebar">
#foreach (var menuItem in Model) {
<li class="text-info leftMenuHeading ">
<a href="#" onclick="NavigateTo('#menuItem.ActionUrl')">
<span>#menuItem.Title</span>
</a>
</li>
}
</ul>
Note that clicking on a menu item calls NavigateTo which is defined as follows in the '_Layout' page
function NavigateTo(view) {
$.ajax({
url: view,
type: "GET",
cache: false,
success: function(data) {
$('#mainScreenDiv').html(data);
});
}
This creates an Ajax based navigation system that will replace the content in the page with the navigated page, note that Action methods need to return PartialView or else _Layout renders again.
My first question is, how would I go about to adding this dynamic navigation to all the pages (views). In webforms I would simply create a custom user control and throw it onto the masterpage or template.
Asp.Net MVC has something similar - _Layout.cshtml. By default it is inside Views/Shared folder and it is like master page for all pages. You can generate your dynamic menu in following way
public abstract class BaseController : Controller
{
public YourMenuModel YourMenuModel { get; set; }
public BaseController()
{
this.YourMenuModel = //load dynamically your menu. For example from database
ViewBag.MenuModel = this.YourMenuModel ;
}
}
Inside _layout view you can get your menu model and render in any way
#{
var YourMenuModel = (YourMenuModel)ViewBag.MenuModel;
}
Often we would have a few pages that show some news for example in a side panel. Others would not. Would I create a different viewmodel for the different scenarios, work with sections?
I would create partial view for news and put it inside some views
#Html.Partial("_YourNewsPartial")
i have created a master page with lots of effort but i am not able to figure out how to make my tab selected based on the user click.
i have used one method to do that but finding very complicated as i have pass a viewdata from each of my controller which i dont like it, below is how i have done code in controller
ViewData["ActiveMenu"] = "Inbox";
and in my master page i have written a jquery like below to make the tab highlighted.
$('#lnkInbox').mouseout(function () {
$('#aInbox').removeClass('aInbox-Hover');
$('#aInbox').addClass('aInbox');
//put hover effect on the selected menu
var activeMenu = '<%:ViewData["ActiveMenu"] %>';
if (activeMenu == "Account") {
$('#aAccount').removeClass('aAccount');
$('#aAccount').addClass('aAccount-Hover');
}});
this is how i am doing but is there any other way i can do that...
please suggest
i have found one good link active menu item - asp.net mvc3 master page
but the answer which is showed there i am not able understand how to i utilize in my code and where to write the code in my project.
Use the answer you found.
To create helper class add new class to you project, i.e. like this
public static class LinkHelpers
{
//copy here the first block of code from the answer
}
Add to your HomeController methods (probably you already have them):
public class HomeController : Controller
{
public ActionResult About()
{
return View()
}
public ActionResult Index()
{
return View()
}
}
Create respective views and add to your master page
<ul>
<li>#Html.MenuLink("Main", "Index", "Home")</li>
<li>#Html.MenuLink("About us", "About", "Home")</li>
</ul>
And finally in your css file declare
.current{background-color:red;}
In my application I have a lot of UI elements (particuarly buttons) where I am repeating the same code over and over.
For example in multiple views I might have an edit button that is created with the following code:
#Html.ActionLink("Edit", "Edit", "SomeController", null, new { #class="button large blue" });
The problem here is that I am hard coding the label and styling, so if I want to make changes to the button styling or label, I would have to make them in multiple views. This would be tedious to do, as I would have to track down every button.
So now I am looking at creating a templating mechanism, where I can define a button template in a central area and bring it in to any view I want to use it in.
I have considered two options, which I have tried to sketch out in semi-real world code below.
My question is, am I on the right track here? Which option would be better and for which reasons? Is there already something out there I could consider using, or is there another way which I haven't thought of?
Thanks for your help.
Web.Config Templates
Create a custom class that can bring in template configuration from web.config file. For example:
<ui.HtmlTemplates>
<add templateKey="FormCancel" tag="a" class="form-button large black" />
<add templateKey="FormSave" tag="input" type="submit" class="form-button large green" />
</ui.HtmlTemplates>
And then could call them in with syntax such as this (method signature is contrived)
#HtmlTemplates.Build("FormCancel", Url.Action("Index", "Home"))
Partial View Templates
Create strongly typed partial views with the template I want.
ViewModel
public class UiButtonModel
{
public string Url{ get; set; }
}
Partial View
// Assume the file is called "_Button_FormCancel"
#Model path.to.model.directoy.UiButtonModel
Cancel
Use
#Html.Partial("_Button_FormCancel", new UiButtonModel(){Url = Url.Action("Index", "Home"));
Another option is to create extension methods off of HtmlHelper to create prepackaged HTML output using code:
using System.Web.Mvc;
using System.Web.Routing;
public static class MyHtmlExtensions
{
public static string SaveButton(this HtmlHelper helper, string title = "Save", IDictionary<string, object> htmlAttributes = null)
{
var builder = new TagBuilder("button");
builder.Attributes.Add("type", "button");
builder.AddCssClass("form-button");
builder.AddCssClass("large");
builder.AddCssClass("green");
if (htmlAttributes != null) builder.MergeAttributes(htmlAttributes);
builder.SetInnerText(helper.Encode(title));
return builder.ToString();
}
public static string CancelButton(this HtmlHelper helper, string title = "Cancel", string actionName, string controllerName, RouteValueDictionary routeValues = null, IDictionary<string, object> htmlAttributes = null)
{
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection);
var builder = new TagBuilder("a");
builder.Attributes.Add("href", urlHelper.Action(actionName, controllerName, routeValues));
builder.AddCssClass("form-button");
builder.AddCssClass("large");
builder.AddCssClass("green");
if (htmlAttributes != null) builder.MergeAttributes(htmlAttributes);
builder.SetInnerText(helper.Encode(title));
return builder.ToString();
}
}
Then just make sure the namespace of MyHtmlExtensions is either added to your page directly, or included in all pages via web.config, and use it like this in your view (razor syntax):
<div class="form-buttons">
#Html.CancelButton("Index", "Home")
#Html.SaveButton()
</div>
This method is particularly well suited for creating output consistently across several solutions, as all you need to do is reference the containing assembly and import the namespace.
I create these kinds of templates and put them in my Views/Shared folder.
I have templates like:
AddButton.cshtml
DeleteButton.cshtml
SaveButton.cshtml
...
Then, when I need to call one of them in whatever View, I just call this for example:
#Html.Partial("SaveButton");
Using T4MVC, it gets even better with compile time checking (no more literal strings):
#Html.Partial(MVC.Shared.Views.SaveButton)
Doing so I have a common/central place to change a specific button config. No need to go view after view to change something.
This is the problem that css was designed to handle. I fail to understand the problem. If you want to make changes, you change the CSS and it affects all the buttons that have that styling.
Part of your problem is that you're using style like "blue". If you want to change it to red, you have to change it everywhere.
Instead, you should have a class for the button, then you can simply change the button style and you don't have to worry about redefining blue to red.
I used the following tutorial to help me build an RSS Reader in my ASP.NET MVC3 Razor application:
http://weblogs.asp.net/jalpeshpvadgama/archive/2011/08/17/creating-basic-rss-reader-in-asp-net-mvc-3.aspx
However, unlike the tutorial example, I want the RSS feed to be displayed on every page, and have therefore added it to my layout file, /Views/Shared/_Layout.cshtml
I currently only have 2 views on my site, and to get the RSS Reader to work on both views I've got the following code in my HomeController:
public class HomeController : Controller
{
//
// GET: /Index/
public ActionResult Index()
{
return View(CT.Models.RssReader.GetRssFeed());
}
public ActionResult About()
{
return View(CT.Models.RssReader.GetRssFeed());
}
}
From my WebForms experience, I would simply add the RSS Reader code in my master page code behind, and it would automatically work on every page.
Is there a Controller for layout pages which allows me to do the same?
How can I get this to work on every call of the layout page, without having to return anything?
EDIT: Following #Sebastian's advice, I've now added this code to a Partial View, removed CT.Models.RssReader.GetRssFeed() from return View() and included this in my layout file:
#Html.Partial("_MyPartialView")
The code in this partial view is:
<ul>
#foreach (var item in Model)
{
<li>
#item.Title
</li>
}
</ul>
However, I'm not getting a runtime error:
Object reference not set to an instance of an object.
It's erroring on the line #foreach (var item in Model)
You have to create a partial view and add functionality there.
Then in your layout, render this partial.
EDIT
Is your partial view really a partial view? The reason I said that is because you have "_" in front of the name which suggests that it might be a layout (might just be a naming convention).
To fix object reference error, you have to add the #Model declaration on top of your partial view.
Hope it helps.
UPDATE
In order to use different model in partial view, you need to explicitly declare which model you are going to use on render partialmethod.
#{Html.RenderPartial("../YourFeed", Model.YourFeedModel);}
Let me know if that resolved your issue.
The new error you are having is due to you not passing a Model to the partial view. You can do this with the second argument of the Html.Partial function...
Html.Partial("ViewName", MyModel);
As I think you are trying to do this in a Layout page you could also consider using a static reference to get your RSS feed. So forget about needing to pass in a Model and in your partial have:
#foreach (var item in RssRepository.GetFeed())
{
<li>
#item.Title
</li>
}
this like to a class something like...
public static RssRepository
{
public static MyModel GetFeed()
{
return new MyModel();//<- return what you would normally pass as a Model for RSS feeds
}
}
Hope that all makes sense
I have created an asp.net mvc2 project using the standard visual studio template which contains 2 tabs Home and About.
In the Home tab I want to add sub tabs from here http://labs.silverorange.com/archive/2003/september/simplecsstabs
How do I do this : can I create sub-component and sub-controller or what ?
I'm beginner at asp.net and MVC so can't there be a beginner solution that is close as much as possible to t to the plain css tabs ?
You could write a custom HTML helper that will generate those sub tabs based on the current controller and action. Here's an example to get you started:
public static class HtmlExtensions
{
public static MvcHtmlString SubTabs(this HtmlHelper htmlHelper)
{
var currentAction = htmlHelper.ViewContext.RouteData.GetRequiredString("action");
var currentController = htmlHelper.ViewContext.RouteData.GetRequiredString("controller");
// TODO: base on the current action and controller
// generate proper tabs
var ul = new TagBuilder("ul");
var lis = new StringBuilder();
var li = new TagBuilder("li");
li.InnerHtml = htmlHelper.ActionLink("sub 1", "sub1", "home").ToHtmlString();
lis.AppendLine(li.ToString());
li = new TagBuilder("li");
li.InnerHtml = htmlHelper.ActionLink("sub 2", "sub2", "home").ToHtmlString();
lis.AppendLine(li.ToString());
ul.InnerHtml = lis.ToString();
return MvcHtmlString.Create(ul.ToString());
}
}
and then in your view:
<%= Html.SubTabs() %>
Obviously in my example everything is hardcoded. You might want to keep some centralized hashtable of sub tabs corresponding to given controller and action. Once you get the proper markup simply apply the CSS from the site you are referred to in your question and you are good to go.