JQuery Horizontal Bar Chart Plugin

This is a very simple bar chart plugin using unordered lists and divs. You can populated the data either statically using data-dash attributes for the

  • items or you can add it inline in the data: option of the plugin. If you use the data-dash attributes the plugin assumes that it takes priority and overrides the data: option of the plugin. The plugin allows you to easily change css styles for the auto generated
  • ,
    and elements it creates. This is a very simple plugin with a handful of options. If you are looking for something really comprehensive this probably isn’t it. The purpose of this plugin is to quickly make a simple bar graph that shows a percentage based on some calculation. Essentially the value you supply for the bar is calculated against the size of the bar (either specified or auto generated based on element width). Obviously this could be extended quite a bit and rather easily so have at it. The reason I created this plugin is that the majority of the chart type plugins out there are far more complicated than I needed for this simple task. Hence the focus was to keep it as simple as possible while providing a handful of anticipated options for reuse in projects. The plugin does provide the ability to callback.

    Download: DOWNLOAD

    Below is an example of what it might look like.
    Quickbar Example

    First add some css. Again you can change the css style names to your liking in the plugin.


    .quickbar { padding: 0px; margin: 0px; list-style: none; } /* this is the actual ul */
    .quickbar li { padding: 5px 0px 5px 0px;}
    .quickbar-container { background-color: #F3F3F3; display: block; line-height: 20px; overflow: hidden; border: 1px solid #EAEAEA;
    border-radius: 3px 3px 3px 3px; -moz-border-radius: 3px 3px 3px 3px; -webkit-border-radius: 3px 3px 3px 3px; } /* this is the wrapper around the bar. */
    .quickbar-bar { background-color: #2E3192; width: 0px;} /* this is the bar itself you could use an image here if you wanted */
    .quickbar-title { padding: 3px; } /* this is the element above, left or below the bar.*/
    .quickbar-label { padding-left: 6px; float: left; position: relative; font-size: .9em; } /* this is the label that goes to the right or inside the bar */

    For the HTML markup you have two options the first couldn’t get any easier.

      The second utilizes data-dash attributes. If you are familiar with unobtrusive javascript these will make perfect sense. Hence if you rendered your markup and grabbed data from your database or something you can just add the attributes directly as opposed to supplying them in javascript. Just makes it handy.In this example you have to create the

    • elements. The title is what is displayed again on top, to the left or below the bar itself. The value is the integer value that will be used to calculate against the with of the element to properly show the percentage of use. The label is what shows inside the bar or to the right to further describe what the bar value means. For ex: 75% free. The width is a specified with. Yes you can have each
    • be a different width which is handy is some situations. The width can also be 0. You would need to create each li with:

      data-quickbar-title=”title”
      data-quickbar-value=”value”
      data-quickbar-label=”label”
      data-quickbar-width=”200″

      For the Javascrpt. Reference the plugin as you normal would for any plugin in your header tag.

      Wire up the plugin to your element.

      $(document).ready(function () {
      var chartData = [ { title: 'your_title', value: 'your_value', label: 'your_label', width: 200}, { title: 'your_title2', value: 'your_value2', label: 'your_label2', width: 200} ]
      $(‘.quickbar’).quickbar({
      styles: { container: ‘quickbar-container’, bar: ‘quickbar-bar’, title: ‘quickbar-title’, label: ‘quickbar-label’ },
      title: { visible: true, width: 50, position: ‘left’ }, // position options are ‘top’, ‘left’, ‘bottom’. Defaults: true, 50, ‘left’.
      label: { visible: true, width: 50, position: ‘right’ }, // position options are ‘inner’ or ‘right’. Defaults: true, 50, ‘right’.
      refresh: { btn: ‘.quickbar-refresh’, method: ”}, // this is only need to call a function to repopulate and reinit the plugin with new data.
      data: chartData, // this can be static, empty (if using data-dash attribute) or a function.
      animate: true, // can be true or false. Default: true.
      });
      });

      You could also use the built in destroy or reinit methods.

      $(‘.quickbar’).quickbar(‘destroy’); // destroys the plugin and removes any auto generated elements. (manual data-dash elements will NOT be removed).
      $(‘.quickbar’).quickbar(‘reinit’); // destroys and reinitializes the plugin.

      You could populate the data using an ajax call.

      function buildChart(){
      $.post(‘/your_path/method’, { variable: ‘variable’, variable2: ‘variable2′ },
      function (result){
      // creates a new quickbar.
      var opt = {
      title: { width: 100, position: ‘top’ },
      data: $.parseJSON(result)
      }
      var callback = function () { // some function. };
      $(‘.quickbar’).quickbar(opt, callback);
      }

      }

    • Download Links Fixed

      There some links that were broken. Its been resolved FYI.

      On the Horizon

      More JQuery plugins, Android and IOS goodies as some c# classes.

      The big one though is probably the FreeSWITCH UI I’m building. Should be fun.

      Android HttpRequest Class

      This is simply a post of a class you can find here. The posted class from the source had some errors (missing escapes). This is just the corrected class.

      import java.net.*;
      import java.io.*;
      import java.util.ArrayList;
      import java.util.Enumeration;
      import java.util.Hashtable;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
      import java.util.Set;
      import java.util.Map.Entry;
      import org.apache.http.client.HttpClient;
      import org.apache.commons.*;
      import android.util.Log;
      /**
      * HTTP Request class
      *
      * You can use this class and distribute it as long as you give proper credit
      * and place and leave this notice intact :) . Check my blog for updated
      * version(s) of this class (http://moazzam-khan.com)
      *
      * Usage Examples:
      *
      * Get Request
      * --------------------------------
      * HttpData data = HttpRequest.get("http://example.com/index.php?user=hello");
      * System.out.println(data.content);
      *
      * Post Request
      * --------------------------------
      * HttpData data = HttpRequest.post("http://xyz.com", "var1=val&var2=val2");
      * System.out.println(data.content);
      * Enumeration<String> keys = dat.cookies.keys(); // cookies
      * while (keys.hasMoreElements()) {
      *         System.out.println(keys.nextElement() + " = " +
      *                 data.cookies.get(keys.nextElement() + "rn");
      *    }
      * Enumeration<String> keys = dat.headers.keys(); // headers
      * while (keys.hasMoreElements()) {
      *         System.out.println(keys.nextElement() + " = " +
      *                 data.headers.get(keys.nextElement() + "rn");
      *    }
      *
      * Upload a file
      * --------------------------------
      * ArrayList<File> files = new ArrayList();
      * files.add(new File("/etc/someFile"));
      * files.add(new File("/home/user/anotherFile"));
      *
      * Hashtable<String, String> ht = new Hashtable<String, String>();
      * ht.put("var1", "val1");
      *
      * HttpData data = HttpRequest.post("http://xyz.com", ht, files);
      * System.out.println(data.content);
      *
      * @author Moazzam Khan
      */
      public class HttpRequest {
       
              /**
              * HttpGet request
              *
              * @param sUrl
              * @return
              */
              public static HttpData get(String sUrl) {
                      HttpData ret = new HttpData();
                      String str;
                      StringBuffer buff = new StringBuffer();
                      try {
                              URL url = new URL(sUrl);
                              URLConnection con = url.openConnection();
       
                              BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                              while ((str = in.readLine()) != null) {
                                      buff.append(str);
                              }
                              ret.content = buff.toString();
                              //get headers
                              Map<String, List<String>> headers = con.getHeaderFields();
                              Set<Entry<String, List<String>>> hKeys = headers.entrySet();
                              for (Iterator<Entry<String, List<String>>> i = hKeys.iterator(); i.hasNext();) {
                                      Entry<String, List<String>> m = i.next();
       
                                      Log.w("HEADER_KEY", m.getKey() + "");
                                      ret.headers.put(m.getKey(), m.getValue().toString());
                                      if (m.getKey().equals("set-cookie"))
                                      ret.cookies.put(m.getKey(), m.getValue().toString());
                              }
                      } catch (Exception e) {
                              Log.e("HttpRequest", e.toString());
                      }
                      return ret;
              }
       
       
       
       
              /**
              * HTTP post request
              *
              * @param sUrl
              * @param ht
              * @return
              * @throws Exception
              */
              public static HttpData post(String sUrl, Hashtable<String, String> ht) throws Exception {
                      String key;
                      StringBuffer data = new StringBuffer();
                      Enumeration<String> keys = ht.keys();
                      while (keys.hasMoreElements()) {
                              key = keys.nextElement();
                              data.append(URLEncoder.encode(key, "UTF-8"));
                              data.append("=");
                              data.append(URLEncoder.encode(ht.get(key), "UTF-8"));
                              data.append("&amp;");
                      }
                      return HttpRequest.post(sUrl, data.toString());
              }
              /**
              * HTTP post request
              *
              * @param sUrl
              * @param data
              * @return
              */
              public static HttpData post(String sUrl, String data) {
                      StringBuffer ret = new StringBuffer();
                      HttpData dat = new HttpData();
                      String header;
                      try {
                              // Send data
                              URL url = new URL(sUrl);
                              URLConnection conn = url.openConnection();
                              conn.setDoOutput(true);
                              OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
                              wr.write(data);
                              wr.flush();
       
                              // Get the response
       
                              Map<String, List<String>> headers = conn.getHeaderFields();
                              Set<Entry<String, List<String>>> hKeys = headers.entrySet();
                              for (Iterator<Entry<String, List<String>>> i = hKeys.iterator(); i.hasNext();) {
                                      Entry<String, List<String>> m = i.next();
       
                                      Log.w("HEADER_KEY", m.getKey() + "");
                                      dat.headers.put(m.getKey(), m.getValue().toString());
                                      if (m.getKey().equals("set-cookie"))
                                      dat.cookies.put(m.getKey(), m.getValue().toString());
                              }
                              BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                              String line;
                              while ((line = rd.readLine()) != null) {
                                      ret.append(line);
                              }
       
                              wr.close();
                              rd.close();
                      } catch (Exception e) {
                              Log.e("ERROR", "ERROR IN CODE:"+e.getMessage());
                      }
                      dat.content = ret.toString();
                      return dat;
              }
              /**
              * Post request (upload files)
              * @param sUrl
              * @param files
              * @return HttpData
              */
              public static HttpData post(String sUrl, ArrayList<File> files)
              {
                      Hashtable<String, String> ht = new Hashtable<String, String>();
                      return HttpRequest.post(sUrl, ht, files);
              }
              /**
              * Post request (upload files)
              * @param sUrl
              * @param params Form data
              * @param files
              * @return
              */
              public static HttpData post(String sUrl, Hashtable<String, String> params, ArrayList<File> files) {
                      HttpData ret = new HttpData();
                      try {
                              String boundary = "*****************************************";
                              String newLine = "rn";
                              int bytesAvailable;
                              int bufferSize;
                              int maxBufferSize = 4096;
                              int bytesRead;
       
                              URL url = new URL(sUrl);
                              HttpURLConnection con = (HttpURLConnection) url.openConnection();
                              con.setDoInput(true);
                              con.setDoOutput(true);
                              con.setUseCaches(false);
                              con.setRequestMethod("POST");
                              con.setRequestProperty("Connection", "Keep-Alive");
                              con.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
                              DataOutputStream dos = new DataOutputStream(con.getOutputStream());
       
                              //dos.writeChars(params);
       
                              //upload files
                              for (int i=0; i<files.size(); i++) {
                                      Log.i("HREQ", i+"");
                                      FileInputStream fis = new FileInputStream(files.get(i));
                                      dos.writeBytes("--" + boundary + newLine);
                                      dos.writeBytes("Content-Disposition: form-data; "
                                      + "name=\"file_"+i+"\";filename=\""
                                      + files.get(i).getPath() +"\"" + newLine + newLine);
                                      bytesAvailable = fis.available();
                                      bufferSize = Math.min(bytesAvailable, maxBufferSize);
                                      byte[] buffer = new byte[bufferSize];
                                      bytesRead = fis.read(buffer, 0, bufferSize);
                                      while (bytesRead > 0) {
                                              dos.write(buffer, 0, bufferSize);
                                              bytesAvailable = fis.available();
                                              bufferSize = Math.min(bytesAvailable, maxBufferSize);
                                              bytesRead = fis.read(buffer, 0, bufferSize);
                                      }
                                      dos.writeBytes(newLine);
                                      dos.writeBytes("--" + boundary + "--" + newLine);
                                      fis.close();
                              }
                              // Now write the data
       
                              Enumeration keys = params.keys();
                              String key, val;
                              while (keys.hasMoreElements()) {
                                      key = keys.nextElement().toString();
                                      val = params.get(key);
                                      dos.writeBytes("--" + boundary + newLine);
                                      dos.writeBytes("Content-Disposition: form-data;name=\""
                                      + key+"\"" + newLine + newLine + val);
                                      dos.writeBytes(newLine);
                                      dos.writeBytes("--" + boundary + "--" + newLine);
       
                              }
                              dos.flush();
       
                              BufferedReader rd = new BufferedReader(
                              new InputStreamReader(con.getInputStream()));
                              String line;
                              while ((line = rd.readLine()) != null) {
                                      ret.content += line + "rn";
                              }
                              //get headers
                              Map<String, List<String>> headers = con.getHeaderFields();
                              Set<Entry<String, List<String>>> hKeys = headers.entrySet();
                              for (Iterator<Entry<String, List<String>>> i = hKeys.iterator(); i.hasNext();) {
                                      Entry<String, List<String>> m = i.next();
       
                                      Log.w("HEADER_KEY", m.getKey() + "");
                                      ret.headers.put(m.getKey(), m.getValue().toString());
                                      if (m.getKey().equals("set-cookie"))
                                      ret.cookies.put(m.getKey(), m.getValue().toString());
                              }
                              dos.close();
                              rd.close();
                      } catch (MalformedURLException me) {
       
                      } catch (IOException ie) {
       
                      } catch (Exception e) {
                              Log.e("HREQ", "Exception: "+e.toString());
                      }
                      return ret;
              }
      }

      jCarouselLite Extended, Carousel Slider with Paging, NFL.com Style

      If you’ve done some Googling you’ve come across jCarousel Lite and jCarousel by now. Both are excellent solutions as they stand. jCarousel in particular is a full featured JQuery carousel/slider plugin that makes slick presentations of unordered lists a snap. The “Lite” version is just that. A much smaller footprint for a lack of a better term is the main difference. Base functionality is very similar. I extended the “Lite” version as for most implementations it was perfect but there are just a couple key things I wanted that it didn’t have. So I extended it to include these features:

      • Pause the slide when hovered.
      • Add paging that was simple and customizable via css.
      • At the end of the last slide go to the first slide.
        To me those key additions make this already solid plugin even better. In the download below you’ll find two files. The extended uncompressed source and the minified counterpart. This was based on the original source of jCarouselLite v1.0.1  as of 8/1/2011.

      DOWNLOAD

      What you’ll end up with.

      image

      I won’t go into the full CSS styling. You can find this here in the documentation by the author. The below CSS is all you’ll need to add the pager feature. Please note that I chose this method rather than using images for ease of use. If you want to use images simply add your image as a background to the LI and position it centered for both x/y using CSS. It works just fine and makes the plugin nice and light weight as we aren’t managing images for the different states. As long as your image is properly positioned and correctly sized according to the size of your LI it will have a similar effect as if the image tags were right inside the LI.

      NOTE: The Html for the pager is dynamically built within the plugin for simplicity. You could certainly extend this for your needs quite easily if desired. The structure of the dynamic elements are very basic and simplistic. In fact you could easily wire up some custom JQuery using .appendTo() if you wanted to add some sort of element to the li’s.

      Dynamic Pager Html

      <div class="pager">
          <ul>
          <li class=”0”></li>
          <li class=”1”></li>
          <li class=”2”></li>
          <li class=”3”></li>
          </ul>
      </div>

      CSS Styles for above NFL.com carousel look.

      The pager styling:

      .pager
      {    
          position: absolute;
          height: 24px;
          right: 15px;
          bottom: 30px;    
      }
      
      .pager ul
      {    
          list-style-type: none;
          margin: 0px; 
          padding: 0px;  
      }
      
      .pager li
      {
          border: 1px solid #333333;
          line-height: 15px;
          height: 15px;
          width: 15px;
          display: inline-block;
          background-color: #F7F7F7;
          margin: 2px 4px 2px 4px;
          vertical-align: middle; 
          float: right; /* float:right; required for IE because it sucks and can't handle inline-block*/ 
          
          /* CSS 3 Styles */
          opacity: .50;
          filter: “alpha(opacity=50)”;
          -moz-border-right: 3px;
          -webkit-border-radius: 3px;
          -khtml-border-radius: 3px;
          border-radius: 3px;
      }
      
      .pager li:hover
      {
          background-color: #C41313;
          cursor: pointer;
          
          /* CSS 3 Styles */
          opacity:100;
          filter:alpha(opacity=100);
          filter: “alpha(opacity=100)”;
      }
      
      .pager li.selected
      {
          /* CSS 3 Styles */
         background-color: #C41313;
         opacity:100;
         filter:alpha(opacity=100);
         filter: “alpha(opacity=100)”;
      }

      The Overlay style that sits under the pager and contains the description text for the given slide:

      .carousel-overlay
      {
          position: relative;
          bottom: 95px;
          width: 725px;
          height: 75px;    
          background-image: url('Images/Backgrounds/carousel_bkgrnd.png');
          background-repeat: no-repeat;
      }
      
      .carousel-overlay div
      {
        padding: 3px 24px 3px 24px;
        width: 450px;
      }
      
      .carousel-overlay-caption
      {
          font-weight: 600;
          font-size: 1.1em;
          color: #F2F2F2;
      }
      
      .carousel-overlay-description
      {    
          color: #F2F2F2;
      }

      Again I didn’t enclose the Html or the styling for the carousel itself. If you need this please follow the detailed documentation at the author’s site here. If you would like to use the overlay image in the demo you can DOWNLOAD it along with the source plugin.

      Wiring Up Your Carousel

      Wiring up the carousel is extensively covered by the author so I’m only going to hit on the extended options which are very simple.

      <script type="text/javascript">
          $('.carousel').jCarouselLite({
              visible: 1,
              auto: 8000,
              speed: 1200,
              circular: false, // Set circular to false to use onEndFirst.
              onEndFirst: true, // Navigates to first slide on end.
              showPager: true, // Boolean to show or hide the pager.
              pauseOnHover: true, // Pauses the carousel when hovered over
              pager: 'pager', // The class to use for the pager.
              scroll: 1
          });
      </script>

      Wrapping Up

      Please note as normal you will need to add reference in your header to JQuery itself and the extended jCarousellite plugin as normal. I know you are aware of this but for those that are new and in the interest of being complete it should be mentioned.

      To reiterate the ‘”pager” was intentionally created using only CSS and the basic dynamic html for structure. This was because your pager could literally look like anything and may need to be placed in many locations. If you want to place the pager outside of the main carousel element use neg. values for top, bottom, right, left. By using CSS only rather than embedding images it becomes much more tolerant of your unique design.

      This was tested in Opera, Chrome, FireFox, IE 9 and IE 8. I have not tested on any other browsers.

      MVC3 Shared Hosting on GoDaddy

      As of this post GoDaddy Shared Hosting does not directly support MVC3. Have no fear though you just need to add reference to a handful of libraries to make it all work. Additionally you’ll need to set these libraries to copy to local. Simply click the following references listed and set them from false to true as the images below. Note the post Phil Haack has additional details however the list posted there is missing a couple references follow his post but add the additional libraries. Again this may change once GoDaddy updates their shared hosting environment, so this may not be needed by the time your read this. It its not working well you know what to do then…

      • Microsoft.Web.Infrastructure
      • System.Web.Helpers
      • System.Web.Mvc
      • System.Web.Razor
      • System.Web.WebPages
      • System.Web.WebPages.Deployment
      • System.Web.WebPages.Razor
      • system.Web.Abstractions
      • System.Web.Routing

      MVC Menu Generation Class

      Menus can be complex or rather simple. Often if it is just a simple site with maybe one level of sub navigation and a handful of links it might be easiest to just throw in some divs, uls or whatever you choose and build up the menu manually in a partial view. When you start to add more items or they change frequently this is probably not the best solution. When the menu gets larger or every bit of speed counts you may want to cache it as well. So here is a class I put together this afternoon. It uses four things. A class, an Mvc helper method, some Css and a tiny bit of JQuery.

      Quick Note

      There are several other solutions out there for menus in MVC. You should check them out. The reason I did this is that I wanted to leverage the good ol’ SiteMapCollection and SiteMap built into .Net. Additionally something I think many don’t realize is in your Web.SiteMap you can add additional attributes to each node. For example in this article an additional attribute is used called “image”. This is used to add the dropdown arrow you see in the example below. So you could even change the whole structure with the exception of a few required attributes to do whatever you need. So rather than create a custom XML file that takes in controller = “home”, action = “index” and so on I chose to use the SiteMap for this one. Essentially “/Home/Index” is essentially the same thing. Obvious there are exceptions but for the most part this works just fine. The SiteMapCollection is rather easy to work with as you’ll see in this example. However you should as I mentioned check out some other examples. In fact this method in this article I’ve obviously used but have used several others also. Some links to checkout:

      Alternate Menus To Checkout

      MVC SiteMap Provider – This works great but may be more than you need. Used it many times.

      Telerik MVC Menu – Telerik is great if you’re already using their controls.

      Getting Started

      Download Source

      First thing you probably want to do is take a look at the class. All it is doing is building up some strings. You could create tags and so on but this was quick and does the job just fine for mvcmenu_screenshota simple solution to build out some unordered lists. Typically what you would use with JQuery. I won’t go over the whole class. It is heavily commented so you should have no trouble modifying or doing what you want. The biggest key to this class is the small method called “Process Child”. this is because it does some recursion which makes building out the menu possible when you don’t know exactly how many levels of sub menus we’d have in our SiteMap.

      After you make reference (add using to your helper extensions class) to the MVCMenu.cs (see source) in your Helper Extensions class wherever that may be, you’ll need the following Helper to make it all wire up. Note may of the options here could be set statically in the method so that for example you don’t have to pass all those options. Maybe just enable security trimming. Most of these you’ll set once and forget anyway. It does help in development to have them all exposed to you in your view. If you need help creating a Helper or don’t understand what it is, please stop and go here first then come back.

      Adding Helper Method

      /// <summary>
      /// Creates a menu by dynamically creating a string to be returned as MvcHtml.
      /// </summary>
      /// <param name="helper">The Html Helper.</param>
      /// <param name="homeController">Your Home controller name, probably "Home" w/o quotes.</param>
      /// <param name="useIcons">Indicates whether icon images should be used on parent menus.</param>
      /// <param name="iconPosition">For icons on left 0, for right enter 1.</param>
      /// <param name="enableDesc">Enables/Disables tooltips on anchors inside each menu. Use false in most cases.</param>
      /// <param name="enableSecurityTrimming">Provides security trimming when enabled.</param>
      /// <param name="enableCache">Adds caching to the MVCMenu, Selected CSS state will not work when enabled.</param>
      /// <returns></returns>
      public static MvcHtmlString MVCMenu(this HtmlHelper helper, string homeController, 
                                          bool useIcons, int iconPosition, bool enableDesc, 
                                          bool enableSecurityTrimming, bool enableCache)
      {
          MVCMenu mu = new MVCMenu();
          string menu = mu.GetMenu(homeController, useIcons, iconPosition, 
                        enableDesc, enableSecurityTrimming, enableCache);
          return MvcHtmlString.Create(menu);
      }

      Reference Helper in View

      So we have our Helper method in place, have referenced/resolved (Namespace = Origin1Technologies.MVC.Helpers)  MVCMenu.cs, now we’re ready to actually call the helper on our view. See syntax below. You’ll get intellisense as you specify the options. It will be self explanatory.

      @Html.MVCMenu("Home", true, 1, false, false, false)

      CSS Classes Built Into MVCMenu

      There are few classes built into the MVCMenu.cs class. They are set as constants in the class. Feel free to change those to names/conventions that make more sense to you if you like but this is the defautl:

        • “menu-root” – This is the root of our menu. Not to be confused with the root of your SiteMap which is almost never used.
        • “menu-parent” – This is a ul that is nested in menu that has sub items regardless of nexted level.
        • “menuitem” – This is an item in the menu. Essentially an li, from root to the last child.
        • “menuitem-selected” – When a menuitem matches the current route (basically just the controller name) it is set as selected. See comments for a few exceptions and a better understanding.
        • “level{0}” – Where {0} is the level at which the “menu-parent” is at. Level 0 is the root of the menu. The next 1 and so on. This can be helpful in css styling. Often with a good JQuery design this is not needed but it can be handy.

      There are several examples of using ul’s as menus. You can even grab some existing css from a menu plugin (JQuery) and use it or use the menu plugin. The majority use the very structure the Helper builds out. Just modify the CSS class names in the MVCMenu.cs class or your stylesheet to match up with the above described class names and bingo you have a menu.

      Wrap Up

      Now you have some todos. The Helper will build out an unordered list for you with nested unordered lists. You’ll need to wire up some css. That is up to you. The design of a menu is pretty specific to a site so that’ll be up to you. You can use CSS pseudo classes for hovering but probably better to use JQuery for better control. One HINT is to make sure the submenus are position: absolute in most cases for desired look.

        NOTE: this hasn’t been fully debugged. As with many of the things I post here that quick things thrown together for a need and I share them out. Often by the time you read this article the code will have been modified several times. Often I update the download links to the current source but may not always do so. So don’t get frustrated if something breaks just take a moment, do some proper debugging and you’ll be good. You can also comment and if I catch it in time I’ll gladly reply.

      I’m Convinced IE Just Plain Stinks!

      Right now you’re thinking yeah no duh! Had to vent though. Wish I had a dollar for every hour I’ve spend editing CSS styles to work for IE. Such a pain. It’s funny many have never heard of Opera (unless you’re a geek like us) yet I’d use it in a heartbeat over IE. Back to work…

      MVC Data Annotations and Custom Attributes

      So you’ve found out that MVC supports Editor Templates. About 10 min. later you think to yourself hey how can I leverage this to automatically set my textbox width or maxlength and so on. This is a simple example using just a couple classes.

      Create Abstract Class Inherit Attribute:

      public abstract class MetadataAttribute : Attribute
      {
          public abstract void Process(ModelMetadata metaData);
      }  

      This simply creates a void to process any attributes we set to inherit our class MetadataAttribute. The next piece of code will make sense of this.

      Custom Attribute Class:

      public class OptionalAttributes : MetadataAttribute
      {
          public string style { get; set; }   
          public override void Process(ModelMetadata metaData)
          {
             metaData.AdditionalValues.Add("style", this.style);
          }
      }

      All we are doing here is creating a public property to store our Attribute value. In this case a simple style (you would probably do something much more relevant to your project this is just for demonstration purposes) Attribute is created. We then must implement our Process as this is inheriting MetadataAttribute.

      Adding Attribute to View Model

      [OptionalAttributes(style = "width: 150px")]
      public string Email { get; set; }

      Here we reference our OptionalAttributes Class and expose our style, nothing exciting here.

      Wiring it All Up:

      public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
      {
          protected override ModelMetadata CreateMetadata(
              IEnumerable<Attribute> attributes,
              Type containerType,
              Func<object> modelAccessor,
              Type modelType,
              string propertyName)
          {
              var metaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
              attributes.OfType<MetadataAttribute>().ToList().ForEach(x => x.Process(metaData));
              return metaData;
          }
      }

      This is the magic that makes it all work. Here we create our Metadata Provider inheriting from DataAnnotationsModelMetadataProvider. So basically any Attribute that is of type MetadataAttribute will be processed hence our first abstract class we created.

      Adding our Provider to Global.asax:

      protected void Application_Start()
      {
          AreaRegistration.RegisterAllAreas();
      
          RegisterGlobalFilters(GlobalFilters.Filters);
          RegisterRoutes(RouteTable.Routes);
      
          ModelMetadataProviders.Current = new CustomModelMetadataProvider();
      }

      Pretty straight forward right. Since we inherit DataAnnotationsModelMetadataProvider we can simply set the ModelMetadataProviders.Current to our CustomModelMetadataProvider;

      Making it Happen in Our View:

      @model String
      
          @if (!ViewData.ModelMetadata.AdditionalValues.ContainsKey("style"))
          {         
              @Html.TextBoxFor(model => model)
          }
          else
          {
              var width = ViewData.ModelMetadata.AdditionalValues["style"];   
              @Html.TextBoxFor(model => model, new { style = width })
          }  

      O.K so a couple things here. First off what you see is an EDITOR TEMPLATE partial view (you could use the above syntax in your view as well however your model syntax would be more like model => model.Email where Email is the name of your property in your model). Not the strongly typed view itself. Please note that. If you notice the model is of type String.  You can make templates for DateTime or whatever you like. In this case we are describing a simple string. So what is happening here. Well with a simple if else we check to see if the Metadata Key is null. If it has a value we get the value for that key in this case the key is called “style” and then apply it to the style Attribute for our textbox. Now any property in our view model that has a valid value for our Optional Attribute style will be used. No more styling adding css whatever to our view it will all automatically wire up. Can’t stress enough this is NOT a real world implementation but rather just giving you the idea. For instance you should incorporate checking for nulls and correct types but you already know that.

      If you really want to be slick create an HtmlHelper to further extend this and do away with the if else statement and handle it all in your HtmlHelper.

      No code source on this one. Straight forward just copy from the above, be sure to include your usings or imports of namespaces and you’ll be good.

      JQuery Hylite Plugin

      Wrote a little plugin this am to highlight rows of a table or unordered list. This has a little twist to it in that it has a couple of neat little features. Often we need to iterate over rows of data, maybe pullout a guid or index of the row and so on. This is a common task in just about every web application. So I quickly threw this one together. Obviously no guarantees and I’m sure it can be improved upon…feel free to do so.

      What can HyLite do?

      • - highlight a table or list row on hover.
      • - can set a row as selected and then toggle off when clicked again.
      • - when row is selected it can either simply change to the selected class or do a mild animation.
      • - can also animate use JQuery UI. simply pass in your effect and options or use the default pulsate effect.
      • - on hover or click a callback function can report back the index of the row, the guid if you specify the guid element to lookup, the total number of rows and the rows selected on clear.
      • - has two modes select and navigate. select will do just that, navigate will redirect to the url passed.
      • - query string params are supported too. you can pass in an array of key/values and they will be concatenated to the url. if you pass the key guid in the query string params it will if you’ve set the option find the guid element specified and use it, otherwise if it can’t find it, it will use the value you’ve passed.

      Download
      jquery-hylite-1.1.js

      jquery-hylite-1.1.min.js

       

      Instruction
      Nothing different here. Install the one of the above downloaded files in your script directory and then add a reference to the top of your page, master page, mvc shared layout etc. CSS styling is really straight forward. For what ever element you are iterating over just add three states in CSS. Your default, hover and selected states. You can also changes those class names to whatever you want, those are just the defaults so for example your CSS may look something like this:

      CSS

      .mylist
      {
          /* default style*/
      }
      
      mylist.hover
      
      {
          /* hover style*/
      }
      
      mylist.selected
      
      {
          /* selected style*/
      }

      HTML

      Your html is equally simplistic. Say you have an unordered list or table it would be something like the below:

      <ul class=”mylist”>
          <li>My first row of data<span class=”myguid”>100</span></li>
          <li>My second row of data<span class=”myguid”>101</span></li>
      </ul>

      PLUGIN

      *** obviously the below would be wrapped in pageLoad or $(document).ready()***

      - basic usage

      $(.mylist li).hylite();

      - animate using JQuery UI

      animation is only used when in select mode.

      $(.mylist li).hylite({
          uiAnimate: { enabled: true, effect: ‘pulsate’, opt: { times: 1 }, speed: 500 }
      });

      - turn animation off

      $(.mylist li).hylite({
          animate: false
      });

      - changing default selected and hover styles

      $(.mylist li).hylite({
          hoverStyle: ‘some-hover-style’,
          selectedStyle: ‘some-selected-style’
      });

      - navigate to new page after click

      onclick navigate to new page example url: ‘/mypage.html?key1=value&key2=value’

      $(.mylist li).hylite({
          mode: ‘navigate’,
          navParams: { url: ‘/mypage.html’, qsParams: { key1: ‘value’, key2: ‘value’ }
      });

      - using a unique identifier

      $(.mylist li).hylite({
          guidElement: ‘.myGuid’,
          }, myCallbackFunction
      );
      
      function myCallbackFunction (element, data){
          // using the above you could update an input for example anytime there is
          // a mouseover or click event. I like JSON that way it is always in that format no matter what I’m doing.
          // possible options when calling back from main init method are:
          // obj.index (index hovered or selected), obj.rows (total count), obj.guid (the guidElemement )
          // possible options when calling back from clear method (see below)
          // obj.rows, obj.selected (counts the rows that were selected that you’ve cleared)
          // these properties passed back could in part be accessed from the JQuery object that also gets
          // passed back in the init method. meaning element above represents $(this)
      
          var obj = $.parseJSON(data);
          alert(obj.index);
      }

      - clearing selected rows when in select mode

      $(.mylist li).hylite( ‘clear’, myClearCallback);
      
      function myClearCallback(data){
          var obj = $.parseJSON(data);
          alert(‘There were ‘ + obj.selected + ‘ rows cleared.’
      )

      MVC Paging and Sorting

      If you’re new to MVC this is undoubtably something you’ll face at some point. You’ve already been searching Google and found a few examples, heard about MVCContribWebGrid and Telerik. All of which are great solutions. However you want the manual control, don’t want to use tables or you just like to stay up late and bang keys on the keyboard. If that is the case this should at the very least get you on the right track. As with anything I post feel free to critique. It can only help. There are always alternate ways to skin a cat. This is no exception. Be advised as often with code I post I’ve significantly scaled this down to make it easy to follow striping out the application specific stuff.

      So let’s get to it…

      The first thing you’ll need to do is create a class that will handle your paging and sorting. This was expanded on from Scott Gu’s PaginatedList.cs class which didn’t support sorting but we’ll get to that.

      PaginatedList.cs  (abbreviated)

      Declare a few properties:

      public int PageIndex { get; private set; } // Index of your current page.
      public string SortExpression { get; private set; } // The expression to sort on ex: "FullName DESC".
      public string SortColumn { get; private set; } // The column you are sorting on ex: "FullName".
      public string SortDirection { get; private set; } // The direction to sort ex: "DESC" or "ASC".
      public int PageDisplayIndex { get; private set; } // This is the page number to display. Show 1-5 instead of 0-4.
      public int PageSize { get; private set; } // The amount of records per page.
      public int TotalCount { get; private set; } // The total count of records.
      public int TotalPages { get; private set; } // The total number of pages. TotalCount / PageSize
      public int PagerSize { get; private set; } // The size of the pager. How many page numbers to show.
      public int PagerStart { get; private set; } // The starting page in the pager numbers.
      public int PagerEnd { get; private set; } // The ending page of the pager numbers.
      public List<int> Pages { get; private set; } // The list of pager page numbers to display.

      Let’s create the paginated list by passing a few variables from our controller (we’ll get to that shortly) .

      public PaginatedList(IQueryable<T> source, string sortExpression, int pageIndex, int pageSize, int pagerSize)
      {
          //Populate variables.
          PageIndex = pageIndex;
          SortExpression = sortExpression;
          PageDisplayIndex = pageIndex;
          PageSize = pageSize;
          PagerSize = pagerSize; 
      
          //Store the current Sort Column and Direction.
          //There should always be a value but just in case. This is for providing the current sort expression in our Model
          if (sortExpression != null && !string.IsNullOrEmpty(sortExpression))
          {
              string[] curSort = sortExpression.Split(' '); // Split so we can get the col and direction sep.
              SortColumn = curSort[0];
      
              //This could be null so let's check first.
              if (curSort.Length > 1)
              {
                  SortDirection = curSort[1];
              }
      
          }       
      
          //Get the record count and the total pages.
          TotalCount = source.Count();
          TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
      
          //Build out the list of pages available.
          BuildPagerPages(); // See below for this method. Creates the page numbers for the pager.
      
          //Finally Sort the data and select the page size.
          this.AddRange(source.OrderBy(sortExpression)
              .Skip((PageIndex - 1) * PageSize)
              .Take(PageSize));
      }

      Now we need a simple method to build out a list of the page numbers to display. Down load source, there are a couple other properties like “HasNextPage” or “HasPreviousPage” that you’ll need to check in your view or Html Helper to verify the link should be displayed.

      /// <summary>
      /// Creates list of page numbers for pager.
      /// </summary>
      private void BuildPagerPages()
      {
          PagerStart = Math.Max(1, (PageIndex) - PagerSize / 2); // Start page of pager.
          PagerEnd = Math.Min(TotalPages, PageIndex + (PagerSize / 2)); // End page of pager.
      
          //Iterate through the total pages to make page selectors.
          Pages = new List<int>();
          for (var i = PagerStart; i <= PagerEnd; i++)
          {
              Pages.Add(i); // Add page number to list.
          }
      }

      DynamicOrderBy.cs (abbreviated)

      Grabbed this in part from StackOverflow and Adam Anderson’s blog. It’s a great piece of code. I won’t bore you It is included in the source below but checkout the links for further details. Essentially though this class allows us to do our Linq OrderBy with ease.

      Let’s Wire Up Our Controller

      Probably the easiest part of the whole process. All we are doing here is grabbing some values from our route grabbing our data from our repository or where ever and then create our new instance of our PaginatedList class.

      public ActionResult Index(string sort, int? page )
      {
          // Define the page and pager defaults.
          const int pageSize = 10;
          const int pagerSize = 10;
      
          // You could also set this as an optional value
          // and set the default that way too.
          if (sort == null || string.IsNullOrEmpty(sort))
          {
              sort = "FullName ASC";
          }
      
          var accounts = repository.GetList(); // This is simply getting your data. That’s up to you, in this case a repository.
          // Note the page number default there. It is 1 instead of 0. This gets changed in our PaginatedList
          // There are a couple ways you could do this but the point is to have page numbers 1-5 instead of 0-5.
          var paginatedAccounts = new PaginatedList<Account>(accounts, sort, page ?? 1, pageSize, pagerSize);          
      
          return View("Index", paginatedAccounts);
      }

      Setup Routing

      The simplest way to do this is of course in your Global.asax. Or you may have some other custom mechanisim to handle your routes as I do. But for this will show you the Global.asax method for brevity. This is very straight forward. Simply create a new route like below. Essentially you’d have something like “Clients/{sort}/{page}”.

      routes.MapRoute(
          "ClientList", // Route name
          "Clients/{sort}/{page}", // URL with parameters
          new { controller = "Account", action = "Index" } // Parameter defaults
      );

      Last Piece of Puzzle the View and Helper

      Updating the view is very easy. The below is abbreviated showing only the Previous button but you could easily add Next, First and Last if you wanted. So there are a couple ways you could do this. You could use JQuery and build out the url or you can use a Helper. There may be reasons why you might use one over the other but I’m going to show the Helper method. Remember in our PaginatedList class we had some properties called SortColumn, SortExpression and SortDirection right? Well now we need them. You could just pass the SortExpression only if you wanted and then split it up in your class however I chose to expose the expression and also the col and exp separately also. So in our link we simply need to pass in the current or last sort details plus the newly selected details. This is simply for one reason and that is if the column name passed is the same as the previous we need simply to sort the opposite way. If the column is a different one or no column exists we just sort ascending. Simple right. May see complicated but isn’t just read the comments it all makes sense.

      public static MvcHtmlString SortActionLink(this HtmlHelper helper, string text, string actionName,
          string controller, string curColumn, string curDirection, string tag, object routeValues, object htmlAttributes)
      {
          string sortResult, sortCol, sortExpression, sortDir, tagType;
          tagType = "a";
          sortExpression = "{0} {1}"; //Just because I don't want to write + ' ' +...ohh wait I just did. LOL.
          RouteValueDictionary routeDict = new RouteValueDictionary(routeValues);
      
          //Check for null. If this is the first time the page is loaded
          // if so we don't need to update the sort values.
          if (routeDict["sort"] != null)
          {
              sortCol = routeDict["sort"].ToString();
              if (string.Equals(curColumn, sortCol, StringComparison.CurrentCultureIgnoreCase))
              {
                  //We're sorting the same column
                  //Check the direction and then supply the opposite.
                  switch (curDirection)
                  {
                      case "ASC":
                          sortDir = "DESC";
                          break;
                      case "DESC":
                          sortDir = "ASC";
                          break;
                      default:
                          sortDir = "ASC";
                          break;
                  }
                  sortResult = string.Format(sortExpression, sortCol, sortDir);
              }
              else
              {
                  //This is a new column pass the default.
                  //No need to append Sort Direction.
                  //If no sort direction is supplied ASC is assumed.
                  sortResult = sortCol;
              }
      
              //Update the Route data.
              routeDict["sort"] = sortResult;
              //helper.ViewContext.RequestContext.RouteData.Values["sort"] = sortResult;
          }
      
          var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
          var url = urlHelper.Action(actionName, controller, routeDict);
      
          if (tag != null)
              tagType = tag;
      
          TagBuilder build = new TagBuilder(tagType);
          build.MergeAttribute("href", url);
          build.InnerHtml = text;
          build.MergeAttributes(new RouteValueDictionary(htmlAttributes));
          return MvcHtmlString.Create(build.ToString(TagRenderMode.Normal));
      }

      …and the razor markup to make it all work. You are passing in the name for the link, then the action, then the controller. Then we pass in our current values of sort column and direction, then our Route Values which we modified based on the current sort values and finally any html attributes if needed.

      @Html.SortActionLink("Full Name", "Index", "Client", Model.SortColumn, Model.SortDirection, new { sort = "FullName", page = Model.PageDisplayIndex }, null)

      Wrap Up

      To wrap up in your source you will notice that the First, Next etc links can be shown or hidden by a simple if statement that checks the HasPreviousPage bool value that we exposed in our PaginatedList class. Again see source for how to do this, if you don’t already.

      Hope this helps you out,

      C

      Download Source

      mvc_paging_sorting.rar

      Follow

      Get every new post delivered to your Inbox.