An ASP.NET MVC 2 & JQueryUI example: Part 2 – Jumping in

This entry is part 2 of 2 in the series ASP.NET MVC 2 & JQuery

Since this series is primarily about applying the JQueryUI themes to an ASP.NET application we will start with looking at how the theme is stored for a user and how this translates into a CSS file applied to the website for an individual user.

Theme storage and retrieval:

The first thing I did was add a user accessible property to the Profile Provider configuration in the web.config as shown below.

Listing 1:

<profile>
  <providers>
    <clear/>
    <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
  </providers>
  <properties>
    <add name="Theme"
      type="System.String"  />
   </properties>
</profile>

As you can see in Listing 1 I am using the standard AspNetSqlProfileProvider and I have added property called “Theme” of type string. This will allow us to store the theme chosen by each user on an individual basis for each user registered by the Membership Provider. I modified the default AccountService which is created by the ASP.NET MVC 2 Project Wizard in the “AccountModels.cs” file by adding the line “ProfileBase.Create(userName, true)” (Listing 2 line 10).

Listing 2:

public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
    if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
    if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
    if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");

    MembershipCreateStatus status;
    _provider.CreateUser(userName, password, email, null, null, true, null, out status);

    ProfileBase.Create(userName, true);

    return status;
}

When a user registers on the website a user profile will be created automatically for the user, however it will not have any data in the profile yet. Setting profile properties during the registration process will NOT work as the request is not in fact authenticated until the user logs in. We will get around this by checking for an empty profile when retrieving the users theme choice and setting a default theme if one does not exist.

In order to set the correct CSS file for a user in the Site.Master file we will user a Helper method to retrieve the CSS file name.

Listing 3:

<head runat="server">
    <link href="<%: Url.Content("~/Content/default.css")%>" rel="stylesheet" />
    <link href="<%: Url.Content("~/Content/themes/" + Html.GetTheme() +  "/jquery.ui.all.css")%>" rel="stylesheet" />

    <script type="text/javascript" src="<%:Url.Content("~/Scripts/jquery-1.4.2.min.js") %>"></script>
    <script type="text/javascript" src="<%:Url.Content("~/Scripts/jquery-ui.1.8.2.min.js") %>"></script>
    <script type="text/javascript" src="<%:Url.Content("~/Scripts/jquery-getCSS.min.js") %>"></script>
    <script type="text/javascript" src="<%:Url.Content("~/Scripts/jquery.loadImages.1.0.1.min.js") %>"></script>
    <script type="text/javascript" src="<%:Url.Content("~/Scripts/mvcjqueryuiexample.js") %>"></script>
    <title>
        <asp:ContentPlaceHolder ID="TitleContent" runat="server" />
    </title>
    <script type="text/javascript">
        $(document).ready(initialise);
    </script>
    <asp:ContentPlaceHolder ID="HeaderContent" runat="server" />
</head>

As you can see on the third line we use “Html.GetTheme()” (Listing 3 Line 3) to retrieve the theme name, the “GetTheme()” function is shown below.

Listing 4:

public static MvcHtmlString GetTheme(this HtmlHelper helper)
{
    string baseTheme = "ui-lightness";
    string theme = baseTheme;
    if (helper.ViewContext.HttpContext.User.Identity.IsAuthenticated)
    {
        theme = helper.ViewContext.HttpContext.Profile.GetPropertyValue("Theme").ToString();
        if (string.IsNullOrEmpty(theme))
        {
            helper.ViewContext.HttpContext.Profile.SetPropertyValue("Theme", baseTheme);
            theme = baseTheme;
        }
    }
    return MvcHtmlString.Create(theme);
}

The function first checks if the current request is authenticated otherwise it just returns the default theme name. If the request is authenticated it uses the profile provider to retrieve the “Theme” profile property, if it does not exist we set the property to the default theme else we return the users chosen theme.
The theme name that is stored in the profile corresponds directly to the name of a theme directory in the /Content/themes directory, these themes are the standard themes included in the JQueryUI themes package and can be downloaded from the JQueryUI blog.
Listing 3 has another very important bit of code, lines 13-15 show a piece of JQuery which hooks the document load event and calls the initialise function, this function resides in the “mvcjqueryuiexample.js” file that I will discuss in detail later, for now just take note that we are initialising some clientside scripts on each load of a page.

A quick aside – the User Menu Area:

 

Being able to store a users chosen theme against their profile is all well and good but there needs to be a way to change the theme, the gateway to the profile editing pages is the user menu area which I will discuss briefly before talking about how the theme itself is selected.

In the Site.Master file I use the Html.RenderPartial helper method to render a usercontrol, in this case “LogOnUserControl.ascx”. Being in the Site.Master master page this is rendered on every page and so needs to be able to differentiate between authorised and anonymous users.

Listing 5:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<% if (Request.IsAuthenticated)
   {
%>
<ul>
    <li>
        <img alt="avatar" src="<%:Html.GetGravatarUrl(24)%>" width="24px" height="24px" />
    </li>
    <li>
        <a class="ui-button ui-state-default ui-corner-all ui-button-text-only" href="<%: Url.Content("~/account/editprofile/") %>">
            <%: Page.User.Identity.Name %>
        </a>
    </li>
    <li>
        <a class="ui-button ui-state-default ui-corner-all ui-button-text-only" href="<%: Url.Content("~/account/logoff/") %>">
        Log Out
        </a>
    </li>
</ul>
<% }
   else
   {
%>
<ul>
    <li></li>
    <li>
        <a class="ui-state-default ui-button-text-only ui-corner-all ui-button" href='<%: Url.Content("~/account/logon/") %>'>
            Log On
        </a>
    </li>
</ul>
<%} %>

The usercontrol just uses some simple conditional statements to render different content based on whether the request is authenticated or not. If the request is authenticated it uses another HtmlHelper to render a gravatar, a button with the user’s username on it to edit the user’s profile and a logout button versus just a logout button when the request is not authenticated. The gravatar url is created by generating an MD5 hash of the user’s email address and appending this and the requested image size to the gravatar service url. The code is shown below.

Listing 6:

public static string GetGravatarUrl(this HtmlHelper helper, int imageSize)
{
	string result = string.Empty;

	MembershipUser User = Membership.GetUser();
	if (User != null)
	{
		System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
		byte[] bs = System.Text.Encoding.UTF8.GetBytes(User.Email);
		bs = x.ComputeHash(bs);
		System.Text.StringBuilder s = new System.Text.StringBuilder();
		foreach (byte b in bs)
		{
			s.Append(b.ToString("x2").ToLower());
		}
		string gravatarHash = s.ToString();

		result = string.Format("http://www.gravatar.com/avatar/{1}.png?s={0}", imageSize, gravatarHash);

	}

	return result;
}

Theme Selection:

In the previous section I described how the user menu area displays a button with the currently logged in user’s name on it as a means to edit the user’s profile. I’ll now show how the user is able to preview and select a theme from the available themes. For simplicity’s sake on application start up I retrieve a list of sub-directories in the /Content/themes directory and store these in the application state as shown below.

Listing 7:

protected void Application_Start()
{
	// build the list of themes
	string physicalPath = Server.MapPath("~/content/themes");
	string[] themeDirs = Directory.GetDirectories(physicalPath);
	IList<string> themes = new List<string>();
	foreach (string themeDir in themeDirs)
	{
		string theme = themeDir.Split(new char[] { '\\' }).Last();
		if (theme != "base")
			themes.Add(theme);
	}

	Application.Add("themes", themes);

	AreaRegistration.RegisterAllAreas();

	RegisterRoutes(RouteTable.Routes);
}

The actual theme selection happens on the edit profile view, this view contains two tabs for editing basic user details and profile settings as shown below.

image

image

This view is managed by the account controller using three methods, “EditProfile” to return the view and the data for the two tabs, “EditProfileBasic” to save the data for the basic tab, and “EditProfileSettings” to save the data from the profile tab. Notice the use of the “Authorize” attribute to ensure only authenticated users can edit their profiles as well as the “HttpPost” attribute on the two methods used for saving the users profile details which only allows the methods to be called from a page post. I also redirect back to the “EditProfile” action at the end of the two post methods as we need to refresh the data model being returned as the data posted to each method is only for the current tab and I would end up with one of the tabs having no data if just returned the view with the current model.

Listing 8:

[Authorize]
public ActionResult EditProfile()
{
	MembershipUser user = Membership.GetUser();
	return View(new ProfileModel(User.Identity.Name, user.Email, user.CreationDate.ToShortDateString(), this.HttpContext.Profile.GetPropertyValue("Theme").ToString()));
}

[Authorize]
[HttpPost]
public ActionResult EditProfileBasic(ProfileModel model)
{
	if(ModelState.IsValid)
	{
		if (string.IsNullOrWhiteSpace(model.EmailAddress))
		{
			ModelState.AddModelError("", "Email address may not be empty.");
		}
		else
		if (MembershipService.ChangeEmail(User.Identity.Name, model.EmailAddress) == false)
		{
			ModelState.AddModelError("", "New email address is not valid.");
		}
	}
	return RedirectToAction("EditProfile");
}

[Authorize]
[HttpPost]
public ActionResult EditProfileDetails(ProfileModel model)
{
	if (ModelState.IsValid)
	{
		if (string.IsNullOrWhiteSpace(model.Theme))
		{
			ModelState.AddModelError("", "A theme must be selected.");
		}
		else
		{
			this.HttpContext.Profile.SetPropertyValue("Theme", model.Theme);
		}
	}
	return RedirectToAction("EditProfile");
}

In the EditProfileDetails method in Listing 8 above I save the the chosen theme back to the current users profile which in turn will change which CSS file will be returned next time a page is requested by the user.

The tabs are created using the JQueryUI library and the view includes the script below in its header to initialise the tabs:

Listing 9:

<script type="text/javascript">
    $(document).ready(initialiseSettings);
</script>

The javascript function initialiseSettings is contained in the mvcjqueryuiexample.js file. This function sets up the tabs and hooks the change event of the dropdown box so that the theme can be previewed.

Listing 10:

function initialiseSettings() {
    /// <summary>
    /// Called from the edit profile page. Sets up the tabs to be JQueryUI tabs, initialises the themes select box and preloads the busy image.
    /// </summary>

    // Setup the tabs
    $("#editprofile").tabs();

    // initialise the themes select options
    initialiseThemes();

    // preload the busy image
    $.loadImages('/Content/ajax-loader.gif', preloadDone());
}

JQuery is used to select the div with id “editprofile” and then the JQueryUI tabs() method is called to create the tabs. Then the initialiseThemes() method is called which hooks the dropdown change event and finally I preload the busy spinner image that will be shown while the new theme is applied so that there is no delay in showing the animation, the preloading is done via a JQuery plugin (loadImages) which is included in the scripts directory.

As the theme preview is rather complicated I will talk about this in part three of this series.

Series Navigation<< An ASP.NET MVC 2 & JQueryUI example : Part 1 – Introduction
This entry was posted in enterprise-examples and tagged , , , , . Series: . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

20 Comments

  1. Florin Homone
    Posted November 26, 2010 at 11:25 pm | Permalink

    Nice article.
    What about using just one single “Save Changes” button for tabs (in case you have more tabs)? This would reside outside tabs div and collect the data from all tabs which have editable elements.

    Cheers,
    Florin

  2. Posted February 22, 2011 at 5:12 am | Permalink

    Hello Eugene:
    Thank you for posting your most excellent example.
    I finally got my head around MVC, I’ve been using straight .aspx pages for too damn long!
    Besides that, it is funny that you live in South Africa, my daughter moved there about a month ago. Currently living in Welcom but she was going to go to Johannesburg.
    Thanks again, you have helped me learn something new.
    Cheers from Cancun, Mexico

  3. Posted July 30, 2013 at 1:56 pm | Permalink

    If you are going for best contents like me, just go to see this web site daily
    as it presents quality contents, thanks

  4. Posted March 18, 2014 at 3:08 am | Permalink

    This design is steller! You most certainly know how to keep a reader entertained.

    Between your wit and your videos, I was almost moved
    to start my own blog (well, almost…HaHa!) Great job.
    I really enjoyed what you had to say, and more than that, how you presented it.

    Too cool!

  5. Posted April 27, 2014 at 11:08 pm | Permalink

    Hi there! Quick question that’s totally off topic.
    Do you know how to make your site mobile friendly? My web
    site looks weird when browsing from my iphone 4.
    I’m trying to find a theme or plugin that might be able
    to resolve this issue. If you have any recommendations, please
    share. Thank you!

  6. Posted May 6, 2014 at 2:50 am | Permalink

    These upgrades are really where this sports management sim is won and lost, by giving your team
    the best tools to improve. How in the world is Halloween
    related to longevity. She provides a perfect canvas for the dancers to
    do with it what they will.

  7. Posted May 22, 2014 at 5:42 pm | Permalink

    This is really interesting, You are a very
    skilled blogger. I’ve joined your rss feed
    and look forward to seeking more of your magnificent post.
    Also, I have shared your website in my social networks!

    Stop by my blog post imvu credits cheats codes

  8. Posted May 22, 2014 at 9:20 pm | Permalink

    Spot on with this write-up, I seriously think this website needs a great deal
    more attention. I’ll probably be returning to
    read more, thanks for the information!

  9. Posted September 10, 2015 at 9:07 am | Permalink

    どうもありがとう、それは非常に有用だったと私はあなたがそこに作られた最初の点が好きかなりの

  10. Posted September 10, 2015 at 7:46 pm | Permalink

    http://itab.cc/prostomag/1640a-10154-hamilton37.htmlハミルト&#12531; ジャズマスター オートクロノ,ハミルトン島,ニナリッチ 香水 マカロン
    [url=http://beehive.us/prostomag/0eed8-80152-hamilton18.html]ハミルトン 腕時計 電池交換,ハミルトン レディース,ニナリッチ 財布 ピンク[/url]

  11. Posted September 10, 2015 at 7:47 pm | Permalink

    http://blog.homehelpers.cc/prostomag/05296-40150-hamilton3.htmlリンダハミルト&#12531;,ハミルトン 腕時計 オープンハート,ニナリッチ 財布 ピンク
    [url=http://afaccounting.ca/prostomag/115e9-90153-hamilton25.html]ハミルトン レディース 腕時計,ハミルトン 腕時計 メンズ クオーツ,ニナリッチ 財布 年齢[/url]

  12. Posted September 18, 2015 at 6:04 am | Permalink

    をczytajするDLA ciebie nowe iformacje nowosci czytaj dalej sprawdzブログ。我々はImはサイトにしようと、など、あなたのサイトの統計情報の検索ボリュームについて話すことができれば

  13. Posted September 20, 2015 at 8:28 am | Permalink

    Hello my loved one! I want to say that this article is awesome, great written and include almost
    all significant infos. I would like to see extra posts like this .

  14. Posted October 3, 2015 at 2:18 am | Permalink

    多くのおかげで、私はスプレーライニング多くの知識を持っているが、常に何か新しいことを学びます。良い仕事を続けて、もう一度あなたに感謝します。私はこのトピックを議論していた私のヘルパーを

  15. Posted October 8, 2015 at 7:50 am | Permalink

    ブックマークやメモを作る、長い時間を再発見。まあただ感謝、このいずれかを使用します。私は幸運なことは、ここで1と同様のトピックを持っている、私の学校のプロジェクトのこのようなものが必要。私はそれを見つけたこと、幸せなトレイルを安心しています。

  16. Posted October 26, 2015 at 7:41 pm | Permalink

    Significantly less than 1per cent of users of Skyla or mirena weight loss (independent.academia.edu) get a critical pelvic illness called PID.

  17. Posted December 7, 2015 at 8:31 pm | Permalink

    Miło nam poinformować, że Centrum Medyczne o profilu
    ortopedycznym rozpoczęło swoją działalność. Naszą maksymą
    jest najwyższa jakość opieki nad pacjentem.

    Nasz personel medyczny to ceniony i doświadczony zespół lekarzy ortopedów i
    fizjoterapeutów, którzy stale podnoszą swoje kwalifikacje, a także śledzą
    postęp światowej wiedzy medycznej. Bazując na najnowszych technologiach
    zapewniamy kompleksową opiekę związaną z rozpoznawaniem
    oraz leczeniem nieprawidłowości w działaniu
    i budowie narządu ruchu, będących wynikiem wad wrodzonych lub nabytych, urazów czy chorób.

    Warto podkreślić, że stała współpraca specjalistów różnych dziedzin jest praktyką stosowaną
    w naszym Centrum Medycznym, co ma na celu dobór najlepszej drogi leczenia, co w rezultacie zapewnia naszym pacjentom szybki powrót do zdrowia.
    W razie potrzeby jesteśmy w stanie zaopatrzyć na
    miejscu, naszych pacjentów, w wysokiej jakości artykuły ortopedyczne, dzięki którym nasi lekarze
    mają pewność, że ich leczenie przyniesie właściwe efekty.

    Oferujemy indywidualne podejście do każdego pacjenta, gwarantujemy
    precyzyjną diagnozę i plan leczenia dla każdego przypadku.
    Dbałość o pacjenta i jego komfort w trakcie wizyty
    jest dla nas najważniejszy.

    Jesteśmy dla osób w każdym wieku, szczególnie tym, którzy prowadzą aktywny tryb życia.
    Stosując najnowocześniejsze procedury medyczne, umożliwiamy naszym Pacjentom
    szybki powrót do pełnej sprawności i swoich sportowych pasji.

    To priorytet nie tylko dla profesjonalnych sportowców, ale i
    dla wszystkich tych, dla których aktywność fizyczna jest niezbędnym elementem codziennego życia

    W ramach Centrum Medycznego, zarówno dorosłym jak i dzieciom gwarantujemy możliwość wykonania badań
    laboratoryjnych jak i USG

    Zapraszamy na konsultacje.

    my web site rehabilitacja warszawa

  18. Posted December 8, 2015 at 4:30 pm | Permalink

    これは本当に | これについてのうち模索する必要がある誰のためウェブログ適しています。あなたはとてもわかり多くのそのあなたと議論することが、事実上、面倒な(私は真ないことをしたいと思います…笑)。あなたは間違いなく上に新しいスピンを置く対象年間について書かれてthatsの。 ワンダフルもの、ちょうど素晴らしい!アウトブログ何をあなたが起こるプラットフォームがあることを探し興味津々|

  19. Posted December 18, 2015 at 4:51 pm | Permalink

    Restauracja i hotel ząbki Animar, znajdują się w Rembertowie w bliskim
    sąsiedztwie podwarszawskich Ząbek.

    Kompleks noclegowo -restauracyjno-konferencyjny położony jest zaledwie10 km.
    od ścisłego centrum Warszawy, 2 Km od Centrum Targowo- Konferencyjnego MT TARGI
    przy ul.Marsa , oraz 3 km od Akademii Obrony Narodowej(AON) .

    Jesteśmy doskonałą bazą dla klientów biznesowych .

    Naszym gościom oferujemy tanie noclegi w pełni wyposażonych pokojach,
    Salę konferencyjną na 80 osób, oraz Restaurację.

    Nasze wszystkie pokoje gościnne posiadają łazienkę
    z kabiną prysznicową ,ręczniki , kosmetyki , telewizor i bezpłatny internet.
    , są idealnym miejsce na odpoczynek po dniu ciężkiej pracy .

    Dla Naszych gości przygotowaliśmy duży bezpłatny parking, a rano pyszne śniadanie.

    Dzięki doskonałej lokalizacji , wysokim standardom usług , niecodziennej kuchni oraz przyjemnej rodzinnej atmosferze zyskaliśmy grono wiernych klientów.

    Wiemy jak ważna jest dobra oprawa kulinarna , przyjemna
    atmosfera i niebanalność ważnych przyjęć rodzinnych i firmowych
    , dlatego serdecznie polecamy Naszą restauracje jako miejsce idealne dla przyjęć okolicznościowych ,świątecznych
    spotkań firmowych , codziennych lunchy biznesowych .
    Jeśli z jakichś przyczyn wolicie Państwo zorganizować przyjęcie w domu lub spotkanie
    świąteczne w siedzibie firmy z przyjemnością przygotujemy i dowieziemy catering zgodnie z ustaleniami .
    Zadbamy o oprawę , smak i całe wyposażenie niezbędne do organizacji przyjęcia .

    Zapraszamy do zapoznania się z naszą ofertą pokoi gościnnych oraz ofertą restauracyjną
    i będzie nam niezmiernie miło jeśli wkrótce powiększycie Państwo
    grono Naszych zadowolonych Gości.

    Codziennie od poniedziałku do piątku zapraszamy do Naszej Restauracji gdzie rano możecie Państwo
    zjeść pożywne i zdrowe śniadanie , a od godz. 11 zapraszamy na lunche,
    przygotowane ze świeżych i najwyższej jakości produktów .

  20. Posted November 19, 2016 at 2:01 pm | Permalink

    Hі there alⅼ, heree every one is sharing thеse kinds of know-һow,so it’s nice to гead this
    weblog, and I used to pρay a visit this weblog ᥱveryday.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Skip to toolbar