The full text of this question is available with a screenshot here
Thanks for any help - original post follows:
So I downloaded the MvcMusicStore and fired up the completed project. I read all the articles talking about extending the view engine and using jquery plugins but I wanted to believe it could be simpler than that to just change the CSS file path when a link gets clicked. Mainly because I didn't want to copy code verbatim that I didn't fully understand. I'm very new to MVC.
So this is what I did:
To HomeController.cs I added:
public ActionResult Theme(string themeName)
{
ViewBag.Theme = ThemeModel.GetSetThemeCookie(themeName);
return View();
}
to Models I added this class:
public class ThemeModel
{
public static string GetSetThemeCookie(string theme)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("userTheme");
string rv = "Blue";
if (theme != null)
rv = theme;
else
{
if (cookie != null)
rv = cookie["themeName"];
else
rv = "Blue";
}
cookie = new HttpCookie("userTheme");
HttpContext.Current.Response.Cookies.Remove("userTheme");
cookie.Expires = DateTime.Now.AddYears(100);
cookie["themeName"] = rv;
HttpContext.Current.Response.SetCookie(cookie);
return rv;
}
}
I then created 2 copies of Site.css, changing only the background color and font-family and a view to generate my link tag.
<link href="@Url.Content(string.Format("~/Content/{0}.css", ViewBag.Theme))" rel="stylesheet" type="text/css" />
Finally, I made these changes to my _Layout.cshtml.
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
@if (ViewBag.Theme == null) {Html.RenderAction("Theme", "Home");}
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")"
type="text/javascript"></script>
</head>
<body>
<div id="header">
<h1><a href="/">ASP.NET MVC MUSIC STORE</a></h1>
<ul id="navlist">
<li class="first"><a href="@Url.Content("~")" id="current">Home</a></li>
<li><a href="@Url.Content("~/Store/")">Store</a></li>
<li>@{Html.RenderAction("CartSummary", "ShoppingCart");}</li>
<li><a href="@Url.Content("~/StoreManager/")">Admin</a></li>
</ul>
</div>
@{Html.RenderAction("GenreMenu", "Store");}
<div id="main">
@RenderBody()
</div>
<div id="footer">
Themes: @Ajax.ActionLink("Coral", "Theme", "Home", new { themeName = "Coral" }, null, new { @style = "color : coral"} )
@Ajax.ActionLink("Blue", "Theme", "Home", new { themeName = "Blue" }, null, new { @style = "color : blue;"})
</div>
</body>
</html>
When I run the app I get the general layout rendered twice. Once with only the genre menu rendered on the left and nothing in the body. And then again with the top 5 albums. I can't post the image as I don't have enough rep.
When I click my Coral and Blue links, my theme changes and I get just the one set without the top 5 albums.
So after some more reading on here I tried this:
_Layout.cshtml:
@{Html.RenderAction("Theme", "Home");}
HomeController.cs
public ActionResult Theme(string themeName)
{
ViewBag.Theme = ThemeModel.GetSetThemeCookie(themeName);
return PartialView();
}
But even though this stops the duplicate rendering, when I click the theme link, the colour changes but I get absolutely nothing else on the page.
Well and truly flummoxed now and could really use some help.
Cheers, .pd.
Okay - here's how I did it in the end.
Create a javascript file. Mine's called master.js:
function ajaxSuccSetTheme(theme) {
$('#linkTheme').attr('href', '/Content/' + theme + '.css');
}
Modify the _Layout.cshtml:
@{
if (ViewBag.Theme == null) {
ViewBag.Theme = MvcMusicStore.Models.ThemeModel.GetSetThemeCookie();
}
}
<link id="linkTheme" href="@Url.Content(string.Format("~/Content/{0}.css", ViewBag.Theme))" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-2.0.3.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/master.js")" type="text/javascript"></script>
Notes on this:
<link>
tag the same ID as the jQuery selector in your js file aboveThen my theme switching links in _Layout.cshtml look like this:
<div id="footer">
Themes :
@Ajax.ActionLink("Coral", "Theme", "Home", new { themeName = "Coral" },
new AjaxOptions { HttpMethod = "POST", OnSuccess = string.Format("ajaxSuccSetTheme('{0}');", "Coral")},
new { @style = "color : coral;" }) |
@Ajax.ActionLink("Blue", "Theme", "Home", new { themeName = "Blue" },
new AjaxOptions { HttpMethod = "POST", OnSuccess = string.Format("ajaxSuccSetTheme('{0}');", "Blue")},
new { @style = "color : blue;" })
</div>
Notes on that:
Next the HomeController.cs change:
public ActionResult Theme(string themeName)
{
ViewBag.Theme = ThemeModel.GetSetThemeCookie(themeName);
if (Request.IsAjaxRequest())
{
return PartialView();
}
else
{
return null;
}
}
Honestly, it doesn't matter if you just return null without checking for IsAjaxRequest()
cuz all we need from this is to set the cookie so it remembers when you next login.
Which just leaves the cookie setting method in the ThemeModel:
public class ThemeModel
{
public static string GetSetThemeCookie(string theme = null)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("userTheme");
string rv = "Blue";
if (theme != null)
rv = theme;
else
{
if (cookie != null)
rv = cookie["themeName"];
else
{
cookie = new HttpCookie("userTheme");
rv = "Blue";
}
}
cookie.Expires = DateTime.Now.AddYears(100);
cookie["themeName"] = rv;
HttpContext.Current.Response.SetCookie(cookie);
return rv;
}
}
Hope I helped somebody. If you'd rather do it all in jQuery here's Tim Vanfosson's Theme Manager jQuery Plugin
Cheers,
.pd.