Tag Support

By
Dave
Project
Published
10 Jun 2012 22:24
Last Modified
13 Jan 2013 17:19

I currently use Surface Tags for menus, reticles, compasses, and filters.

There are both advantages and disadvantages to this approach. Advantages inlcude the ability of a tag to provide context of which control to add to the screen, and the ease at which a physical object can be moved, rotated, and removed (i.e. no requirement for "close" buttons). Disadvantages include the requirement to have the tags, and what to do in either Windows 7 multi-touch or vertical Surface deployments. With the latter in-mind, I was keen to explore a "tag-free" mode, which uses touch instead of tags.

In order to add a control to the screen without a tag, I needed a gesture which was easy to discover, and which had a low chance of being invoked accidentally. I opted for an analogy to sliding a physical tag from the bezel onto the edge of the screen. To add a default control, I drag a touch from the edge of the screen. In order to add different controls, I slide in a default "virtual tag" until the tag "selection bar" appears, then move in an orthogonal direction to cycle through a circular list of controls, then continue sliding in to place the control. These steps are illustrated below.

Tag Free Hint

Figure 1. Hint to "tag-free" default control.

Tag Free Menu

Figure 2. Slide-in "tag-free" default (menu) control.

Tag Free Compass

Figure 3. Cross-slide to "tag-free" compass control.

Tag Free Reticle

Figure 4. Cross-slide to "tag-free" reticle control.

In a similar way to physical tags, I can rotate the "virtual tags" using two or more touch points placed where the physical tag would normally reside. To remove a tag, I slide or flick it to the edge of the screen.

In order to best support multiple users and/or orientations, it is possible to slide in controls from each edge of the Surface, with the tag "selection bar" and controls being oriented appropriately.

Sitemaps

By
Dave
Project
Published
10 Jul 2010 17:19
Last Modified
13 Jan 2013 18:20

As part of optimising the results from search engines such as Bing and Google, I added a dynamic site map. The structure of this file is defined by sitemaps.org. In order to generate a suitable file, I added a simple action as shown below in Listing 1.

public ContentResult Sitemap()
{
    XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
    XElement root = new XElement(xmlns + "urlset");

    // home
    root.Add(GetUrlElement(xmlns, ""));

    // posts, categories etc
    foreach (...)
    {
        string url = Url.RouteUrl("MyRoute", new { myparam = value });
        root.Add(GetUrlElement(xmlns, url);
    }

    return Content(root.ToString(), "text/xml", Encoding.UTF8);
}

private XElement GetUrlElement(XNamespace xmlns, string relativeUri)
{
    return new XElement(xmlns + "url",
        new XElement(xmlns + "loc", new Uri(Request.Url, relativeUri).AbsoluteUri));
}

Listing 1. Action to generate dynamic sitemap

While not shown above in Listing 1, I also added <lastmod /> elements, e.g. which corresponded to the last-modified date of a post. The final step was to add a suitable route to global.asax, as shown below in Listing 2.

routes.MapRoute(
    "Sitemap",
    "sitemap.xml",
    new { controller = "MyController", action = "Sitemap" }
);

Listing 2. Sitemap route

The sitemap is then available at http://{domain}/sitemap.xml and can be submitted to search engines using their webmaster tools. For Bing, the webmaster pages can be found at http://www.bing.com/webmaster. For Google the link is http://www.google.com/webmasters/tools.

Legacy & Not-Found Links

By
Dave
Project
Published
10 Jul 2010 17:06
Last Modified
3 Jan 2011 01:32

I needed to deal with existing links to my previous blog engine, as well as invalid requests to the new engine.

Legacy Links

As I mentioned previously, my new blog engine has opted for a different URL structure to my old engine. In order to support existing links to my old blog posts, I added some legacy routes to my routing table, as follows:

  1. Category links of the format http://{domain}/{directory}/category/{name}.aspx are mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "category/{name}.aspx",
        new { controller = "MyController", action = "MyAction" });
    
  2. Post links of the format http://{domain}/{directory}/post/{name}.aspx are mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "post/{name}.aspx",
        new { controller = "MyController", action = "MyAction" });
    
  3. Month links of the format http://{domain}/{directory}/{year}/{month}/default.aspx are mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "{year}/{month}/default.aspx",
        new { controller = "MyController", action = "MyAction" });
    
  4. The syndication link http://{domain}/{directory}/syndication.axd?format={format} is mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "syndication.axd",
        new { controller = "MyController", action = "MyAction" });
    

The nice thing about this approach is that I can support these legacy links without any additional code, just these additions to my routing table.

Not-Found Links

Requests to the new blog engine might not return anything for a couple of reasons:

  1. An invalid controller or action is specified
  2. A invalid parameter is passed to a controller action

For invalid controller requests, a catch-all route is a good starting point, such as the following:

routes.MapRoute(
    "Error",
    "{*url}",
    new { controller = "Error", action = "Http404" });

This ensures that requests of an unrecognised pattern will be passed to an error controller, and an appropriate view used to render an error.

For invalid parameters, additional code is required in the relevant controllers to redirect to a "Not Found" view, for example when a requested post is not found.