MVC3 razor return current view - c#

I've got a actionlink in my master page _Layout That when pressed, changes the layout of my menu. But I don't want the current Content of my page to change.
How can I accomplish this with MVC3 razor without using javascript?
I'm guessing it will be something along the lines of:
Button is clicked postback happens
Set some value so changed menu state persists
Return previous view
Especially the "return previous view" part perplexes me, could someone explain how to accomplish this?

In your controller override OnActionExecuting:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.QueryString["MenuLayoutName"] != null && IsValidMenuLayoutName(filterContext.RequestContext.HttpContext.Request.QueryString["MenuLayoutName"] != null))
ViewBag.MenuLayoutName = filterContext.RequestContext.HttpContext.Request.QueryString["MenuLayoutName"];
}
In your _Layout.cshtml when you render the menu, look at the ViewBag.MenuLayoutName to decide which menu to use. The most efficient way is to simply create partial views so you can render the menu as follows:
#{ Html.RenderPartial(ViewBag.MenuLayoutName); }
However, take note of the call to IsValidMenuLayoutName! Otherwise people could put the name of any valid partial view in there and get it rendered where you expect your menu to appear.
In your links where you want to allow the user to select the various menu layouts, change your link to the page to specify the name of the layout to use.
Use Red Menu

Using a VERY primitive approach, you can create buttons essentially linking to itself:
Foo Menu
Bar Menu
(Or use logic to show either/or based on what's visible) Then modify your _Layout.cshtml to render what's provided:
#{
String menu = (String)Request.Params["menu"] ?? "foo";
if (menu == "foo"){
/* Foo menu render */
} else {
/* bar menu render */
}
}
Then it's just a round-trip to the same URL. Again, if they landed on the page via POSTed information, you'll lose that. Also, I didn't add logic to test if menu already existed in the request, but you should.

Related

How to set a value in controller indicating if a link exists on top partial part of its view

So I have a view, that has a prtial for its header, that partial can have some links to for example going back to home page, going to profile page, etc...
Sometime some of those links can be hidden or disabled based on some situations.
What I need is in one of my controller action method I need to have accees to some sort of flag or variable that tell me if that home page button is on the page or not. How can I do that ?
In your controller method you can have add a line like this in cases where you need to limit the links displayed:
ViewBag.LimitOptions = true;
Then in your partial view where you output your buttons, you can do this:
#if(ViewBag.LimitOptions != null)
{
/*define links here*/
}

Manage tabbed content with full support for routing

I'm building an Asp.net mvc 3 application. What I want to do, is a profile page (pretty mutch like the stackoverflow profile page), and the content of this page will be splited in tabs, each tab is represented by an PartialView.
When the user click on the tab X, I want to refresh only the tab area, without refreshing all my page. And in the same time changing my URL adress, so if the user click on the browser refresh button, it refresh the page with the selected tab. Is this possible ?
What I already know, is how to get data using AJAX, and replace the content of my view. I've read this post , I found it interesting, but it refresh all the page.
Thanks in advance.
You are on the right track. You will need to use both ajax to get the html to render without refreshing the page, as well as use push state to update the URL to include the current tab.
You could do these things as separate operations, but I would suggest using PJAX. With PJAX you will need a little additional logic in your server to decide whether to return the full html page with the layout, or just return the partial (a PJAX request).
Here is our Foo controller. Index has a default selected tab of "Bar". The Bar and Baz actions return the Index view, but with different tabs being the selected tab. If it is a PJAX request, then all we need is the partial view that fills in the tab content.
public class FooController : Controller
{
public ActionResult Index()
{
ViewBag.SelectedTab = "Bar";
return View();
}
public ActionResult Bar()
{
if(Request.Headers["X-PJAX"] != null)
return PartialView();
ViewBag.SelectedTab = "Bar";
return View("Index");
}
public ActionResult Baz()
{
if (Request.Headers["X-PJAX"] != null)
return PartialView();
ViewBag.SelectedTab = "Baz";
return View("Index");
}
}
Inside of the Foo/Index.cshtml we have the Razor code that will determine what partial view to render based on ViewBag.SelectedTab.
#{
ViewBag.Title = "Foo";
}
<h2>Foo</h2>
<ul>
<li><a class="tabs" href="#Url.Action("Bar", "Foo")">Bar</a></li>
<li><a class="tabs" href="#Url.Action("Baz", "Foo")">Baz</a></li>
</ul>
<div id="tab_content">
#if(ViewBag.SelectedTab == "Bar")
{
#Html.Partial("Bar")
}
#if (ViewBag.SelectedTab == "Baz")
{
#Html.Partial("Baz")
}
</div>
#section scripts
{
<script type="text/javascript">
$(function() {
$(".tabs").pjax("#tab_content");
})
</script>
}
The script section at the end is how you wire up PJAX. You are telling it, for all hyperlinks with the tabs class use pjax to dynamically load and render the html resulting from the href into the container with the id tab_content and update the url in the browser to be the url from the href.
Each tab's partial view is fairly simple in this scenario
Bar.cshtml
<p>This is the Bar tab. No pun intended.</p>
Baz.cshtml
<p>This is the baz tab.</p>
This is obviously a very simplified solution. You typically would want to use a presentation model to handle the logic of the view. Also, I have intentionally left out any tab styling to demonstrate that this technique can be used on any type of link. It is not limited to only tabs.
The full source code for this example can be on my GitHub site.
When the user click on the tab X, I want to refresh only the tab area, without refreshing all my page. And in the same time changing my URL adress, so if the user click on the browser refresh button, it refresh the page with the selected tab. Is this possible ?
To accomplish this you need to plug into browser's history API (and it's part of HTML5, and you'll have to use some kind of plugin to get the same thing in browsers that does not support it).
Manning's "HTML5 for .net developers" has pretty good section on that, but it's still in "early access".

Telerik Styles with multiple views in ASP.NET MVC

In a previous post I had trouble setting the styles on my views in the same way that they do it on the telerik demo site by selecting the style from a dropdown. This was answered however I now have a new but related problem.
I have multiple pages on my site (as is common to MVC) and each of these pages have many telerik controls on them. Allowing the user to style the site using predefined css done for me is great and saves me a lot of work.
The problem is that when I move from page to page, the combo box that telerik uses resets to its default value, thus resetting the styles on the site everytime the user changes a pages (not ideal).
I have tried using sessions to store the state, but there is no way to detect postback like with standard aspx development. So I cant do something like:
if(isPostBack)
{
if(string.isNullOrEmpty(Session["MyTheme"]+""))
{
Session["MyTheme"]="black";
}
else
{
Session["myTheme"]=//Some combo box selected value
}
}
Even if I could, the combo box onChange event is handled via JavaScript but the Telerik control requires C# Razor and try as I mgiht, I cant get them to talk and share a simple value.
Ultimately all I want to do, is allow the user pick a theme from the combo box and from then on, that theme is remembered throughout the site until they next change it.
I have tried query strings and sessions, but neither work as I cant access them in JavaScript. Aparently they are used on the server side only.
I have tried cookies but that doesnt work because I cant access them in C# Razor. Aparently they are client side only.
Below is my code:
<head>
#(
Html.Telerik().StyleSheetRegistrar()
.DefaultGroup(group => group
.Add("telerik.common.css")
.Add(string.IsNullOrEmpty(#Html.ViewContext.HttpContext.Request.QueryString["theme"]) ? "telerik.black.css" : "telerik."+#Html.ViewContext.HttpContext.Request.QueryString["theme"]+".css").Combined(true).Compress(true)
))
</head>
<body>
#(
/* TELERIK COMBOBOX */
Html.Telerik().ComboBox()
.Name("cbxTheme")
.SelectedIndex(0)
.ClientEvents(events => events.OnChange("cbxTheme_onChange"))
//.BindTo((IEnumerable<DropDownItem>)ViewData["Files"])
.Items(item =>
{
item.Add().Text("black");
item.Add().Text("common");
item.Add().Text("default");
item.Add().Text("forest");
item.Add().Text("hay");
item.Add().Text("metro");
item.Add().Text("office2007");
item.Add().Text("office2010black");
item.Add().Text("office2010blue");
item.Add().Text("office2010silver");
item.Add().Text("outlook");
item.Add().Text("rtl");
item.Add().Text("simple");
item.Add().Text("sitefinity");
item.Add().Text("sunset");
item.Add().Text("telerik");
item.Add().Text("transparent");
item.Add().Text("vista");
item.Add().Text("web20");
item.Add().Text("webblue");
item.Add().Text("windows7");
})
)
#(Html.Telerik().ScriptRegistrar().DefaultGroup(group => group.Combined(true).Compress(true)))
</body>
<script type="text/javascript">
function cbxTheme_onChange()
{
var selectedItemText = $("#cbxTheme").data("tComboBox").text();
//var selectedItemValue = $("#cbxTheme").data("tComboBox").value();
window.location.href = window.location.protocol
+ '//'
+ window.location.host
+ window.location.pathname
+ '?theme='
+ selectedItemText;
}
</script>
As I explained, for the most part it works fine. Execpt when I click on a likn to another page. Then everything gets set back to a preset default.
Ideally what I am looking for is a way to do a postback when a new item is selected in the combo box (like in the JavaScript). The style is changed so the whole page needs to be refreshed anyway. This works. But when I move to another page, it resets to a default style. So I need a way to store the selected style either client side or server side (preferred as my pages are loaded this way).
I have read this can be done by using a controller but it is not clear how. I would like the controller method if possible, because I am going to use a controller to load a list of CSS styles dynamically allowing the user to download additional styles and they will be added to the list automatically. So anything along this line would be great.
You can create a static class with a static property which will act as a global property.
public static class MyTheme
{
public static string MyGlobalTheme { get; set; }
}
or you could you the Application class.
Application["MyTheme"] = "black";
You could put this code in _Layout.cshtml. If your project name is TProj and your static class is in a folder called Objects, it would look like this.
_Layout.cshtml
#{
if (#Html.ViewContext.HttpContext.Request.QueryString["theme"] != null)
{
TProj.Objects.MyTheme.MyGlobalTheme = Html.ViewContext.HttpContext.Request.QueryString["theme"];
}
else
{
if (TProj.Objects.MyTheme.MyGlobalTheme == null)
{
TProj.Objects.MyTheme.MyGlobalTheme = "black";
}
}
}
Now in after this in _Layout.cshtml, you can use the string #TreasuryReportsMvc.Objects.MyTheme.MyGlobalTheme, which should stay the same even when you go to another page. _Layout.cshtml may not be the best place for this logic. You should think about where it makes the most sense for your project.
Be aware that global variables are frowned on by many. This question has a good discussion of Asp.Net MVC global variables.

ContentPage Control (mv) --> MasterPage Access

Is it possible to acces a control thats located in a content page (withing a content place holder, a multiview control to be more exactp) from the master page?
The situation is, i have a menu with buttons thats located in the master page.
Now in my content page i have 1 content place holder.
In which a multiview with several views is located.
If i press a button in the menu (MasterPage) then it should open the proper view (with its controls) displayed in the content place holder area.
I have set the ActieveViewIndex=0 but i am getting all sorts of wierd behavour.
I have to do something with the ActiveViewIndex++ somewhere but nothing seems to work.
edit::
string a = Request.Querystring["one"]
string b = Request.QueryString["two"]
if ( a == "addOne") // where addone is a redirection to the content page from the master page button
{
mvMultieView.SetActiveView(vView1);
}
else
if ( b == "addTwo")
{
mvMultieView.SetActiveView(vView2);
}
Any suggestions?
Kind Regards.
You can easily do that using find control
View myView = (View)this.Master.FindControl("PlaceHolderFullMain").FindControl("PlaceHolderMain").FindControl("Mymultiview")
The way my team and I accomplished this task (and I don't know that it's the best method, but it was effective) was to use query strings (as you had in your previous question it looks like). We established a standard QS variable called iView that would determine the name of the view in question (not necessarily the control name itself, but some keyword that the content control would respond to). Since all of our pages/controls have a page base class they inherit from, we put a method in the base class (in our case it was at the page level, but in yours a control level might work) that was responsible for getting the requested view. In the control we would have a mechanism (switch perhaps) that would set the activeView. In some cases we just used the actual ID of the control (since it was a mystery to be obfuscated) and avoided the switch altogether.
http://www.mydomain.com/mypage.com?iView=mySecondView
partial class MyControl : System.Web.UI.WebControl
{
// blah blah control stuff
public string getRequestedView()
{
return (Request.QueryString["iView"]) ? Request.QueryString["iView"] : String.Empty;
}
}
...
protected void Page_Load(object sender, EventArgs e)
{
View myView = myMultiView.FindControl(this.getRequestedView());
if(myView != null)
this.MyView.SetActiveView(myView);
}
have you done this change in the Master page or content page?
because i also got the same problem.
I have link buttons in my master page which i need to activate view controls in content page.
the content page is not the currently loaded page.
will this method help for my solution?

What's the best place to put default content in an MVC application?

I'm working on a sort of a CMS/Wiki application to help me experiment with the new Asp.Net MVC framework, and I'm trying to wrap my head around some of the code organization.
Right now, I have three views that cover displaying an article: Index, Edit, and Rename. All three views display the contents of the current page, or placeholder content stating that the page does not exist.
This is currently accomplished with the following code in the action method for each view:
MyPage myPage = null;
if (!string.IsNullOrEmpty(pageName)) {
myPage = mRepository.GetMyPage(pageName);
}
//Page does not exist.
if (myPage != null) {
ViewData["pageContent"] = myPage.GetParsedSource(new PageState());
ViewData["pageSource"] = myPage.Source;
ViewData["title"] = myPage.Title;
}
else {
ViewData["title"] = pageName;
ViewData["pageContent"] = "Page does not exist, feel free to create it!";
ViewData["pageSource"] = "";
}
ViewData["pageName"] = pageName;
My question is, where should this logic actually go?
1) The Controller (as it is now), which requires the above code to be replicated across action methods?
2) The Model, defaulting values for pageSource to the verbiage shown above? This would have the downside of moving display text into the model.
3) The View, using a null coalescing operator to convert null ViewData entries to their defaults?
4) In the Views, but add additional controllers to handle cases where the pageName does not exist.
EDIT:
Hopefully this should clarify things a little. The flow of the application is as follows:
When the user enters a URL (i.e. /pages/page_title), they arrive at a screen which displays the content of the article, along with hyperlinks labeled "edit" and "rename."
Clicking edit displays a page which contains the article content, as well as form controls to edit the article's source.
Clicking rename displays a page which contains the article content, as well as form controls to edit the article's name.
I would have several actions:
Lookup
Display
Create
Edit
Rename
In your default Lookup controller action (which gets hit when the user asks for, say, "/wiki/article-title"), you can redirect (RedirectToAction()) to the appropriate action as necessary. That encapsulates your Create logic into its own controller, and can also be called directly (RESTful). Same with the others. That also allows you to keep your views very, very stupid (always a good thing).
I would keep it in the controller but extract it out so that you don't have to replicate the code in each of the actions.
Maybe set some defaults in the controller's constructor and then have a separate private method (ie. not an action method) that takes your MyPage object and sets the viewdata that is shared between your actions.

Categories

Resources