For my learning, I'm building a document management solution with ASP.NET MVC3. Below are pages I manage:
a search/result page (list of items)
a favorite page (list of items)
an edit page
a create page
I also have a Site.Master page where I show a treeview menu on the left side of the screen. So wherever the user is located in the website, the treeview menu is showing his location by underlining his location in the menu.
For building the treeview menu, I use the code below (cleaned for easy reading):
<ul id="treemenu1" class="treeview">
<li>Documents
<ul>
<%= Html.TreeviewMenu(TreeMenu.Create("Search", "Search", "Affaires", null))%>
<%= Html.TreeviewMenu(TreeMenu.Create("Favorite", "Favorite", "Affaires", null))%>
<%= Html.TreeviewMenu(TreeMenu.Create("New", "Create", "Affaires", null))%>
</ul>
</li>
</ul>
The problem is that I need to underline the active item in my menu. So if user is displaying the search page, my search menu entry must be underlined. How can I proceed? I was thinking about the integration of this information in the strongly typed viewmodel passed to each viewpage but it failed because each page is using a different viewmodel. I prefer not using a session variable because it is not a clean solution.
Any ideas?
A solution with a session variable: I save the "current menu item" in a session variable (from my controller). So, whenever the Site.Master page is reloaded, it recreate every treeview menu item. For each one, it checks if the item is equal to the session variable. If yes, the class "selected" is added to the item (css highlighted with blue).
I don't really like using session variables. Maybe there are more elegant solutions?
How about using a helper:
public static MvcHtmlString MenuItem(
this HtmlHelper htmlHelper,
string text,
string action,
string controller
)
{
var li = new TagBuilder("li");
var routeData = htmlHelper.ViewContext.RouteData;
var currentAction = routeData.GetRequiredString("action");
var currentController = routeData.GetRequiredString("controller");
if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
{
li.AddCssClass("active");
}
li.InnerHtml = htmlHelper.ActionLink(text, action, controller).ToHtmlString();
return MvcHtmlString.Create(li.ToString());
}
and then:
<ul>
<%= Html.MenuItem("Search", "Search", "Affaires") %>
<%= Html.MenuItem("Favorite", "Favorite", "Affaires") %>
<%= Html.MenuItem("New", "Create", "Affaires") %>
</ul>
which could yield the following if you navigate to /Affaires/Favorite:
<ul>
<li>Search</li>
<li class="active">Favorite</li>
<li>New</li>
</ul>
Well, there is one easy and effective solution: let each page signal whether it's the active page and set a css class with jQuery.
Assuming your rendered html looks like:
<ul id="treemenu1" class="treeview">
<li>Documents
<ul>
<li class="search"></li>
<li class="favorite"></li>
</ul>
</li>
</ul>
At the bottom of each page, do something like (this would sit on the search view):
<script type="text/javascript">
$(document).ready(function () {
// Set active nav
$('#treemenu1 li.search').addClass('selected');
});
</script>
UPDATE based on new info
A little cleaner than a session vraiable could be to use the ViewBag property from the controller.
public ActionResult Search(/*whatever*/)
{
// do things
// set the selevted view
ViewBag.SelectedMenuItem = "search";
return View();
}
Then in your master page you can check against <%: ViewBag.SelectedMenuItem %>
Note that SelectedMenuItem is a random name. The ViewBag property is of type dynamic so you can use any property name you like.
Related
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;}
I am new to MVC3 and Razor.
I have an "attention banner" on the master page as a Partial View that I want to "acknowledge" with a click on a link to close the banner (without reloading the page). I believe I need to use jQuery and an Ajax call, but I just can't seem to find the right combination.
Here is part of my _Layout.cshtml:
<section id="main">
<span id="attentionBar">#{ Html.RenderPartial("_AttentionBarPartial"); }</span>
#RenderBody()
</section>
This is my Partial View (just using Session as a shortcut for now to get it to work). I'm not sure what to use as the "link" to reload the view:
#{ this.Layout = null;}
#if(! String.IsNullOrWhiteSpace(#Session["Attention"].ToString()))
{
<div class="attentionPanel">
<span class="attentionLabel">Attention</span>
#Session["Attention"].ToString()
<span class="attentionLabel">
#* WHAT DO I PUT HERE *#
#Ajax.ActionLink("X", "AcknowledgeAttentionBar", "Home", new AjaxOptions{ UpdateTargetId="attentionPanel", InsertionMode=InsertionMode.Replace })
</span>
</div>
}
Here is my Home controller. Again, I am not sure that the code is quite correct, but essentially I will clear out the condition that shows the attention banner.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Home Controller Updated At " + DateTime.Now.ToLongDateString()
+ " " + DateTime.Now.ToLongTimeString();
return View();
}
public PartialViewResult AcknowledgeAttentionBar()
{
Session["Attention"] = String.Empty;
return PartialView("_AttentionBarPartial");
}
}
2 things:
Make sure you have included the jquery.unobtrusive-ajax.js script to your page in order for Ajax.ActionLink helper to work and send an AJAX request when the link is clicked instead of a normal redirect:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
In your AjaxOptions you have specified UpdateTargetId="attentionPanel" but there's no element in your markup with id="attentionPanel". You have a div with class="attentionPanel" but that's not the same thing. On the other hand you have wrapped your banner in a <span id="attentionBar">, so you probably meant UpdateTargetId="attentionBar".
I created a dropdown like this:
<%= Html.DropDownList("Nick") %>
When a user selects an item I wanna send the text of the item back to a controller Action. I am guessin I add the same name of the the get controller with an [HTTPPOST] attribute, but how do I pass the selected item text into the controller?
I want this 2 happen in 2 scenerios
-When a user selects an item
-Add a button to the html (im not sure the best way??) and when a user clicks the button
the dropdowns selected text is sent.
I am populating the dropdown from ViewData["items"] i populated in the controller.
You need a form. You need to populate the drop down. And if you want the form to be sent as soon as the item is selected, you need half a line of javascript.
<% using (Html.BeginForm()) { %>
<%: Html.DropDownList("nick",
new SelectList[] { new[] { "hello", "world", "wazza" } },
new { #onchange = "this.form.submit()" })%>
<% } %>
You may also want your controller to receive a value rather than the text in the list option. See the SelectList class for more details.
Have your form submit to this action.
public ActionResult MyAction(string nick)
{
//do stuff
}
I've inherited some code which breaks a page up into tabs using divs. On the first page there are many required field and regex validators. The problem is a user can switch to another tab, trigger a postback and fail the validators on the first page, leaving things in a mess.
What I want to be able to do is perform the validation on the first page as a user selects another tab, thus preventing them from moving to a new tab until the first page is valid.
<ul>
<li>Tab 1 </li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
Where isValid needs to fire off the validators.
Thanks!
UPDATE: The answer provided by codeka is pretty close, however, because I need to provide both href and onclick attributes (to avoid messing up display), the tab (anchor) switch is still taking place even if validation fails. Here's how I solved this. disclaimer: ugly code ahead
<ul>
<li><a id="tab1Tab" href="#tab1" style="display:none"/><a onclick="isValid('tab1');">Tab 1</a></li>
<li><a id="tab2Tab" href="#tab2" style="display:none"/><a onclick="isValid('tab2');">Tab 2</a></li>
<li><a id="tab3Tab" href="#tab3" style="display:none"/><a onclick="isValid('tab3');">Tab 3</a></li>
</ul>
function isValid(tab) {
var valid = Page_ClientValidate();
var tabId = (valid ? tab : "tab1") + "Tab";
$("#" + tabId).click();
}
Note use of jQuery for cross-browser compatibility with click event. And this only works if there are no validators on other tabs, as per Thomas' answer, I'll need to use validation groups and extra logic in isValid if any get added.
ASP.NET creates a global javascript function Page_ClientValidate that you can call to fire the validators:
function isValid() {
return Page_ClientValidate();
}
You might try using a validation group per tab. On clicking from one tab to another, you would call something like so where the tab names represent the validation groups:
function TabValidate( tabName ) {
if ( Page_ClientValidate( tabName ) ) {
//do stuff
}
}
ADDITION I mentioned this in a comment, but I figured I would add it to my post. If there aren't any other validators on other tabs, then another solution would be to simply mark the other .NET controls on the other tabs that can trigger a postback with CausesValidation="false" (e.g. DropDownLists with AutoPostBack="true")
When I navigate on a website utilizing MasterPages, does the application know what page I am on? If so, does it store it in an object I can access?
The reason I am asking is so I can replace this:
//masterpage
<div id="nav_main">
<ul><asp:ContentPlaceHolder ID="navigation" runat="server">
</asp:ContentPlaceHolder></ul>
</div>
//content page(s)
<asp:Content ContentPlaceHolderID="navigation" ID="theNav" runat="server">
<li>Home</li>
<li id="current">FAQ</li>
<li>Videos</li>
<li>Button 4</li>
<li>Button 5</li>
</asp:Content>
With a more elegant solution for the navigation, which highlights the link to the page by having the list item's ID set to "current". Currently each page recreates the navigation with its respective link's ID set to current.
I'd concur with Chris: use a control to handle display of this menu and make it aware of what link should be highlighted. Here's a method I use regularly. It may become more complex if you've got multiple pages that would need the same link styled differently, but you get the idea.
Dim thisURL As String = Request.Url.Segments(Request.Url.Segments.Count - 1)
Select Cast thisUrl
Case "MenuItem1.aspx"
lnkMenu1.CssClass = "Current"
Case "MenuItem2.aspx"
lnkMenu2.CssClass = "Current"
End Select
To get the current request URL from within the master page you would do:
string s = this.Page.Request.FilePath; // "/Default.aspx"
I also recommend moving your navigation into the master page instead of the content page. This will make it easier to maintain / access.
Yes, Use the below code in your master file. It will give you the content page name.
Page.ToString().Replace("ASP.","").Replace("_",".")
Alternatively you can search for page title if you have set an specific title to the child page instead of masterpage try:
this.Page.Title
Hope it helps.
this is in C#
string thisURL = Request.Url.Segments[Request.Url.Segments.Length - 1];
if (thisURL.ToLower()== "default.aspx") li1.Attributes.Add("class","yekan active");
if (thisURL.ToLower() == "experts.aspx") li2.Attributes.Add("class", "yekan active");
You should be able to get the page by accessing the Page property. IE:
string type = this.Page.GetType().Name.ToString();
You'd probably just use one of the Request path from within the master page to set the current. I'd probably also have a property on the master page to override it, so that pages without links or something could set it to something reasonable.
It worked for me this way - Thanks Jared
This is what I did to get our nav menu to highlight the current menu item for the page that we are viewing. The code is in the master page.
You basically get the filepath (Jared's way)
We use the "~" in our links so I had to strip that out.
Iterate the menuItems collection of the Menu control.
Compare the navigateUrl property.
(I'm not the best coder and even worse at explaining - but it works and I was quite chuffed with it!)
protected void HighlightSelectedMenuItem()
{
string s = this.Page.Request.FilePath; // "/Default.aspx"
string nav;
if (s.Contains("~"))
{
s = s.Remove(s.IndexOf("~"), 1);
}
foreach (MenuItem item in navMenu.Items)
{
if (item.NavigateUrl.Contains("~"))
{
nav = item.NavigateUrl.Remove(item.NavigateUrl.IndexOf("~"), 1);
if (s == nav)
{
item.Selected = true;
}
}
}
}
string s = this.Page.GetType().FullName;
string[] array = s.Split('_');
int count = array.Count<String>();
string currentPage = array[count - 2];
The navigation control, not the master page, should be in charge of what page is currently highlighted.
Either the page that is loaded should notify the navigation item who it is, or the nav control itself should keep track of it.
The point is that master pages are supposed to simply be a holder that content is displayed in. They aren't supposed to control anything.
try
this.Page.Master
It will get you the master page of the current page.
There's also the Request.RawURL