Simple Implementation of MVC Cascading Ajax Drop Down

Don’t you miss developing the traditional way? where you can just use Update Panels and Auto Post backs, it was so easy to develop the UI before where you can see it in the design view and interact with the objects in a rich manner. But that was the past, now we have MVC where its presents us a different way of developing things the proper way.  If you are starting to use MVC like me then  you might be thinking how do I make cascading drop downs without the Auto Post back and Update Panels then read further as this is how I implemented it in the most easiest way.

Lets start! Lets say you have the following structure below.

You need to have models for each of them

Now you need a controller to perform all of the operations, Lets call it CascadingDropDown.cs, we need 3 Actions Results one for the Index which will Load the page and populate the Category DropDown, Select Category to populate Sub Category Drop Down and Select Sub Category to populate Products Drop Down.  If you notice the name might be confusing that is because it is named after an Action Result not action to perform, which means what Action was invoked for this to happen.  Below is a sample code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    using Models;

    public class CascadingDropDownController : Controller
    {
        public ActionResult Index()
        {
            ProductCatalog productCatalog = new ProductCatalog();
            productCatalog.Categories = ProductCatalog.GetCategories();

            return View(productCatalog);
        }

        [HttpPost]
        public ActionResult SelectCategory(int? selectedCategoryId)
        {
            ProductCatalog productCatalog = new ProductCatalog();
            productCatalog.SubCategories = new List<SubCategory>();

            if (selectedCategoryId.HasValue)
            {
                productCatalog.SubCategories = (from s in ProductCatalog.GetSubCategories()
                                                where s.CategoryId == selectedCategoryId
                                                orderby s.Name
                                                select s).ToList();
            }

            return PartialView("SubCategoriesUserControl", productCatalog);

        }

        [HttpPost]
        public ActionResult SelectSubCategory(int? selectedSubCategoryId)
        {
            ProductCatalog productCatalog = new ProductCatalog();
            productCatalog.Products = new List<Product>();

            if (selectedSubCategoryId.HasValue)
            {
                productCatalog.Products = (from s in ProductCatalog.GetProducts()
                                           where s.SubCategoryId == selectedSubCategoryId
                                           orderby s.Name
                                           select s).ToList();
            }

            return PartialView("ProductsUserControl", productCatalog);

        }
    }
}

Take note we use partial view on the SelectCategory and SelectSubCategory as we will send a partial view to the response.  While on the Index we need the full rendering to the response.  Now you have your controller we need to create those views.  We need 1 view for Index and 3 partial views for the 3 dropdowns.

Now lets create the Index view, all you have to do is to right-click on the Index method on your controller then you can start coding.  This view will display all of the dropdowns.

Index.cshtml

@model MvcApplication1.Models.ProductCatalog
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<table cellpadding="0" cellspacing="4" border="0">
    <tr>
        <td>Category&nbsp;</td>
        <td>&nbsp;:</td>
        <td>@Html.Partial("CategoriesUserControl", Model)</td>
    </tr>
    <tr>
        <td>Sub - Category&nbsp;</td>
        <td>&nbsp;:</td>
        <td><div id="SubCategories">@Html.Partial("SubCategoriesUserControl", Model)</div></td>
    </tr>
    <tr>
        <td>Products&nbsp;</td>
        <td>&nbsp;:</td>
        <td><div id="Products">@Html.Partial("ProductsUserControl", Model)</div></td>
    </tr>
</table>

Now lets create partial views and for those who does not know how just right-click on the parent folder where your Index view is and Select Add -> View then tick the “create as a partial view”.

Now for the codes:

CategoriesUserControl.cshtml

@model MvcApplication1.Models.ProductCatalog
@using (Ajax.BeginForm("SelectCategory""CascadingDropDown"new AjaxOptions { UpdateTargetId = "SubCategories" }))
{ 
    @Html.DropDownListFor(
            m => m.SelectedCategoryId,
            new SelectList(Model.Categories, "Id""Name"),
           string.Empty
        )
}
<script type="text/javascript">
    $('#SelectedCategoryId').change(function () {
        $(this).parents('form').submit();
    });
</script>

SubCategoriesUserControl.cshtml

@model MvcApplication1.Models.ProductCatalog
@if (Model.SubCategories != null && Model.SubCategories.Count() > 0)
{
    using (Ajax.BeginForm("SelectSubCategory""CascadingDropDown"new AjaxOptions { UpdateTargetId = "Products" }))
    { 
    @Html.HiddenFor(m => m.SelectedCategoryId)
    @Html.DropDownListFor(
            m => m.SelectedSubCategoryId,
            new SelectList(Model.SubCategories, "Id""Name"),
            string.Empty
            )
    }
}
<script type="text/javascript">
    $('#SelectedSubCategoryId').change(function () {
        $(this).parents('form').submit();
    });   
</script>

ProductsUserControl.cshtml

@model MvcApplication1.Models.ProductCatalog
@if (Model.Products != null && Model.Products.Count() > 0)
{
    @Html.DropDownList(
        "Products",
            new SelectList(Model.Products, "Id""Name"),
            string.Empty
            )
}

If you notice instead of Html.BeginForm we use Ajax.BeginForm using the parameters below.

  • actionName will be name of your method in your controller
  • controllerName will be theame of your controller
  • AjaxOptions as the name says AjaxOptions, at this point we only need the UpdateTargetId which we defined as <div> in the index.cshtml file.

If you notice there are jQuery scripts below CategoriesUserControl.cshtml and SubCategoriesUserControl.cshtml, they will handle the auto postback on the partially rendered HTML.

Also on your _Layout.cshtml add this on the header if you haven’t done it yet

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

Note : If you are using ASP.Net MVC3 then by default it uses unobtrusive jquery so you need to remove all MicrosoftAjax scripts and use only those two above otherwise the onchange event will open a new page instead of partially rendering it.

Note : If you are using Telerik MVC Controls then you only need the unobtrusive ajax the other script is already referenced by default if you are using Telerik.

Make sure also on your web.config you have the following lines.

<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>

At this point you have done everything you need and your ready to roll.

If you need the full source code you can download it here (http://www.codeproject.com/KB/Blogs/258172/CascadingDropDownMVC.zip).

Modifying your ISA login screen

It is easy to customize your ISA login screen and is similar to configuring your Web Outlook login screen.  Here is how its done.

Changing the Images

First you need to find the location of your files and it is located at

C:\Program Files\Microsoft ISA Server\CookieAuthTemplates\ISA\HTML

Note that there will be see other forders such as

  • cHTML (This is for i-mode mobile browsers)
  • xHTML (This is for Windows Mobile clients)
  • HTML (This is for standard web browser clients)

For this sample I will use HTML only.  Make sure that you backup everything before getting started.

To change how it looks (images and CSS) you need to change the following files, you can use your own images that can correspond to your company branding.

  • lgntop.gif
  • lgnbottom.gif
  • lglnleft.gif
  • lgnright.gif
  • logon_style.css

Those items correspond to the layout of images as illustrated below

ISA Layout

Most of the styles you need to change is in the logon_style.css.  You can use two methods in changing the image, either you use the same name and overwrite the old ones or you can declare in the CSS which image to use, just search for the following image and replace it with the new filename.

For example changing the backround image to Test.jpg

background-image: url(”/CookieAuth.dll?GetPic?formdir=@@FORMDIR&image=Test.jpg”);

Changing the text

Now it does not stop there you can also change the text that shows on the screen by going to

C:\Program Files\Microsoft ISA Server\CookieAuthTemplates\ISA\HTML\nls\en\strings.txt

You notice that there are multiple languages in that folder, so you can also edit the local language you want.

For a summary of what I use and where it maps.  There are the control names inside the string.txt files and the places where it shows on the screen.  In that file you can change text that corresponds to a control ie.

L_LoginButton_Text=”Log On” 

and change it to

L_LoginButton_Text=”Sign In” 

Control Text Reference
L_LoginButton_Text Log On 10
L_WindowTitle_Text Microsoft ISA Server 2006 21
L_ShowDetail_Text show explanation 22
L_HideDetail_Text hide explanation 2
L_ShowSimpleUI_Text I have a slow Internet connection. If you select this option, the Web applications you use may offer fewer features, but will provide a better experience in some situations. This will show only on slow internet connection
L_ShowTrustTitle_Text Security 1
L_ShowPublicUI_Text This is a public or shared computer 3
L_ShowTrustedUI_Text This is a private computer 5
L_PublicDescription_Text Select this option if you are connecting from a public computer. Be sure to log off and close all browser windows to end your session. Read about the <a href= ‘http://go.microsoft.com/fwlink/?LinkId=65796′>security risks</a> of using a public computer. 4
L_BasicTrustDescription_Text Select this option if you are the only person who uses this computer. 6
L_TrustWarning_Text <B>Warning:</B> By selecting this option you acknowledge that the computer complies with your organization’s security policy. 18
L_Password_Text Password: 9
L_UserName_Text Domain\user name: 8
L_DialogRelogon_Text Your session expired. To log on again, click a mail folder (for example, Inbox). Your browser will be redirected to the logon page. This will show when a session had expired
L_BadRequestHeading_Text Unknown Request 19
L_BadRequest_Text The request could not be resolved by the server. 20
L_LogoffUser_Text You have successfully logged off from ISA Server. We recommend that you close all browser windows at this time. 23
L_ContinueButton Continue 17
L_OldPwd_Text Old password: 13
L_NewPwd_Text New password: 14
L_Confirm_NewPwd_Text Confirm new password: 15
L_ChangePassword_Text Change Password 16
L_RequestPwdChange_Text I want to change my password after logging on 7
L_RequestPwdChangeExpl_Text With this option selected, a page used to change your password will be displayed after your credentials are submitted. 12
L_CookiesDisabledWrn_Text Cookies are currently disabled by your browser settings. To access this Web site, cookies must be enabled.<br /><br />Follow these directions to enable cookies (Microsoft Internet Explorer 6 or later):  In Internet Explorer, on the Tools menu, click Internet Options. Click the Privacy tab, then click Sites. In Address of Web site, type the complete address of this Web site. For example, http://www.microsoft.com. Then click Allow. 24
L_RetryButton_Text Retry 25
L_Copyright &copy; 2006 Microsoft Corporation. All rights reserved. 11

ISA Screenshot 1

ISA Screenshot 2

ISA Screenshot 3

ISA Screenshot 4

ISA Screenshot 5

ISA Screenshot 6

ISA Screenshot 7

ISA Screenshot 8

ISA Screenshot 9

That what I only use, there are still a lot of stings to change and not just the ones I mentioned above.

Page Layout

If you want to change the page layout totally you can go to the file name usr_pwd.htm, that file governs the layout and structure of the ISA login page.You can also change the layout of the other pages such as pwd_pcode.htm, pwd_pcode_nxt.htm, usr_pcode.htm, usr_pwd_pcode.htm, logout.htm, etc.

Whats next?

Once you are satisfied with your work you need to do the following to see you changes:

1. Log on to your ISA Server.
2. Launch the ISA Server 2006 Console.
3. Select the Services Tab.
4. Right click on the Microsoft Firewall Service and restart the service
5. Then thats it! Go to the login page and press CTRL+F5 to refresh the screen with the new files you have changed.

Some things I noticed

1. You cannot use javascript on the page.
2. You cannot remove objects like buttons and text.
3. You cannot use blank text
4. If there’s something wrong in what you have done, it will not show but the old screen will show up.
5. If you have any major updates on ISA like patch and security updates, the customizations you have done will be lost.
6. When you’re done changing any files you will need to restart the firewall service for the changes to take effect.

usr_pwd.htm

Tooltips in CSS

Have you ever wondered how to create tootips uisng CSS only.  I had a problem before when I was tasked to create tooltips in a website and imemdiately I had thought of using Javascript as thats what I am used to but to my surprise that website blocks any JavaScripts.  So the next best thing is to use CSS, to show you how to achieve that here is a simple example.  All you need is 2 images, 1 for the main background which covers the top and bottom part of the tooltip and another for the stretching background.

Here is the code to achieve it.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>CSS Tooltips Sample</title>

<style type="text/css">

a.myToolTip
{
 position: relative;
 color: #767676;
 font-weight: bold;
 text-decoration: none;
}
a.myToolTip span
{
 display: none;
}

a.myToolTip:hover
{
 color: red; background:;
}
a.myToolTip:hover span.tooltip
{
 /* opacity with different browsers */
 filter: alpha(opacity:70);
 KHTMLOpacity: 0.70;
 MozOpacity: 0.70;
 opacity: 0.70;
 width:200px; /* width of the tooltip, if you adjust this you need to adjust the width of your image as well*/
 color: #000000; /* tooltip text color */
 text-align: center; /* tooltip text alignment */
 display:block;
 position:absolute;
 top:0px; left:0;
 padding: 15px 0 0 0;
}
a.myToolTip:hover span.top
{
 padding: 30px 8px 0; /* top padding */
 display: block;
 background: url(tooltip.gif) no-repeat top;
}
a.myToolTip:hover span.middle
{
 /* different middle bg for stretch */
 padding: 0 8px;
 display: block;
 background: url(tooltipfiller.gif) repeat bottom;
}
a.myToolTip:hover span.bottom
{
 padding: 3px 8px 10px; /* bottom padding */
 display: block;
 background: url(tooltip.gif) no-repeat bottom;
}
</style><body>
<div id="container">
<a href="#" class="myToolTip">
Mouse Over
<span class="tooltip">
<span class="top"></span>
<span class="middle">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Cras sit amet velit a arcu condimentum fermentum.
Pellentesque est sapien, sollicitudin pretium accumsan sed, mollis quis leo.
Sed ac vehicula lacus. Nulla ac massa eu felis mollis pulvinar sit amet vel eros.
In ultrices facilisis lorem, quis semper lorem gravida vitae.

</span><span class="bottom"></span></span></a>

</div>
</body>
</html>



And here are the images:

Follow

Get every new post delivered to your Inbox.

Join 773 other followers