Web Q&A

Accessible Images, Image Format Converter, and More

Edited by Nancy Michell

Code download available at: WebQA0307.exe (127 KB)
Browse the Code Online

Q I've heard warnings about the use of graphics on a Web site when you want good accessibility. What should I look out for?

Q I've heard warnings about the use of graphics on a Web site when you want good accessibility. What should I look out for?

A Sometimes a Web site will rely on a nice looking graphic that suggests the type of navigation it will provide. But for the visually impaired user, there needs to be much more than a simple graphic that you hope the user will click on based on visual cues. Screen readers get the text to read from an IMG tag's alt attribute, so make sure to include it. Some designers are focused on a pixel-perfect font sizing design, which rings accessibility alarm bells. If your site is to be properly accessible to partially sighted users, your fonts must resize and flow when the user selects larger text.

A Sometimes a Web site will rely on a nice looking graphic that suggests the type of navigation it will provide. But for the visually impaired user, there needs to be much more than a simple graphic that you hope the user will click on based on visual cues. Screen readers get the text to read from an IMG tag's alt attribute, so make sure to include it. Some designers are focused on a pixel-perfect font sizing design, which rings accessibility alarm bells. If your site is to be properly accessible to partially sighted users, your fonts must resize and flow when the user selects larger text.

When your site uses a fixed font size, it can be overridden only via the accessibility options—by selecting "ignore font sizes specified in Web pages." The Microsoft Web site, looks great with the browser default text size and still honors the user's settings without resorting to the override.

MSDN® provides some good tips for creating accessible sites at Accessibility Design Guidelines for the Web. Some things to keep in mind are:

  • Provide text links in addition to any image map links.
  • Provide good keyboard navigation by following the rules.
  • Make sure you have alternatives to all controls and applets.
  • If your site uses frames, have frame-free pages as well.
  • Provide titles for your objects.

In addition, the aforementioned Web site offers information on proper keyboard navigation, file formats, and other tips. For more information on accessibility, see https://www.microsoft.com/enable.

Q How can I programmatically convert a BMP file to a JPG or GIF?

Q How can I programmatically convert a BMP file to a JPG or GIF?

A If you use a language that's compatible with the Microsoft® .NET Framework you can do this pretty easily with the System.Drawing.Image class. You'd just have to create an image object from a file, then resave it, passing in the new graphics format.

A If you use a language that's compatible with the Microsoft® .NET Framework you can do this pretty easily with the System.Drawing.Image class. You'd just have to create an image object from a file, then resave it, passing in the new graphics format.

If your images start out as BMPs, you might want to think about storing them as PNGs. JPEGs are lossy and so the quality may degrade quite badly depending on the contents of the image. GIFs are good for simple graphics containing large fill areas, but are quite limited with regard to colors (they employ a maximum palette of 256 colors). PNGs don't have either of these problems, so for automated graphics conversions, this is your best bet.

The C# code available for download from the link at the top of this article will produce a command-line tool when compiled; this can convert between any of the image formats that the .NET Framework supports.

These lines in the Main function do the conversion:

Image img = null;
img = Image.FromFile(Source);
img.Save(Dest, Format);

Here, Source is a string containing a valid path to an image file, Dest is a string containing a valid path to the output file, and Format is a System.Drawing.Imaging.ImageFormat object.

All the rest of the code available in the download is just for wrapping that up in a command-line tool. It sounds like you were already getting a file of bitmap type, so something like this could be used to convert it to PNG (or another format). The other option would be to simply use the couple of lines of source code you need, assuming your method of grabbing the screen shot is usable from managed code. Both of these techniques require the .NET Framework to be installed.

Q I've heard that ASP.NET pages (.aspx files) and user controls (.ascx files) can't be shared between applications. Is this true?

Q I've heard that ASP.NET pages (.aspx files) and user controls (.ascx files) can't be shared between applications. Is this true?

A Actually, it turns out that there is a fairly simple method for sharing these files by mapping parts of different applications to the same physical directories.

A Actually, it turns out that there is a fairly simple method for sharing these files by mapping parts of different applications to the same physical directories.

Suppose you have two applications, app1 and app2, and would like to share some user controls between them. In terms of virtual and physical paths, this is what you want to have:

Virtual Physical
/app1 c:\app1
/app2 c:\app2
/app1/controls c:\SharedControls
/app2/controls c:\SharedControls

Note how /app1/controls and /app2/controls both map to the same physical path, achieving the desired sharing.

Here are the steps to follow to set this up:

  1. Run the Internet Information Services (IIS) manager (inetmgr).
  2. Create app1 and app2 the same way you always create applications for IIS.
  3. Still in inetmgr, right-click on the newly created app1, and choose New | Virtual Directory.
  4. Enter "controls" for the alias.
  5. Enter "c:\SharedControls" for the directory and complete the wizard.
  6. In inetmgr, right-click on the newly created "controls," and choose Properties.
  7. In the Properties dialog, click "Remove" to the right of the application name. This keeps the virtual mapping, but does not make it an application.
  8. Follow Steps 3 through 7 again, but for app2 instead of app1.

To test it, put a user control test.ascx in c:\SharedControls. Now put some pages in c:\app1 and c:\app2 that use the user control. You'll have something like this:

<%@ register tagprefix="acme" tagname="test" src="controls/test.ascx" %>
<acme:test runat=server/>

Try requesting https://localhost/app1/testpage.aspx and https://localhost/app2/testpage.aspx. They will use the same user control.

While this technique works well and can be very useful, there are some details you should be aware of before deciding to use it. The pages and user controls are shared, but they are compiled separately for each app that uses them. So while you are improving the maintainability of your sites, you are not affecting the memory usage (compared to having several copies of the controls). If the shared pages and user controls use precompiled code-behind assemblies, you will need to copy the assemblies in the bin directory of each app that uses them. As an alternative, you can sign the assemblies and place them in the global assembly cache.

Q I'm generating an XML file that has several data tables in it and I need them to link together. How can I have my XML file point to other parts of my XML file? It looks like XLink, XPointer, or XPath are what I should be using, but it would really help if I had a complete XML example to work with.

Q I'm generating an XML file that has several data tables in it and I need them to link together. How can I have my XML file point to other parts of my XML file? It looks like XLink, XPointer, or XPath are what I should be using, but it would really help if I had a complete XML example to work with.

A This is a common question. XLink and XPointer don't exist in any Microsoft product, and you can't use XPath alone to query external docs (there is a document function, but it's XSLT). So how do you do it? There are two common ways. With XSLT, you'd have to use the document function. For example, you could create an XML file like the following:

<documents>
   <document>foo.xml</document>
   <document>bar.xml</document>
</documents>

To display these files, write a template similar to this:

<xsl:template match="document">
   <xsl:copy-of select="document(string(.))"/>
</xsl:template>

A This is a common question. XLink and XPointer don't exist in any Microsoft product, and you can't use XPath alone to query external docs (there is a document function, but it's XSLT). So how do you do it? There are two common ways. With XSLT, you'd have to use the document function. For example, you could create an XML file like the following:

<documents>
   <document>foo.xml</document>
   <document>bar.xml</document>
</documents>

To display these files, write a template similar to this:

<xsl:template match="document">
   <xsl:copy-of select="document(string(.))"/>
</xsl:template>

The other way is to use a document type definition (DTD). For example, if you create an external entity for foo.xml, you could invoke it with a simple &foo.

Which way is better? The first is, but unless you have XSLT experience there will be a bit of a learning curve. Using XSLT allows you to do very rich processing of the incoming markup, which is what you'll want the vast majority of the time. Any application that involves reporting and which essentially distills information is an ideal use of XSLT. The primary benefit of the DTD method is simplicity. With a definition for each file, the number of lines is equal to only the number of files you have, plus however many times you reference them. It's a low-cost solution.

Q Is it possible to query an XML document in a namespace-agnostic way? Let's say an XML document contains various elements from multiple namespaces and I want to query using XPath but ignore the namespace prefix. The idea is to fetch the data back in a way that allows users to issue XPath queries without remembering the namespace prefix they used when the data was persisted.

Q Is it possible to query an XML document in a namespace-agnostic way? Let's say an XML document contains various elements from multiple namespaces and I want to query using XPath but ignore the namespace prefix. The idea is to fetch the data back in a way that allows users to issue XPath queries without remembering the namespace prefix they used when the data was persisted.

Modifying my example (see Figure 1), can I issue an XPath query with no namespace:

//bookRecord[book/Author='Asimov'][//Price $ge$ 20]

Is this possible using MSXML or System.XML? Do I need to implement a custom solution that will have overhead of extra processing and/or persistence of extra namespace prefix-related metadata?

Figure 1 Sample XML

<Books xmlns="https://tempuri.org/schema1" 
               xmlns:A1="https://tempuri.org/schema2" 
               xmlns:A2="https://tempuri.org/schema3">
  <bookRecord>
  <book>
    <Author>Asimov</Author>
    <Title>NightFall</Title>
  </book>
  <A1:bookCost>
    <Author>Asimov</Author>
    <Title>NightFall</Title>
    <Price>20</Price>
  </A1:bookCost>
  <A2:bookPublisher>
    <Author>Asimov</Author>
    <Title>NightFall</Title>
    <Publisher>NightFall</Publisher>
  </A2:bookPublisher>
  </bookRecord>
<Books>

A XPath allows you to ignore namespaces, but you need to jump though some hoops to get there. You must use the local-name function and perform a string compare on the name. The performance of this operation is not that great. Here's the original query:

//bookRecord[book/Author='Asimov'][//Price $ge$ 20]

The namespace-agnostic query looks like this:

//*[local-name()='bookRecord'][*[local-name()='book']/*[local-
name()='Author']/text()='Asimov'][//*[local-name()='Price'] $ge$ 20]

A XPath allows you to ignore namespaces, but you need to jump though some hoops to get there. You must use the local-name function and perform a string compare on the name. The performance of this operation is not that great. Here's the original query:

//bookRecord[book/Author='Asimov'][//Price $ge$ 20]

The namespace-agnostic query looks like this:

//*[local-name()='bookRecord'][*[local-name()='book']/*[local-
name()='Author']/text()='Asimov'][//*[local-name()='Price'] $ge$ 20]

Namespaces are an integral part of XML. They are not meant to be ignored. In many ways, being completely namespace agnostic is like storing a database of people's height in whole feet as integers. If you have tight control over your input, then you can normalize to a consistent unit of measure, but if you are not careful, the data becomes useless because the unit of measure was ignored. For instance, 2 is greater than 1, but 2 inches is not greater than 1 foot. XML query/transformation standards purposely make writing namespace-agnostic queries possible, but difficult. While there are cases where it may be necessary, it almost always indicates a flawed design, and it just doesn't scale.

Consider the price field. You already need to have tight control over its unit of measure. Why not also control the namespace? Is the Author field supposed to read 'Asimov', 'Isaac Asimov', or 'Asimov, Isaac'? Is <Author> always a subelement of <book>, or might it be <author>, or might it be a subelement of <bookRecord>? You already have an implied schema contract. Why not just extend it to include XML namespaces? Basically, that is what namespaces were designed for. Customers can add whatever other information they want in there, but the standardized information conforms to a schema with a standardized namespace to indicate that.

Got a question? Send questions and comments to  webqa@microsoft.com.

Thanks to the following Microsoft developers for their technical expertise: Vikas Ahuja, Derek Denny-Brown, Chris Dreher, David Ebbo, Dion Houston, Michael Kaplan, Fatih Kihtir, Serguei Kouzmine, Richie Meyer, Leonid Smetanin, and Mark Wilson-Thomas.