Website & App Accessibility for the Visually Impaired

The internet is an important and mandatory part of modern life, including the 285 people million worldwide who have some form of visual impairment (1.4 billion web users worldwide have accessible needs). However, those with visual impairments are often unable to use much of the internet as many sites demand that lack features necessary for low or no vision users. The lack of sight isn’t the only issue as approximately 8% of the male population experiences one of the three forms of color blindness. Visually impaired users comprise of a large segment of the population, usually around 10%. It’s important to provide an accessible site so that you can reach all your customers without inadvertently adding challenges for some. Additionally, there are laws that US government websites as well as websites at some companies or sectors must adhere to defined by http://www.section508.gov/. Before jumping into the technical aspects of accessibility, let’s look at how visually impaired users “see” your web pages.

How the Visually Impaired Use the Internet

The internet is largely visual, and that can exclude people who have visual impairments from accessing its contents. Low vision users often require the use of assistive technology, usually in the form of a screen reader. While most people need to increase the browser’s font size frequently, those with severe impairments must use a screen reader such as JAWS, NVDA, Window Eyes, or one of the many others. If you’ve never used a screen reader, you can watch this video about how a blind student uses the internet that should give you a sense of what it’s like. Screen readers are quite robust in their capabilities, but they do have limitations, and cannot completely substitute for the visual experience, especially in a graphic laden site. That’s because screen readers cannot read videos or image data, only textual data and DOM elements. Additionally, low vision and blind users rely heavily on using the keyboard, since a mouse demands high fidelity eyes as well as hands to work. That means non-mousers use Tab/Shift keyboard combination in a Windows system to move between heading level tags, named anchors, and form elements, sometimes while waiting for the reader to catch up. This is the opposite of a sighted person who’s eyes can quickly scan away from redundant menus or ads to get to the good content.

Others may have a contrast sensitivity rather than, or in combination with, low or no vision. When one has a visual issue that affects contrast, the eye has reduced ability to distinguish between similar shades and levels of brightness. That makes the experience of color different from that of what we classify as normal vision. In fact, the popular social networking site Facebook uses color code #3b5998 (dark blue), because its founder Mark Zuckerberg is colorblind. Eye diseases such as Retinitis Pigmentosa, Retinopathy, Cataracts, and Glaucoma all lead to sharp decreases in contrast and/or color sensitivity. The colors actually look different to folks with contrast sensitivities. What you see as red might look brownish or even gray to someone else. This article at MSDN reveals how users with varying colorblindness and contrast sensitivities see web pages.

Accessible Content and Design

When building accessible web pages, we can do many things that will enable their usage for people with assistive technology. The first and easiest step to take in creating visually accessible web pages is to increase the default font size! Most of the citizens of the world do not have 20/20 vision, so increasing the font size is a tremendous gain with only a single line of code to change in the site’s default CSS file. When crafting hyperlinks be sure to set the link text (i.e., the text between the anchor tags) to something descriptive that matches the link target. Avoid generic text such as the words “Click here”, and be sure to include a complete description of what the user can expect on the other side of the link. Applying the name attribute to anchor tags means users can use the keyboard to skip through to that hyperlink.

Two colors that have low contrast might be fine to someone with normal vision, but it is likely harder to distinguish between the colors, letters, and symbols for someone who is colorblind. Quite possibly the most important design concept for accessibility is to keep the site clean of clutter, and navigable with alternative methods of input than a mouse. You can ensure your color schemes and contrast work well by visiting these sites: http://webaim.org/resources/contrastchecker/ and http://www.checkmycolours.com.

Screen readers must read everything on the page they can access (usually DOM and text), and that includes headers and footers that show on every page in the website. It can take several minutes to plow through a menu that a sighted user can scan in just one second. To remedy this, we use Skip Links. Skip links are HTML anchros that allow the speech software or braille user to bypass redundant information, such as navigational menus. Speech and braille users generally read the page from top to bottom,  and consequently are subjected to repeated information before reaching the heart of the page. All you need are two lines of code. Just put an anchor before the repeated HTML (usually a menu), or right at the beginning of the page, like this:

<a href=”#content”><img src=”empty.gif” height=”15″ border=”0″ alt=”Skip Main Navigation” width=”5″></a>

Then place a named anchor at the beginning of the unique content, where the reader’s eyes would normally skip to:

<a name=”content”></a>

Then you have a skip link. This tag can exist anywhere in the page that you think the screen reader should land, so it can continue reading from there. Adding something as simple as skip links in your source enables the machine reader to save a significant amount of time on behalf of the user. Check out the skip links at WebAIM. They work perfectly.

Accessible HTML Layout and Elements

You can craft accessible HTML with little to no extra effort than you normally use in your daily development tasks. Simple things like using header tags, <h1> through <h6>, make the page navigable via keyboard, as low vision users may have difficulty seeing where to mouse. Avoid using <div>, <span>, or <p> tags with larger fonts, as they don’t allow for keyboard navigation or work well with assistive technologies. Besides, <h1>-<h6> are meant for headings. When displaying media, each <img> element must have clear and succinct alt attribute value, as this is the only way assistive technology such as screen readers and braille machines can convey what is inside the image. This is an easy coding task. When laying out sections of pages with HTML <tables>, be sure to include the <th> elements for navigation and assistive technology, rather than using <td> tags for column headings.

The Web Accessibility Initiative established WAI-ARIA, the Accessible Rich Internet Applications Suite, as a way to make Web content and sites more accessible to people with disabilities of any sort that makes computing unnecessarily difficult. WAI-ARIA published a set of standard attributes so that HTML can integrate with assistive technologies.

Many popular websites, especially news sites, will be disheartened to hear that JavaScript cascading menus and JavaScript popup ads (jQuery lightboxes) make it an order of magnitude harder for low vision and blind users. Often, ads fly in from the top, bottom, and sides of a page, and march across the screen making it impossible for assistive technology to translate what’s going on to the user. Screen readers and braille machines can’t do their jobs properly under these conditions. When this happens, low vision users must leave the site as it becomes simply unusable. Considering the numbers of visually people internet is 10% of your user base, you’re turning away a big chunk of your user base just by using popups. There are better ways to advertise.

Accessible HTML Forms

Submit and reset buttons as well as the <button> element automatically work with assistive technology. You must make some minor modifications to other form elements though. Using the <label> element is a very effective way to enable screen readers and braille machines to present the text and meaning of any HTML input tag. When building HTML forms in general, be consistent with the placement of labels and input fields, and be sure to explicitly state required fields and validation. While it is a common practice to indicate which items are required fields with a non descript red asterisk or icon, it is not very accessible. Adding the word “Required” or a precise description of the validation message to the label makes validation rules clear to readers and devices. Label all input elements of the form. Below is a sample of the proper way to label for accessibility:

<label for=”fullName”>Full Name </label><input id=”fullName” type=”text” />

Notice that the <label>’s for attribute matches the <input>’s id attribute. Without this connection, visual assistants cannot determine which labels belong to which element. When working with dropdowns, label the entire dropdown as well as each individual item in the dropdown by placing text between the <option> elements.. 

<label for=”favoriteLanguage”>Choose your favorite programming language:</label>
<select id=”favoriteLanguage” name=”select”>
    <option value=”1″>C#</option>
    <option value=”2″>VB</option>
    <option value=”3″>JavaScript</option>
    <option value=”4″>Ruby</option>
    <option value=”5″>Lisp</option>
</select>

Checkboxes only need that you use the <label> element, and they’ll work with readers. The American Association of the Blind recommends avoiding Radio buttons, stating that many of the assistive programs out there do not support them well. Since radio buttons often represent simple binary data or yes/no answers, a checkbox should work fine as an alternative. You can move to a drop-down for three or more items.

<label for=”yesNoAnswer”>Is C# your favorite language?</label><input id=”yesNoAnswer” type=”checkbox” />

In addition to open standard HTML, the WAI-ARIA standards provide ARIA tags that you can use to enhance assistive technologies, especially in HTML forms. You can apply the aria-label attribute to elements that have no text, such as an image or a submit button. Form elements that you require the user to fill in should have the aria-required attribute set to “true”. Our previous example that creates a text box to contain a full name can be expanded like this:

<label for=”fullName”>Full Name </label><input aria-label=”Full Name” aria-required=”true” id=”fullName” type=”text” />

It’s not just HTML elements that affect accessibility. While it’s a common practice to set the cursor’s focus to a starting field with JavaScript in HTML forms, doing so interferes with screen readers. The same goes for AJAX scripts that run upon form submissions. Having client side scripts are Ok as long as you don’t forget to test the pages to make sure they work with the keyboard as well as assistive technology, and do not intrude on the user’s workflow. You can read more in depth about techniques for HTML form accessibility at WebAIM online.

Conclusion

The American Association of the Blind has many resources for creating visually accessible web sites. Hide your mouse away while testing web pages to force yourself to be a keyboard user, and test for harmony with assistive technology. You can do so by trying out a few of these 10 free screen readers, or the Web Anywhere browser based screen reader that requires no software installation. Before you publish your web site check out the Web Accessibility Evaluation Tool (WAVE). This tool lets you plug in a URL and it will show that same page with annotations specifying its level of accessibility compliance. Finally, Microsoft has released an Accessibility Center where you can learn about various development techniques and tools to help you reach all of your customers.

Designing and Programming Accessible Websites and Apps

All too often, popular websites and apps fail to design for accessibility, unknowingly resulting in a huge loss of potential customers who cannot consume or access the site. This is all too often due to overestimating the amount of work it takes to make a website accessible, while also underestimating how many users require accessible content. As you’ll see here, there are a few basics you need to know about designing websites and writing code that considers accessible users as first class users.

Web Accessibility Overview

WebAIM http://webaim.org/intro/#people says that about 20% of the population has some sort of disability that affects their lives, and often it affects the way they use the internet. That means 62.8 million Americans and 1.4 billion people worldwide.

There are four main types of disabilities that one might contend with, and that you can consider when designing  websites and apps.

  1. Visual : Vision impairments range from a slight loss of vision to complete blindness. Contrast sensitivity and colorblindness are also common visual impairments.
  2. Auditory: Often, those with hearing impairments cannot discern background noise from important sounds, or have any degree in loss of hearing, or a combination thereof.
  3. Motor : Diseases such as dyslexia, ALS, and Parkinson are often behind a motor impairment. Neurological and spinal damage contribute as well to motor impairments.
  4. Cognitive : ADHD, Dyslexia, and other disorders contribute to the challenges these folks face in using the web.

In other words, there is no “normal”. For every human quality or behavior runs across spectrum. Some can navigate a site easily where others need help. The majority of the population might see red one way, but those with color vision differences will not. People from every walk of life and corner of the planet range in their ability and opportunity. As developers who create websites that can theoretically reach almost the entire world, we should ensure that as many as users possible can access our products that we build, be they websites or native apps. Good developers want to make it as easy as possible for all (or as many as possible) users.

At some point, everybody will need some assistive technology, even if it is what you’d consider a primitive one.

 

image_thumb1

(Image says: I just used the accessibility settings on my iPhone to change the systemwide font to BOLD. Pure win. Give it a try.)

Me too. I must increase fonts on most websites and the default zoom in all browsers to clearly read any text. Sometimes something small like a font increase makes a big difference. Even to those who are considered able bodied like Scott and myself.

Designing with Accessibility in Mind

Way back in the beginning days of the internet, browsers and BBS software displayed little more than text. Fast forward a few decades and now the Web is largely visual. That means that a large percentage of data that crosses the internet is in the form of a image, video, or audio file, often to accompany, and as frequently to replace textual data. This change in data format is due to the wide availability of broadband connectivity. It has also contributed to many sites relying only on graphic data, which shuts out or limits a significant portion of the population from consuming the content. Below is a quick laundry list of things to consider when designing with accessibility (any of the above impairment types) in mind. Notice that most of these things will make it easier for those without disabilities as well.

  • Fonts should be large, clear, and have a high contrast
  • Use HTML5 semantic elements
  • Colors should have high contrast
  • Use a consistent and clear navigation scheme
  • Provide alternate ways to interact with the user instead of just the mouse and keyboard
  • Field tab orders should be straight forward
  • Label fields and elements such as buttons using the <label> element
  • Images should have alt attributes set to something quick and descriptive
  • Movies and animations should provide captions or transcripts
  • Organize content into distinct sections with clear headings

The list of techniques and suggestions above reveal not just things to do to make your site more accessible but these are indeed best practices in the realm of proper standard UX design. That’s because users should be able to access content from their favorite browser, OS, or device, including those with accessibility needs.

Summary

I’ve authored a few articles on designing and programming for accessibility at Internet Explorer’s Modern.IE site, that expand on this post by going into more depth about programming for visual, auditory, motor, and cognitive impairments.

Both WebAIM and Microsoft have an online Accessibility Center containing lots of great information about designing an accessible web.

Use the W3C Geolocation API to create location aware web sites

Location aware services and applications are becoming more important than ever before, as smartphones, tablets, and other fully featured mobile devices become ubiquitous. The W3C Geolocation API gives developers an easy way to integrate client side location aware services within their web pages.

Hello, location aware world!

With the advent of geolocation services and mapping APIs, web sites and applications are popping up daily providing the following types location aware software:

  • Games and social applications, (e.g., Foursquare, Twitter, Trip It, Facebook, etc…)
  • Search applications for local services (e.g., restaurants, shops, entertainment, etc…)
  • Navigation & maps
  • Package shipping/tracking & transportation services
  • Personal health & fitness
  • More…

Notice there’s a good mix of both business and consumer applications that benefit from using geolocation. Fortunately, for developers, location aware web sites and apps are easy to create with web standards like HTML5, JavaScript, and CSS. On top of that, APIs like Bing  or Google Maps make great companions to these sites – and don’t forget 3rd party APIs such as Foursquare or Yelp.

How location aware web sites work

Geolocation apps work by detecting the user’s location via positional data that it receives from a geolocation source.

There are three basic steps to the process:

  1. The client browses to a web page that attempts to access the geolocation source.
  2. The app requests permission to send access the geolocation source.
  3. If the user permits, the app or web site can access and use the user’s positional data. (If not, an error occurs. See below

Once you have obtained the user’s coordinates from the API, you can display maps, give directions,show nearby points of interest

However, keep in mind that the device is the deciding factor regarding the accuracy of the positional data. For example, using a 3G smart phone should yield more accurate results than a desktop machine that connects through an ISP a mile away, using an IP address based method. Devices can use one or more of these methods:

  • Coordinate triangulation
  • Global Positioning System (GPS)
  • Wi-Fi with MAC addresses from RFID, Wi-Fi, and Bluetooth
  • IP address
  • GSM or CDMA cell phone IDs
  • Custom sources: Some phone manufacturers like Nokia

Many smart phones provide a way for users to switch between sources such as 3G or WIFI.

Obtaining the user’s positional data using the HTML5 Geolocation API.

For creating web sites either straight-up JavaScript or jQuery will work fine with the geolocation API. It’s quite small, with only three methods:

Method Description
clearWatch Stops listening for updates to the current geographical location.
getCurrentPosition Obtains the geographic position, in terms of latitude and longitude coordinates.
watchPosition Begins listening for updates to the current geographical location of the device running the client. Use this method to listen for location updates.

Before obtaining the user’s coordinates, a call to Modernizr performs feature detection for geolocation support. If the browser supports geolocation, the code calls the watchPosition() method, and passes success and error callback methods as arguments. Below is the code sample outlining  the call to watchPosition().

$(function () {
    var watchID;
    $("#watch").click(function () {
        if (Modernizr.geolocation) {
            var nav = window.navigator;
            if (nav != null) {
                var geoloc = nav.geolocation;
                watchID = geoloc.watchPosition(successCallback, errorCallback);
            }
        }
        else
        { $("#message").text("You'll need to enter your address manually."); }
    });   
});

A successful completion of the watchPosition() method triggers the successCallback() method. At this time, the code can access the positional data returned from the Geolocation service in order to display Bing maps, list nearby attractions, or whatever is required. Below shows code that obtains the user’s coordinates, then displays the latitude and longitude in a web page <div> element (<div id=”message”>). Notice that the position argument  is populated with data, however not all the properties will contain values (e.g., altitude), depending on the capabilities the device and service.

function successCallback(position) {
 
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
 
    $("#message").text("There you are at " + latitude + " " + longitude);
}

Of course, the code won’t always run successfully. There are many things that can get in the way: bad weather, permission denied, timeouts, no cell reception, and gremlins, to name a few.

Dealing with errors and timeouts

The errorCallback() function does what you might expect, it runs when an error occurs. There are three frequently occurring errors to look for when the errorCallback() executes:

  1. PERMISSION_DENIED
  2. POSITION_UNAVAILABLE 
  3. TIMEOUT

A switch statement works nicely to determine the error source and take corrective action, as each case checks value in the error.code property against a set of constants that correspond to error numbers.

function handle_errors(error) {
    switch (error.code) {
        case error.PERMISSION_DENIED: 
            alert("User did not share geolocation data");
            break;
        case error.POSITION_UNAVAILABLE: 
            alert("Could not detect current position");
            break;
        case error.TIMEOUT: 
            alert("Time out");
            break;
        default: 
            alert("Unknown error");
            break;
    }
} 

 

Summary

Location aware web sites are becoming more important than ever before, and since the W3C Geolocation API is a standard, it works with any technology ranging from plain HTML to ASP.NET.

To get started creating location aware sites, check out the following resources:

W3C Geolocation Introduction

Geolocation APIs & client side frameworks on Code Project

Upload and download files using ASP.NET MVC

Uploading and downloading files are common functions you’ll see in most websites and apps. Fortunately, it’s easy to write code to upload and download files using ASP.NET MVC. To start, we need a view and controller pair to upload a file. This is the same HTML and MVC code that you already know. However, there is no need for the model portion of the MVC pattern if you are only uploading files to disk and not working with a database. Otherwise, files can be a property of a model, for example, a profile picture.

The upload view

First, we must start in the view by creatingan HTML <form> element. You can create the form with the Html.BeginForm helper and pass in the following arguments, in the following order (for this particular overload signature):

  1. Action method name : Action method that processes uploaded file
  2. Controller name : Controller that belongs to above action method
  3. FormMethod : Any acceptable HTTP verb
  4. enctype : Appropriate encoding type

Using Helpers is a great way to take advantage of built in features of the ASP.NET MVC framework such as accessing routing information to create a link or using strongly typed objects to render HTML. In the case of the Html.BeginForm helper, we are rendering a <form> element. The Html.BeginForm helper expects an enctype attribute that designates that the form can send binary data as well as textual data in the HTTP POST request. Since the default encoding type is “text-plain” the HTTP Request will not send binary data (that means files!) to the server along with the usual textual form data, so you must set the enctype attribute to multipart/form data or it will not work!

Below is a complete sample of an MVC view containing a form with a file input and submit button:

@using (Html.BeginForm("Upload", "Home", FormMethod.Post,
    new { enctype = "multipart/form-data" } ))
 {
    @Html.AntiForgeryToken()

    <fieldset>
        <legend>Upload a file</legend>
        <div class="editor-field">
            @Html.TextBox("file", "", new { type = "file" })
        </div>
       <div class="editor-field">
            <input type="submit" value="Upload" />
        </div>
    </fieldset>
}

ASP.NET Web Forms has a FileUpload Control, but in ASP.NET MVC our options are either a plain HTML element such as <input type=”file” /> or the TextBox and TextBoxFor Html Helpers (or any helper that outputs a file field). Any Html Helper that outputs an <input type=”file”> tag works. In the example above, the type, id, and name attributes of this tag are all set to “file”. A quick look in the browser using the IE Developer Tools illustrates what the Html.BeginForm and TextBox helpers from the above code sample have rendered:

image_thumb2

When the user clicks the submit button, it causes an HTTP POST submission to /Home/Upload, which maps to the Home Controller’s Upload action method. The Upload action method receives the file and form data and from there you can perform the actual upload and save the file to disk or put in the cloud.

The upload action method

Action methods of MVC Controllers accept incoming HTTP requests for processing. Each HTTP request has a certain way, or method (POST GET PUT DELETE etc..) that it sends data to the server. The receiving action method must expect that type of request in order to process it. Since HTTP GET is the default, you must apply the HttpPost attribute to the Upload action method. This, of course, matches the method=”post” attribute of the HTML <form> element.

You can access uploaded files in the action method by querying the argument that is of type HttpPostedFileBase . The name of this argument, “file” in the below sample, must match the name of its corresponding file input in the HTML form (i.e., <input type=”file” name=”file”>). This allows ASP.NET MVC’s model binding to occur for file uploads. Ensuring the names match is using a concept called Convention over Configuration and leads to more readable code.

[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
    try
    {
        if (file.ContentLength > 0)
        {
            var fileName = Path.GetFileName(file.FileName);
            var path = Path.Combine(Server.MapPath("~/App_Data/Images"), fileName);
            file.SaveAs(path);
        }
        ViewBag.Message = "Upload successful";
        return RedirectToAction("Index");
    }
    catch
    {
        ViewBag.Message = "Upload failed";
        return RedirectToAction("Uploads");
    }
}

If you need to upload multiple files, use IEnumerable<HttpPostedFileBase> instead of a single HttpPostedFile and loop through the collection to access individual files. When completing an upload, be sure that you have the correct permissions to write to the directory you want, then use classes from System.IO to access the file system and save the file.

System.IO.Path.GetFileName retrieves a file name including its extension, and System.IO.Path.Combine combines two or more strings to create a complete path. Because we’re working with an uploaded file from HTTP, we need Server.MapPath to map a virtual path (HTTP) to a physical directory (C:\) . In this case the mapping is from the webroot, i.e., “~/” to the <project root>\App_Data\Images directory. I’m saving files to the App_Data directory to demonstrate concepts, but in real world sites it is unlikely that internet users would have privileges to save files there, so check with your sys admin or Web hoster as to where you should upload and download files

The sample code above also overwrites any existing files, so if that’s an issue you can generate a unique file name by spinning up a Guid and using that as the name, as shown here:

var guid = Guid.NewGuid();

If the file to upload is accompanying form data, the controller action method signature should contain the model type and an HttpPostedFileBase type for the file. For instance, if the file is a user’s profile picture and part of a Person model, the action method signature should look like the following:

public ActionResult Upload( Person Person, HttpPostedFileBase file ) {… }

If you are using Convention over Configuration then model binding will take care of everything and all you do is access the method’s arguments.

In order to download, we need the same MVC components – a view and a controller. We’ll start at the controller.

The Download Action Method

Usually the UI presents a list or grid of links or images in which the user can click to download an image or file. What we’ll use for this example is a simple list of links of images to download. We can do this by creating a List<string> to store the file names in hyperlinks for download. The code below shows obtaining the directory and file information and adding the file names to the list of strings. The method then returns the list of strings as a model to the view.

public FileActionResult Downloads()
{
    var dir = new System.IO.DirectoryInfo(Server.MapPath("~/App_Data/Images/"));
    System.IO.FileInfo[] fileNames = dir.GetFiles("*.*");
    List<string> items = new List<string>();

    foreach (var file in fileNames)
    {
        items.Add(file.Name);
    }

    return View(items);
}  

The view accepts the list of strings and displays them in the page.

The Download View

Notice that the model for this view is a List<string> type instead of a custom object as usual. Of course, this the view consists of a simple for loop that cycles through a list of strings containing the file names and displays them in <label> and <a> tags. The hyperlinks render so that they point to the Home controller’s Download action method, and pass in the name of the file as a URL parameter. Cycling through a list of strings means that you can use array syntax (i.e., @Model[i]) to access each member in the list.

@model List<string>

<h2>Downloads</h2>
<table>
    <tr>
        <th>File Name</th>
        <th>Link</th>
    </tr>
    @for (var i =0; i <= Model.Count -1; i++) {
        <tr>
            <td>
                @Model[i].ToString()
            </td>
            <td>
                @Html.ActionLink("Download", "Download", new  { ImageName=@Model[i].ToString() })
            </td>
        </tr>
    }
</table>

The image below is what the download view produces. The ActionLink Helper renders links that point to the Home controller’s Download action method, passing in the image name as a query string. I’m using the defaults but you might want to consider configuring Routing so that the URL looks friendlier.

image

As users click on the download link, the Download action method returns a FileResult:

 public FileResult Download(string ImageName)
{
   
return File(“<your path>” + ImageName, System.Net.Mime.MediaTypeNames.Application
.Octet);
}

However, you can also return a FileContentResult, FilePathResult, or  FileStreamResult (don’t forget to match the action method’s return type to the type you will use):

FileContentResult : Use this when want to use a byte array to access the file. In this scenario, you might have obtained the file as a byte array from a database call.

return new FileContentResult(byteArray, “image/jpeg”);

FilePathResult  : Returns a file on disk when you must access via a file path. In the FilePathResult you can return a File type or a FileStreamResult.

return new FilePathResult(“~/App_Data/Images/” + ImageName, System.Net.Mime.MediaTypeNames.Application.Octet);

FileStreamResult : Sends a stream out to the response.

return new FileStreamResult(new FileStream(“<your path>”, FileMode.Open), “image/jpeg”);

The various ways to return a file are very similar but allow sufficient customizations to meet the needs of most scenarios. Sometimes the file is from a stream or byte array, or sometimes you only have a simple file path. Media types as well as the System.Net.Mime.MediaTypeNames enum are available so you can designate the file type for download, as the above samples demonstrate.

Summary

Code for uploading and downloading files are mandatory tasks for Web developers at some point in time. We want to allow users to safely upload and download files to go along with other data, and using ASP.NET MVC means you can use Html Helpers as an easy way to provide this functionality.

More on the dev tools:

Profile & Tune Web Pages Using IE Developer Tools

Investigate Web Pages Using IE Developer Tools

More on the Razor view engine:

Introducing ASP.NET MVC development w/the Razor view engine

Create mobile site layouts with CSS Media Queries

Web sites and apps need to deal with the challenge of serving content to a multitude of browsers and devices. People access web sites and apps via tablets and mobile phones more than ever before, and smart phone usage numbers are growing exponentially. In some populations, mobile Web traffic is higher than traffic from desktops. Technology nowadays means devices and machines range in size from quite small to quite big, and as a result, you the developer must format the content appropriately for each device type. You can accomplish this task by using CSS Media Queries, as they are the best foundation of a responsive UI.

What is a CSS Media Query?

In the past, in order to serve browser or device specific content, we would write code that determines which style sheet to use based on feature detection (or UA detection on older Web sites). Now, instead of having to write code, we can use declarative CSS Media Queries to apply rules to an HTML page based on the requesting device’s characteristics. CSS Media Queries are a way to apply different styles when the requesting browser or device matches the conditions within the media query. With media queries you can target various screen sizes, orientations, and form factors including TV and print, and serve tailored content to them. For example, the media queries below apply a different layout to the tablet sized, landscape screen (768 x 1366 ), vs. the phone sized, portrait screen (320 x 480):

@media screen and (min-width: 768px) and (orientation: landscape)

{
    /* CSS layout for screens with minimum 800px width, landscape */

} @media screen and (max-width: 320px) and (orientation: portrait)

{
    /* CSS layout for screens with maximum 320px width, portrait */
}

While multiple media queries can exist side by side in a single .css file (like the example above), maintenance can be easier if you create one .css file for each type of device you want to support. Doing so allows you to swap style sheets based on the media query defined in the media attribute of the <link> tag.

<link rel=”stylesheet” type=”text/css” href=”smartphones.css” media=”only screen and (max-device-width: 320px)” /> <link rel=”stylesheet” type=”text/css” href=”tablets.css” media

="screen and (min-device-width: 800px)" />

Effectively, media queries rid you of the long lines of “if then” statements otherwise required as conditional logic to figure which style sheet to use for which occasion. You no longer need to resort to using JavaScript to swap style sheets by querying user agents, media types or common device form factors.

Design a page layout with CSS Media Queries

Layouts that fit traditional landscape screens work horribly on smart phones, and thus the need for a responsive layout. A responsive layout is fluid and adaptable, and needs flexible elements such as grids and images that can automatically resize based on the form factor of the requesting device. In addition to resizing elements, you must adapt the page to remove content or unnecessary navigation, because space is at such a premium on small devices.

CSS Media Queries are the foundation of responsive layouts that work across devices. Let’s start with an example of a simple web page with a two column layout in which there is a container <div> and two child <div> tags that render the columns. This layout is for desktop browsers, and later we’ll modify it to run on a phone.

#wrapper 

{ margin:0 auto; height:768px; } #sidebar { background-color:#2885c9; width:24%; height:600px; float:left; } #content { background-color:#229731; width:75%; height:600px; float:right

;
}

<!DOCTYPE html> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <title>CSS Media Queries</title> <link href=”default.css” rel=”stylesheet” /> </head> <body> <div id=”wrapper”> <div id=”sidebar”><span>Side bar</span></div> <div id=”content”><span>Content goes here.</span></div> </div> </body> </html>

The code above looks like this in IE 10, a desktop browser. There is a column to the left (side bar) and one on the right (content).

 

image_thumb3_2

Of course, this layout just won’t work in a small, portrait, screen. Most side bars are for navigation, that when mobilized is painful to use in such a small form. Repositioning the elements so that the side bar is on top of the content is a much better layout for smart phones, because of the change in orientation. We can do this by creating a media query and modifying the CSS to fit smart phone dimensions. Below is a media query definition containing the same three selectors in the previous sample, but modified for a smart phone layout. What was the leftmost column (the side bar) is now positioned at row #1, and its adjacent content area is now positioned at row #2. As you might expect, the sizes of the elements are smaller than in the desktop version.

@media screen and (max-width: 320px) and (orientation: portrait) { #wrapper { width:320px; height:480px; margin: 0 0 0 5px; } #sidebar { width:320px; height:90px; float:left; margin: 0 0 5px 0; } #content { width:320px; height:380px; float:left

;
    }
}

 

image_thumb2_2SNAGHTML103a6a17_thumb1_2

The media query works great in IE10 desktop when resized to smart phone dimensions (left side, above image). However, in some phones or emulators, the above media query does not work (right side, above image). This is because we are missing a key component in mobilizing content – the <viewport> meta.

Mobilizing sites with the viewport meta

When mobile devices receive content formatted for the desktop or larger sized browsers, their default action is to zoom out to show all the content. The phone browser tries to show the page as a desktop browser does but condensed into its tiny, portrait, view. They do this for backward compatibility of the many non-mobilized sites that phones otherwise render incorrectly and break. We can change this behavior and tell phones to optimize their default zoom for mobile by using the viewport meta element. The viewport meta instructs mobile devices to resize the content to fit on their screen or to a certain scale based on the tag’s attribute values. Below is an all-purpose viewport that resizes content to fit the device:

<meta name=“viewport” content=“width=device-width”/>

If you aren’t sure what attributes to use, use the above as the default, as it renders the page to fit the device. Now because the phone will render the content correctly, and the output looks like the following:

SNAGHTML9843bc_thumb1_thumb

Notice the browser has applied the media query this time. This is because the phone’s browser took a cue from the viewport and rendered the page like a mobile browser rather than a desktop browser.

Summary

As you can see, CSS Media Queries are the best way to deliver perfectly fitted content to any device, with less maintenance hassles. Media queries aren’t just for the Web either – you can write Windows Store apps in HTML/CSS/JavaScript and use media queries for Windows 8 features like Snap-View. If you are working cross platform, don’t worry about support as CSS Media Queries have a wide adoption across desktop and mobile browsers.

Lastly, if you would like to go mobile but must support the older-than-dirt browsers like IE8 and below then check out Respond.js. Respond is a polyfill for CSS Media Queries (a mechanism for backwards compatibility), enabling their use in old or unpopular browsers.

Partial views in ASP.NET MVC

What is a partial view and when should I use it?

As developers become familiar with the ASP.NET MVC model, they’ll naturally want to create reusable components filled with content & code. In Web Forms, we could do this by creating a web user control or a web server control, but in MVC, we will use partial views. You’ll notice that conceptually, all of the scenarios below would work for either technology.

  • A stock ticker that’s displayed on each page in an application.
  • A calendar widget that’s displayed on multiple pages in application.
  • A login box.
  • A social networking component used on multiple pages, such as a Facebook Like button.

While ASP.NET MVC partial views may behave similarly in theory to web user controls, syntactically and functionally, the two behave differently. Web user controls found in web forms uses ViewState, PostBacks, and Events while MVC partial views do not use any of the preceding techniques for managing state. Just as ASP.NET web user controls do, partial views can tap into the models contained in your application as well as share data between other application components.

Rendering partial views

Partial views in ASP.NET MVC 3 allow the developer to create reusable content with the very precise and clean Razor syntax (or ASPX). The syntax that renders the partial view is implemented as an Html helper. The Html.Partial helper renders the partial view named “_FeaturedProduct” inline. The first argument of all overloaded methods in the call the @Html.Partial expect the view/partial’s file name, without the file extension. Adhering to convention, the @Html.Partial helper assumes the view resides in the \Views\Shared\ folder. Additional overloads in the @Html.Partial method allow you to pass ViewData/ViewBag objects between views/partials (see below under Sharing data…).

The code below demonstrates the call to render the featured product partial view.

<div>
  @Html.Partial("_FeaturedProduct")
</div
>

Partial views can be rendered inside a Layout Page (or if using MVC 2/3 w/ASPX, the Master Page) as well as regular views.

There are some cases where you might like to step aside and write directly to the HTTP Response stream rather than having a partial view render the results (partials/views use MvcHtmlString/StringWriter). To do so, use the Html.RenderPartial helper.

<div>
  @Html.RenderPartial(“_FeaturedProduct”)
</div
>

Use Html.RenderPartial for streaming images or other elements that are media-centric or where faster download times are highly important.

Creating a partial view

Both partial views and regular views are .cshtml files, with the difference being the folder where the partial views reside: \Views\Shared\. Use the Add New View dialog by accessing the context menu from the \Views\Shared node in Solution Explorer. The Add New View template dialog offers choices for creating your partial views, including the option for strongly typing views. Don’t forget to check off  “Create as a partial view” or you’ll end up with a lot of code to delete.

SNAGHTML44dfac8

Once you’ve created the view you can get started customizing it by simply editing the file. There’s no problem in deleting or modifying the view’s code, as there’s no designer tied to it. The code shown below (_FeaturedProduct.cshtml) is the same code the default view template creates, but modified to display the featured product differently:

<style type=“text/css”>
    .featuredProduct {border: solid 1px #000}
</style
>
<
div>
    <div>
        <h2>
            Our Featured product:<br />
            @ViewBag.FeaturedProduct.Name
        </h2>
    </div>    <div>
        <h3>
            Now discounted to $@String.Format(“{0:F}”, ((decimal)ViewBag.FeaturedProduct.Price)-100)
        </h3>
    </div>  
    <div>
        @Html.ActionLink(“Featured Product Details”, “Details”, new { id = ViewBag.FeaturedProduct.ProductId })
    </div> 
    <div>
        <img class=“featuredProduct” src=@Href(“~/Content/Images”)/@ViewBag.FeaturedProduct.ImageName” alt=Featured Product”/>
    </div
>
</
div
>

As with strongly typed views, strongly typed partial views also support dot notation syntax and access to the model, ViewBag, ViewData and other classes specifically designed for sharing data.

It should be clear when reading through the above code that the syntax of partial view looks the same as a regular view. The important take-away is not about the syntax but how the partial view is used. However, a syntactic benefit to developers is the consistency between both partial and full views, particularly when we need to share data between them.

Sharing Data between views and partial views

It’s a common occurrence to pass data between components of an application, and in this case those components are MVC partials, views & controllers. As previously noted, you should use the ViewBag or ViewData classes to share data across views and controllers. First, a few notes on ViewBag and ViewData:

  • ViewData was available in previous versions; ViewBag was released with MVC 3.
  • ViewData can contain any type of data in a name-value pair format. I.e., ViewData[“Message”] = “Welcome”;
  • ViewBag objects are just wrappers around ViewData objects, and allow developers to code to them using strongly typed syntax.
  • ViewBag objects can be extended by simply setting properties in a more fluent syntax. I.e.,  ViewBag.Customer = new Customer(1,”Smith”);

If code in a controller uses either the ViewBag or ViewData classes, those classes will be available throughout the view’s lifecycle, and that includes its partial views.

The preferable object is the ViewBag. Because of its more fluent and dynamic syntax, there’s more complex objects that can be shared quite easily between components. The sample below demonstrates setting up the ViewBag object in the controller so it will be available to all necessary components:

ProductModel productModel = new ProductModel();        
public ActionResult Index()
{
    ViewBag.FeaturedProduct = new FeaturedProduct(105, "The Most Awesome Bike Ever!", 1000.00M, "bike4.png");          
    return View(productModel.Products);           
}

The ViewBag is accessed inside the view or partial using the follows syntax:

@ViewBag.FeaturedProduct.Name

Summary

Partial views are a great way to reuse fragments of HTML and Razor syntax together, with the ability to easily share data.

Resources:

Introducing Razor

Introducing MVC Development w/the Razor View Engine

Razor View Syntax

Add HTML5 Geolocation plus Bing Maps into ASP.NET MVC views

The Bing Maps API is a free, easy to use API, that allows you to incorporate mapping features into your ASP.NET MVC or application by using Web standards such as JavaScript, and the now supported HTML5 Geolocation.

Bing Maps and ASP.NET MVC

A Bing maps API key is required when using the API. The Bing Maps key allows you to access Geolocation data, and to manipulate an in-view Map object with JavaScript via the Bing Maps API. The Bing Maps Developer Portal is where you can get a key by registering with your Windows Live ID.

Once you’re set with a key, you’ll need these files in your MVC project to develop with Bing Maps:

\Scripts\YourBingMapsScript.js – This file contains your script that works with Bing Maps & Geolocation.

\Content\Site.css – A small bit of CSS is required to style the Map

\Views\BingMaps\Index.cshtml – The MVC view that contains the Bing Map HTML

Since the code is HTML &CSS that conform to Web standards, the bulk of the lives is in the .js file and/or the view. Although using MVC, the model and controller are not necessary for manipulating maps with client side script, but don’t throw out the model and controller yet, as you will need them to work with other data.

The Bing Maps Interactive SDK

If you’re writing code that uses Bing Maps, the Interactive Bing Maps SDK is a “must visit” Web site. By choosing the options on the left of the page, the Bing Maps Interactive SDK creates usable JavaScript, and a “View HTML ” button that exposes the page source.

 

image_2

The Bing Maps API & SDK contain more than an interactive website. Bing Maps development gives you access to many productive development tools such as:

  • The Bing Maps AJAX Control (multiple versions)
  • The Bing Maps iOS Control
  • A Bing Maps Silverlight Control
  • SOAP and REST services
  • More…

You can use the Bing Maps Interactive API to create the equivalent code as the samples shown in this post.

Render Bing Maps in MVC views with the Bing Maps AJAX control.

While it’s called the Bing Maps AJAX control, it’s not a control at all, but a JavaScript library instead. HTML and client side script (JavaScript, jQuery, etc…) are the enabling technologies for Bing Maps on the ASP.NET (or any Web) platform. To reference the Bing Maps AJAX Control, your view needs the <script> reference below.

<script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0" type="text/javascript"></script>

If you plan to use the Bing Maps AJAX control in multiple views, the <script> reference can go in the _Layout.cshtml file, otherwise you can add it to the required views or a different layout page. Inside a view, a <div> element should contain and display the map, and is also the DOM object that you manipulate in code, so it needs an id attribute.

<div id="map"></div>
Since the browser renders a tiny <div> by default, some CSS is necessary, such as the position, width, and height, in which to create a suitable DOM shape for maps. You can save the CSS in the \Content\Site.css file, or its own file, for reusability across views.  
<style>
#map
{
    position:relative; 
    width: 500px; 
    height: 500px; 
    border:0px
}
</style>

After the HTML is in place, you can write code to capture the user's positional data to display a Bing map showing the location, nearby restaurants, or other information requested by the user. 

Display a Bing Map with a Pushpin.

You first need positional data from the user before displaying the map. Once you have the positional data points, i.e., latitude and longitude, you can add a Bing Map to any MVC view. To learn how to obtain positional data for your Web site or app, see this post on creating location aware Web sites with the HTML5 Geolocation.

The code below retrieves the latitude and longitude coordinates from the position argument of a showMap method, then creates an instance of a Map object the position, credentials, center, map type, and zoom level. The code continues on by centering the Pushpin to represent the user’s location in the map.

function showMap(position) {
 
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
 
    var map = new Microsoft.Maps.Map($("#map")[0],
    { 
        credentials: "Your Bing Maps API Key",
        center: new Microsoft.Maps.Location(latitude, longitude),
        mapTypeId:   Microsoft.Maps.MapTypeId.road,
        zoom:        10
    });   
 
    var center = map.getCenter();
    var pin = new Microsoft.Maps.Pushpin(center, { width: 50, height: 50, draggable: false });
    map.entities.push(pin);
}

The result should look something like this (but with your coordinates):

SNAGHTML1015b426

Should you want to customize the Pushpin icon, just change the Pushpin construction arguments, and pass in the path and name of the graphics file (.png, .jpg., and standard Web formats).

var pin = new Microsoft.Maps.Pushpin(center, {icon: 'CustomPushpin.png', width: 50, height: 50, draggable: true}); 

Customizing the map further by adding an Infobox, which is a small rectangle with information about a particular location, takes only a few lines of code. Make sure the clear method is run before pushing an Infobox onto a map or the latest Infobox, Pushpin, or other object will overlay the previous.

 map.entities.clear(); 
 var infoboxOptions = {title:'Bing Maps Rock!', description:'Add an Infobox to a map with very little JavaScript!'}; 
 var defaultInfobox = new Microsoft.Maps.Infobox(map.getCenter(), infoboxOptions );    
 map.entities.push(defaultInfobox);

The above code creates an Infobox on the map similar to the one shown here:

SNAGHTML101866d1 

These are just a few simple, but core examples, of what you can do with Bing Maps. For more information on developing for Bing Maps, see below in the Summary & Resources.

Summary & Resources

The Bing Maps AJAX control is a handy library you can use to add geo-interactivity to your Web sites with little code. Additionally, you can incorporate other APIs to make full featured location aware mapping apps.

Use the W3C Geolocation API to create location aware Web sites

The Bings Map control (Bing Maps API)

Bing Maps Articles

Customizing Bing Maps Pushpins

G. Andrew Duthie demonstrates bing maps in action with CommunityMegaphone.com

Managing data in web applications with HTML5 Web Storage

Before the advent of Web Storage, developers have had to juggle between cookies, session, query strings, and HTML forms to manage data across stateless HTTP requests – with cookies being the only client-side, data storage mechanism. Working with all these different ways to get data from here to there is often tedious at best, but unavoidable, as most web applications do need to preserve state somewhere. At the same time, you also need to consider users who have turned off features like cookies, either knocking both cookies and session objects out of the question or making them harder to work with than they should be.

Enter…HTML5 Web Storage

HTML5 Web Storage, also known as DOM Storage, is a way to preserve state on either the client or server, making it much easier to work with (or should I say against?) stateless nature of HTTP. Web storage gives you, the developer a great deal of flexibility in how you deal with data. In the earlier days of the web, hardware constraints on both the server and client, coupled with low bandwidth, created an environment in which it was much harder to pass data around. Today however, hardware is cheaper, better, and faster, so most of the constraints have been lifted, but the supporting mechanisms to move data around haven’t changed much (e.g., cookies, session, query strings). That is, until the advent and adoption of Web Storage.

Today’s storage issues revolve around these points:

  • Having a choice between storage on the server or client between HTTP requests
  • The need to deal with larger sets of data, to provide a rich client side experience to users
  • Optimize performance and scalability by not forcing data to travel with the HTTP requests, like cookies, form data or query strings.
  • Cross browser friendliness

Web Storage helps solve these important issues, as you can add data to either server (session) storage or a local (client memory) storage.

Web Storage Browser Compatibility

As far as cross browser compatibility goes, all the major browsers support Web Storage.

image_2

 

Web Storage size limits

Web Storage also allows you to cram much more into storage than the traditional 4k that you get with cookies. So far, the value is 5MB; however, browsers can implement the limits they’d like. According to the W3C, here’s the official word on Web Storage size limits.

“A mostly arbitrary limit of five megabytes per origin is recommended. Implementation feedback is welcome and will be used to update this suggestion in the future.”

IE allows up to 10MB, as stated here in the DOM Storage documentation on MSDN.

“Storage size is calculated as the total length of all key names and values, and a single storage area can contain up to 10 million bytes. The remainingSpace property is used to determine the available storage space.”

Moving from a mere 4k to more than 5MB gives you far more space to work with than ever before. Of course, you still need to write the code to work with Web Storage.

Working with the Web Storage API

The Web Storage API is a really small and easy to use library to stash data wherever and whenever you need. However, Web Storage isn’t just a huge bag of cookies or over-bloated session variables. Because you can store data locally, without transmitting it back to the server, applications such as these are far easier to write and maintain.

  • Wizards, multi-page applications
  • Shopping cart data
  • Local document storage, for online word processing, spreadsheets, or blogs
  • Rich, UI heavy web applications that need to display large amounts of data

Applications like these will also perform much better, as bandwidth needs decrease when the data stays put, and doesn’t travel with the HTTP request, as cookies and form elements do.

There are two ways to deal with data in the Web Storage API:

  • Local storage
    • Long term data persistence
    • The local storage object spans multiple windows and persists beyond the current session
    • Values put into localStorage are shared across all windows from the same origin
    • Data is stored on the client. Behaving similarly to cookies, values persist beyond when the browser’s session ends
  • Session storage
    • Short term data persistence
    • Once the browser’s session ends (window/tab closed), sessionStorage ends
    • Values put into sessionStorage are only visible in the window/tab that created them
    • Data is stored on the server

Both objects are properties of the browser’s window object, and you can access them by using JavaScript/jQuery. Both local and session storage objects are of type

Implementing a simple hit counter will work with similar syntax between local and session storage, however, it will behave much differently in each. Take for example, the following code that sets a page count in local storage and outputs it to an element with an id of pageCount :

$(function () {

    if (localStorage.pagecount) {

        localStorage.pagecount = Number(localStorage.pagecount) + 1;

    }

    else {

        localStorage.pagecount = 1;

    }

    $("#pageCount").text(localStorage.pagecount)

});

<div id="pageCount">Page Count</div>

The page count will increment each time this page is visited by the same user agent, regardless of whether or not it’s a different tab or  window. Closing and reopening the browser will continue to show data in local storage, as it’s been saved to disk. Session storage, on the other hand, though similar in syntax, will vanish once the user has closed the browser. Below is a sample of the same code, but done with session storage:

$(function () {

    if (sessionStorage.pagecount) {

        sessionStorage.pagecount = Number(sessionStorage.pagecount) + 1;

    }

    else {

        sessionStorage.pagecount = 1;

    }

    $("#pageCount").text(sessionStorage.pagecount)

}); 

<div id=”pageCount”>Page Count</div>

Due to JavaScript’s dynamic nature, you can create properties on either local or session storage on the fly, as is the case with the pagecount variable in the previous samples. This gives you a lot of flexibility when determining how and where to work with data. This flexibility does come at a price…

Web Storage and security

Security is important to all aspects of development, as where malicious users can gain access to sensitive information or cause damage. Any time that you involve and store data, an attacker will try to gain access to it.

Web Storage is more public than cookies, and you’ll need to take special precautions to ensure security if Web Storage isn’t compromised by attackers. The W3C provides these guidelines on Web Storage security, quoted here:

7.1 DNS spoofing attacks

Because of the potential for DNS spoofing attacks, one cannot guarantee that a host claiming to be in a certain domain really is from that domain. To mitigate this, pages can use TLS. Pages using TLS can be sure that only the user, software working on behalf of the user, and other pages using TLS that have certificates identifying them as being from the same domain, can access their storage areas.

7.2 Cross-directory attacks

Different authors sharing one host name, for example users hosting content on geocities.com, all share one local storage object. There is no feature to restrict the access by pathname. Authors on shared hosts are therefore recommended to avoid using these features, as it would be trivial for other authors to read the data and overwrite it.

Even if a path-restriction feature was made available, the usual DOM scripting security model would make it trivial to bypass this protection and access the data from any path.

7.3 Implementation risks

The two primary risks when implementing these persistent storage features are letting hostile sites read information from other domains, and letting hostile sites write information that is then read from other domains.

Letting third-party sites read data that is not supposed to be read from their domain causes information leakage, For example, a user’s shopping wishlist on one domain could be used by another domain for targeted advertising; or a user’s work-in-progress confidential documents stored by a word-processing site could be examined by the site of a competing company.

Letting third-party sites write data to the persistent storage of other domains can result in information spoofing, which is equally dangerous. For example, a hostile site could add items to a user’s wishlist; or a hostile site could set a user’s session identifier to a known ID that the hostile site can then use to track the user’s actions on the victim site.

Thus, strictly following the origin model described in this specification is important for user security.

As you can see, there is some thought needed before running off to use Web Storage. Additionally, this article from Network World describes HTML5 security issues in general and including specifics on Web Storage.

Summary

Web Storage has the support of all major browsers while promising to be a versatile technology that allows you to create more robust, data driven, client side sites with little code. However, Web Storage is very new and you need to use it with caution, especially when concerned with security.

MSDN Intro to DOM Storage http://msdn.microsoft.com/en-us/library/cc197062(VS.85).aspx