Saturday, 21 August 2010

Geocoding (and reverse-geocoding) with Bing Maps

In previous posts, I’ve explored how to use Bing Maps with SQL Server spatial data. In this post, I want to explore the Bing Maps control a little more. Specifically, I want to look at how to use the Bing Maps control to geocode a street address (that is, find the latitude and longitude coordinates of the address), and how to reverse-geocode a spatial location to find the corresponding street address.

The first step in building a Web page that uses the Bing Maps control to geocode an address, is to add a <div> tag to host the map control and use the page body’s onload event to call a JavaScript function that loads the map – like this:

<head>

<!-- add a reference to the Virtual Earth map control -->
<script type="text/javascript"
        src="
http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3">
</script>

<script type="text/javascript">
    function GetMap() {
        map = new VEMap('mapDiv');
        map.LoadMap();
    }
</script>

</head>
<body onload="GetMap()">

    <div id="mapDiv" style="position:relative; width:600px; height:400px;">
    </div>
</body>

Next, you need a textbox so that users can enter the address they want to geocode, a button they can click to geocode the address, and two textboxes to show the resulting latitude and longitude coordinates.

Address:<input id="txtAddress" type="text" style="width:340px" />
        <input style="width:60px" id="btnFind" type="button" value="Find" onclick="return btnFind_onclick()" />
Latitide:<input id="txtLat" type="text" style="width:400px" />
Longitide:<input id="txtLong" type="text" style="width:400px" />

Note the onclick property of the button control, this calls the function that uses Bing Maps to geocode the address. Here’s the code to do that:

function btnFind_onclick() {
    //Geocode the address to find the Lat/Long location
    map.Geocode(document.getElementById("txtAddress").value, onGeoCode, new VEGeocodeOptions())
}

Note that the code in the btnFind_onclick function calls the Geocode method of the map control, specifying the address to be geocoded, the name of the callback function to use to process the results (onGeoCode), and a VEGeocodeOptions object that ensures the user is shown a list of options when the address has multiple possible matches. The calback function looks like this:

function onGeoCode(layer, resultsArray, places, hasMore, veErrorMessage) {
    var findPlaceResults = null;

    // verify the search location was found
    if (places == null || places.length < 1) {
        alert("The address was not found");
    }
    else {
        // we've successfully geocoded the address, so add a pin
        findPlaceResults = places[0].LatLong;
        addPinToMap(findPlaceResults);
    }
}

The callback function is called when the geocode method returns, and assuming a location has been found the JavaScript calls the following addPinToMap function to display the results:

function addPinToMap(LatLon) {
    // clear all shapes and add a pin
    map.Clear()
    var pushpoint = new VEShape(VEShapeType.Pushpin, LatLon);
    map.AddShape(pushpoint);

    // center and zoom on the pin
    map.SetCenterAndZoom(LatLon, 13);

    // display the Lat and Long coordinates
    document.getElementById("txtLat").value = LatLon.Latitude;
    document.getElementById("txtLong").value = LatLon.Longitude;
}

This adds a pin to the map and centers and zooms to ensure it can be seen clearly. It then displays the latitude and longitude in the textboxes defined earlier.

We now have all the code required to geocode an address, but  what about the opposite? Ideally, we also want the user to be able to click a location on the map and reverse-geocode the point that was clicked to find the address.

Of course, the Bing Maps map control already responds to user clicks, so our application will use right-clicks to enable users to specify a location. We’ll do this by attaching an onclick event handler to the map control in the GetMap function (which you will recall is called when the page loads to display the map), and then checking for a right-click before reverse-geocoding the clicked location:

// added to the GetMap function
map.AttachEvent("onclick", map_click);

function map_click(e) {
    // check for right-click
    if (e.rightMouseButton) {
        var clickPnt = null;

        // some map views return pixel XY coordinates, some Lat Long
        // We need to convert XY to LatLong
        if (e.latLong) {
            clickPnt = e.latLong;
        } else {
            var clickPixel = new VEPixel(e.mapX, e.mapY);
            clickPnt = map.PixelToLatLong(clickPixel);
        }

        // add a pin to the map
        addPinToMap(clickPnt)

        //reverse-geocode the point the user clicked to find the street address
        map.FindLocations(clickPnt, onReverseGeoCode);
    }
}

This code finds the latitude and longitude of the clicked location (in some views, the map control uses X and Y pixel coordinates so we need to check for that), displays a pin on the map at the clicked location, and then uses the FindLocations method of the map control to find the address. A callback function named onReverseGeoCode is used to process the results:

function onReverseGeoCode(locations) {
    // verify the search location was found
    if (locations == null || locations.length < 1) {
        document.getElementById("txtAddress").value = "Address not found";
    }
    else {
        // we've successfully found the address, so update the Address textbox
        document.getElementById("txtAddress").value = locations[0].Name;
    }
}

The completed application looks like this:

BingGeocoder

You can try the page out for yourself here, and you can download the source code from here.

del.icio.us Tags:

Tuesday, 8 June 2010

Creating Multi-Sheet Workbooks with SQL Server 2008 R2 Reporting Services

One thing I’ve learned in over ten years of creating database and reporting solutions, is that no matter how dynamic and interactive you make online reports, no matter how to embed live reporting into the user interface of applications, and no matter how funky a dashboard you design; many executives don’t believe data is real unless it’s in a spreadsheet. That’s why one of the most used features of Reporting Services is the ability to render reports in Excel format.

However, I recently encountered a situation where Content Master hosts a Luminosity learning management system, and uses SQL Server Reporting Services to generate reports of student activity in Excel format. The number of students has grown substantially over time, and we hit an unforeseen problem – The Excel 2003 format that Reporting Services renders the reports in supports a maximum of 65,536 rows per worksheet, and the report (which shows students and all training they have completed) has grown to exceed this limit.

After some head scratching, I investigated enhanced new page-break support in SQL Server 2008 R2 and came up with a solution that works, and which can enhance the ability to create complex reports in Excel format for those pesky executives – so I thought I’d share it here.

Let’s imagine your executives want a report in Excel format that lists every customer, along with their contact details. If you have less than 65,537 customers, you could design a report that simply lists them in a worksheet, but if you have more customers than that (or you want to include headers, spaces, or other elements in your report that will use rows when rendered to Excel), then you’ll need a better solution. Ideally, you might want to create something like this – an Excel workbook with multiple worksheets, consisting of a generic “cover page” and a tab for each letter of the alphabet so that you can view customers by last name.

Workbook

You can download a copy of this workbook from here.

Each worksheet in the workbook lists customers with a last name that begins with the letter on the corresponding worksheet tab, as shown here:

Customers-A

To create this report, I used the AdventureWorks2008R2 sample database (which you can download from here) and the following Transact-SQL query:

SELECT Title, FirstName, LastName, AddressLine1, City, StateProvinceName, PostalCode, CountryRegionName
FROM Sales.vIndividualCustomer

The report includes a tablix data region that includes a details grouping (in which all fields are displayed) and a grouping based on the following expression (which returns the first character of the LastName field in upper-case):

=ucase(left(Fields!LastName.Value, 1))

I also added an image and a textbox to the report, and placed them above the tablix data region as shown here:

ReportDesign

To create the page breaks that generate the worksheets when rendered to Excel,  I’ve used some of the new page-break support in SQL Server 2008 R2. First of all, I’ve set the report’s InitialPageName property to Customer Addresses, as shown here:

ReportProperties

This property defines the default name for the first page of the report (or for all pages if no explicit page breaks with page names are defined). That’s why in the Excel workbook, the “cover page” has this name on its worksheet tab (if the InitialPageName property wasn’t set, the worksheet tab would show the the report name).

Next, I created a page break at the start of the tablix as shown here:

TablixProperties

This causes the data in the table to be displayed on a new page, effectively defining the “cover page” as “everything before this”.

Finally, I used the properties of the grouping I defined earlier to create a page break between each instance of the grouping, and apply a page name based on the same expression used to define the grouping. In other words, there will be a page for each first character of the LastName field, and the page name for this page will be the grouping character.

GroupProperties

You can download the complete solution from here. You’ll need to have an instance of SQL Server 2008 R2 with the AdventureWorks2008R2 database (the DataSet in the report assumes that this is in the default instance of SQL Server 2008 R2 on the local computer).

Exporting this report to Excel creates the desired multi-sheet workbook, with a tab for each initial character of the last name, and a “cover page”.

Hopefully, you can see from this article how easy it is to create multi-sheet workbook reports that will add value to your reporting solutions.

Saturday, 23 January 2010

First Steps with the Silverlight Bing Maps Control

A while back, I posted an article about displaying spatial data from SQL Server with what was then called the Virtual Earth Maps control. The article demonstrated an application that retrieves information about locations visited by a toy stuffed bear named Beanie, and displays those locations on a map. Since then, the Virtual Earth Map control has been renamed Bing Maps, and a Silverlight version of the map control is now available – so naturally, the time has come to update the Beanie Tracker application.

Unlike the Javascript version of the Bing Maps control, to use the Silverlight Bing Maps control, you need to sign up at the Bing Maps Account Center and obtain a key.  However, this is a straightforward process (and free!). Once you have a key, you can create Silverlight applications that display and manipulate the Bing Maps control. To do this, download and install the Bing Maps control.  Then create a new Silverlight application and add a reference to the assemblies provided with the control as shown here:

Ref

Now that you have a reference to the Map control, you can add its namespace to a XAML UserControl and include a map object in the Silverlight user interface as shown here, referencing the key you obtained from the Bing Maps Account Center:

<UserControl x:Class="BeanieTracker.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="500" Width="700" Height="400">
  <Grid x:Name="LayoutRoot">

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0" Grid.Row="0" Orientation="Vertical">
            <Image Name="imgBeanie" Source="Beanie.jpg"></Image>
            <Button Cursor="Hand" Width="195" Height="25" HorizontalAlignment="Left" Content="Show Locations" x:Name="b1" Margin="2,10,0,1" Click="b1_Click"></Button>
       </StackPanel>

        <m:Map Name="map" Grid.Column="1" Grid.Row="0" CredentialsProvider="YOUR_KEY" Width="475" Height="300" />

    </Grid>
</UserControl>

Adding the map control displays a Bing Maps map in Silverlight user interface, enabling users to view the map and interact with it through it’s built in controls for changing the zoom level or view, and moving around the map. However, to add custom functionality, you need to write some code to manipulate the map control.

The Silverlight map control exposes a number of objects with properties and methods you can control programmatically, though some of the functionality in the Javascript version of the control has not been implemented in the Silverlight version. Unfortunately, the functionality that enables you to import a GeoRSS feed as a ShapeLayer onto the map is not implemented in the Silverlight control, so a simpler version of the Beanie Tracker application is required. In this version, I’ve written code to retrieve the GeoRSS feed, and then parse the XML feed and create a pushpin for each GML pos element, as shown here:

private void b1_Click(object sender, RoutedEventArgs e)
{
     Uri url = new Uri("../Feed.aspx?data=locations", UriKind.Relative);
     WebClient client = new WebClient();
     client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
     client.DownloadStringAsync(url);
}

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
     if (e.Error == null)
     {
         StringReader stream = new StringReader(e.Result);
         XmlReader reader = XmlReader.Create(stream);
         string gmlURI = "http://www.opengis.net/gml";

         while (reader.Read())
         {
             if (reader.NodeType == XmlNodeType.Element)
             {
                 if (reader.NamespaceURI == gmlURI && reader.Name == reader.Prefix + ":pos")
                 {
                     string[] loc = reader.ReadInnerXml().Split(" ".ToCharArray());
                     double lat = Double.Parse(loc[0]);
                     double lon = double.Parse(loc[1]);
                     Pushpin p = new Pushpin();
                     p.Location = new Location(lat, lon);
                     map.Children.Add(p);
                 }
             }
         }
     }
}

You can see the resulting application at http://www.graemesplace.com/beanietracker.aspx.

Monday, 11 January 2010

Multi-touch in Silverlight 3: Part 1

 

1. Introduction

1.1 Introduction

One of the cool features in Silverlight 3 is the Multi-touch capabilities.   This feature allows Silverlight developers to create fun and interactive applications that can be used with standard inputs, such as mouse and keyboard, as well as with touch-enabled devices. 

If you have seen Minority Report, you are already aware of the gesture-based technology.  The user can interact with the device to drag objects around, flick to navigate, and pinch to adjust the size of an object.  These concepts have become a standard feature on a variety of devices, including Microsoft Surface, Windows 7, and mobile phones.

Silverlight 3 provides the  fundamentals of receiving touch events from the hardware to the application.  By default, the touch events are fired from Windows 7 to the browser (IE / Firefox).  If the application is not processing the touch events, these events are registered as standard mouse events.

Here are two sample applications that utilize Multi-touch concepts.  You will need a touch-enabled device and Windows 7 to get the full experience.

SlideView

3D SlideView | SlideView

In this two-part blog series, I’ll show you the process that I went through in developing a Silverlight class library with Multi-touch gestures and events.  This process was used in SharePoint Silverview to add Silverlight / Windows 7 touch functionality in a SharePoint environment.  You can find the complete source code at http://code.msdn.microsoft.com/ssv.

1.2 Online Research

Jesse Bishop, Program Manager on the Microsoft Silverlight team, wrote an amazing article about Multi-touch Gesture recognition in Silverlight 3.  The article illustrates on how to register touch events and process standard gestures on individual elements.  The primary concept is to have a single entity that reads the incoming events and determine which element that is being accessed.  After reading Jesse’s article, I got inspired to extend his concept with a variety of gestures and events that can be used in a standard Silverlight application.

1.3 Hardware

Before you can develop or run Silverlight application with Multi-touch, you need the right equipment.  I have been developing on the Dell SX2210T and the HP TouchSmart IQ586.  The HP TouchSmart TX2 is also a good choice.  One interesting thing that I learned was the HP TouchSmart IQ586 only had support for a single touch point.  There is an updated driver to ensure that the monitor utilizes two touch points.  You can check for this by viewing the Pen and Touch information under System Information.  It is ideal to have 2 Touch Points, if you plan to support two-finger gestures such as Scale and Rotate. With the recent release of Windows 7, there may be drivers for existing hardware that will need to be updated. 

1.4 Touch 101

The Touch framework has two primary concept: the TouchPoint object and the FrameReported event.

The TouchPoint object stores data related to a single touch point.  This data is passed from the hardware as the user interacts with the touch device.  The number of touch points vary on the supported hardware.   The first touch point that is fired to the application is called the primary touch point.  The object has two important properties: Position and Action.  The Action property stores the current action state, such as Up, Down, and Move.  The Up and Down actions are similar to the mouse’s MouseLeftButtonUp and MouseLeftButonDown events.  The Move action is the transition action that is constantly called until the user releases the Touch Point, which also invokes the Up action.

The FrameReported event is an application-wide event that sends data from the hardware to the application.  One thing to note is that this event is global to the application rather than an individual element.  The application doesn’t directly know the element that the event is processing.  It is possible to retrieve this information using the VisualTreeHelper.GetPointsUnderCoordinates method.  The TouchFrameEventArgs argument has methods of getting information about touch points.  You can get the current touch points by calling the GetPrimaryTouchPoint and GetTouchPoint methods.

2. Basic Implementation

Demo | Source Code

2.1 Creating the MultiTouch Class Library

Now, let’s start by creating a class library.  The library will be useful for  rapidly developing touch applications.  We will only need two objects for the library: TouchElement control and TouchProcessor (explained in Jesse’s article).

In Visual Studio 2008 (or Visual Studio 2010 Beta 2), create a new Silverlight Application.  Right-click on the Solution in the Solution Explorer and select Add New Project.  In the Project Dialog box, select Silverlight Class Library and name it TouchLibrary.  Remove the default Class1.cs file. 

2.2 TouchElement

We will define the TouchElement control as a content control to allow you to place any element, such as an image, movie clip, or UIElement, into the control.   This is not a requirement for deploying Touch applications, but rather a personal choice for development.  One useful scenario is having a couple of TouchElement controls with images to allow users to drag and scale images around the user interface.

Right-click on the TouchLibrary and select Add New Item.  In the Add New Item Dialog box, select User Control and name it TouchElement.  Replace the TouchElement.xaml content with the following:

<ContentControl x:Class="TouchLibrary.TouchElement"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RenderTransformOrigin="0.5,0.5"
>
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scale" />
<TranslateTransform x:Name="translate" />
</TransformGroup>
</ContentControl.RenderTransform>
<ContentControl.Template>
<ControlTemplate>
<ContentPresenter Margin="0" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}">
</ContentPresenter>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>





Note that this is a Content Control rather than a UserControl.  We have added TranslateTransform and ScaleTransform transforms to render the control’s transformation based on the touch events.



Now it’s time for some code action.  In Jesse Bishop’s article, he implements the concept using ITouchElement as an interface that describes the supported gestures and ManipulateElement as a UserControl that implements the ITouchElement interface.  I decided to merge the code from his elements into the TouchElement content control.   I highly recommend checking out his article if you want to learn the details of the concept.



We will implement the Translate and Scale gestures.  The Translate gesture occurs when the user drags the element from one position to another.  The Scale gesture occurs when the user pinch and scales using two touch points.  There may be scenarios when the developer wants to create their own custom translate and scale events.  For this reason, we will add support for default transforms as well as add custom events that will send information back to the application.



The TransformEventArgs class stores basic data for both Translate and Scale events.




public class TransformEventArgs : EventArgs
{
public TouchElement.TransformType Type { get; set; }
public Point Translate { get; set; }
public double Scale { get; set; }

public TransformEventArgs(TouchElement.TransformType type, Point translate)
{
Type = type;
Translate = translate;
Scale = 1;
}

public TransformEventArgs(TouchElement.TransformType type, double scale)
{
Type = type;
Scale = scale;
Translate = new Point(0, 0);
}
}





The TransformHandler delegate is used for both events, with the TransformEventArgs object as the second parameter.




public delegate void TransformHandler(object sender, TransformEventArgs e);
public event TransformHandler ScaleChanged;
public event TransformHandler TranslateChanged;





We will add three properties to the TouchElement class.  The TouchEnabled property will be used in the TouchProcessor class to check which TouchElement objects need to be processed.  The TouchDrag and TouchScale boolean properties enable the default touch behaviors for drag (translate) and scale events.  By default, the properties are set to false to allow the user to create their own events.




public bool TouchEnabled
{
get { return (bool)GetValue(TouchEnabledProperty); }
set { SetValue(TouchEnabledProperty, value); }
}

public bool TouchDrag
{
get { return (bool)GetValue(TouchDragProperty); }
set
{
SetValue(TouchDragProperty, value);

if (value)
this.TranslateChanged += new TransformHandler(TouchElement_TranslateChanged);
else
this.TranslateChanged -= new TransformHandler(TouchElement_TranslateChanged);
}
}

public bool TouchScale
{
get { return (bool)GetValue(TouchScaleProperty); }
set
{
SetValue(TouchScaleProperty, value);

if (value)
this.ScaleChanged += new TransformHandler(TouchElement_ScaleChanged);
else
this.ScaleChanged -= new TransformHandler(TouchElement_ScaleChanged);
}
}

public static readonly DependencyProperty TouchEnabledProperty =
DependencyProperty.Register("TouchEnabled", typeof(bool), typeof(TouchElement), new PropertyMetadata(true));

public static readonly DependencyProperty TouchDragProperty =
DependencyProperty.Register("TouchDrag", typeof(bool), typeof(TouchElement), new PropertyMetadata(false));

public static readonly DependencyProperty TouchScaleProperty =
DependencyProperty.Register("TouchScale", typeof(bool), typeof(TouchElement), new PropertyMetadata(false));





The TouchDrag and TouchScale properties connect the default event handlers, which validates the input value and directly updates TouchElement’s transform values.




void TouchElement_ScaleChanged(object sender, TransformEventArgs e)
{
double scale = e.Scale;

double s = this.scale.ScaleX + scale;
double MIN_SCALE = 0.25;
double MAX_SCALE = 5.0;

if (s > MIN_SCALE && s < MAX_SCALE)
{
this.scale.ScaleX += scale;
this.scale.ScaleY += scale;
}
else if (s < MIN_SCALE)
{
this.scale.ScaleX = MIN_SCALE;
this.scale.ScaleY = MIN_SCALE;
}
else if (s > MAX_SCALE)
{
this.scale.ScaleX = MAX_SCALE;
this.scale.ScaleY = MAX_SCALE;
}
}

void TouchElement_TranslateChanged(object sender, TransformEventArgs e)
{
this.translate.X += e.Translate.X;
this.translate.Y += e.Translate.Y;
}





The TranslateElement and ScaleElement methods calculate the appropriate transforms from the positions based on the touch events.  After the value is calculated, the event is fired with the TransformEventArgs as a parameter.  This will process the default transformation as well as any custom work that is connected to the event.




private void TranslateElement(Point oldPosition, Point newPosition)
{
double xDelta = newPosition.X - oldPosition.X;
double yDelta = newPosition.Y - oldPosition.Y;

if (yDelta != 0 || xDelta != 0)
RaiseTranslate(new TransformEventArgs(TransformType.TRANSLATE, new Point(xDelta, yDelta)));
}

private void ScaleElement(Point primaryPosition, Point oldPosition, Point newPosition)
{
double prevLength = GetDistance(primaryPosition, oldPosition);
double newLength = GetDistance(primaryPosition, newPosition);

if (prevLength == double.NaN || newLength == double.NaN)
return;

double scale = (newLength - prevLength) / newLength;

if (scale != 0)
RaiseScale(new TransformEventArgs(TransformType.SCALE, scale));
}





The TouchProcessor class calls the TouchPointReported method to process the touch event based on the TouchPoint’s Action property.  On the Down action, we collect information on the TouchPoint.  If it is the initial touch point, then the position is later used for distance calculation.  In the next blog post, I will go into additional functionality that can be added in the TouchDown method, such as a start time for timing touch event for other gestures.  On the Touch Up action, we free up the current point.  The TouchUp method can also be used to process any gestures, which will be detailed in the next blog post.  The Touch Move is where the fun’s at.  After the Down action occurs, the Move action constantly runs until the Up action is called.  If there is only a single touch point, the TranslateElement method is called.  The ScaleElement method is called if there are multiple points.  In Jesse’s article, he also has Rotate functionality, which is processed with multiple points.




public void TouchPointReported(TouchPoint touchPoint)
{
switch (touchPoint.Action)
{
case TouchAction.Down:
TouchDown(touchPoint.TouchDevice.Id, touchPoint);
break;
case TouchAction.Move:
TouchMove(touchPoint.TouchDevice.Id, touchPoint);
break;
case TouchAction.Up:
TouchUp(touchPoint.TouchDevice.Id, touchPoint);
break;
};
}

private void TouchDown(int id, TouchPoint touchPoint)
{
_points.Add(id, touchPoint.Position);

if (_points.Count == 1)
{
_startPoint = touchPoint.Position;
_primaryId = id;
}
}

private void TouchMove(int id, TouchPoint touchPoint)
{
Point oldPoint = _points[id];
Point newPoint = touchPoint.Position;

if ((_points.Count == 1 || id == _primaryId))
TranslateElement(oldPoint, newPoint);
else
ScaleElement(_points[_primaryId], oldPoint, newPoint);

_points[id] = newPoint;
}

private void TouchUp(int id, TouchPoint touchPoint)
{
Point oldPoint = _points[id];
Point newPoint = touchPoint.Position;
_points.Remove(id);
}





2.3 TouchProcessor



The TouchProcessor class manages the global touch functionality by connecting the Touch’s FrameReported event.  Jesse goes into detail about the TouchProcessor class in his article.  Add the following class to the Class Library.  The primary difference in this version is the RootElement property, which is used to connect to the current application’s visual root.  This property is required for the VisualTreeHelper.FindElementsInHostCoordinates method.




public class TouchProcessor
{
#region Data Members
private static Dictionary<int,TouchElement> touchControllers = new Dictionary<int, TouchElement>();
private static bool _istouchEnabled = false;
#endregion

#region Properties
public static UIElement RootElement
{
get;
set;
}

public static bool IsTouchEnabled
{
get { return _istouchEnabled; }

set
{
if (value && !_istouchEnabled)
Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);
else if (!value && _istouchEnabled)
{
Touch.FrameReported -= new TouchFrameEventHandler(Touch_FrameReported);
touchControllers.Clear();
}

_istouchEnabled = value;
}
}
#endregion

#region Constructor
public TouchProcessor()
{ }
#endregion

#region Events
private static void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
TouchPointCollection touchPoints = e.GetTouchPoints(null);

foreach (TouchPoint touchPoint in touchPoints)
{
int touchDeviceId = touchPoint.TouchDevice.Id;

switch (touchPoint.Action)
{
case TouchAction.Down:
TouchElement hitElement = GetTouchElement(touchPoint.Position);

if (hitElement != null)
{
if (!touchControllers.ContainsKey(touchDeviceId))
touchControllers.Add(touchDeviceId, hitElement);

// Prevent touch events from being promoted to mouse events (only if it is over a TouchElement)
TouchPoint primaryTouchPoint = e.GetPrimaryTouchPoint(null);
if (primaryTouchPoint != null && primaryTouchPoint.Action == TouchAction.Down)
e.SuspendMousePromotionUntilTouchUp();

ProcessTouch(touchPoint);
}

break;
case TouchAction.Move:
ProcessTouch(touchPoint);
break;
case TouchAction.Up:
ProcessTouch(touchPoint);

touchControllers.Remove(touchDeviceId);
break;
};
}
}
#endregion

#region Touch Process Events
private static void ProcessTouch(TouchPoint touchPoint)
{
TouchElement controller;
touchControllers.TryGetValue(touchPoint.TouchDevice.Id, out controller);

if (controller != null)
controller.TouchPointReported(touchPoint);
}

private static TouchElement GetTouchElement(Point position)
{
foreach (UIElement element in VisualTreeHelper.FindElementsInHostCoordinates(position, RootElement))
{
if (element is TouchElement)
return (TouchElement)element;

// immediately-hit element wasn't an TouchElement, so walk up the parent tree
for (UIElement parent = VisualTreeHelper.GetParent(element) as UIElement; parent != null; parent = VisualTreeHelper.GetParent(parent) as UIElement)
{
if (parent is TouchElement)
return (TouchElement)parent;
}
}

// no TouchElement found
return null;
}
#endregion
}





3. Test the TouchLibrary



In the Silverlight Project, add TouchLibrary to the project’s References.  This will allow you to add the custom touch functionality to your project. 



In MainPage.xaml, add the following namespace to connect to the new library.




xmlns:touch="clr-namespace:TouchLibrary;assembly=TouchLibrary"             





Add the following to MainPage.xaml to create three TouchElements (two with color rectangles and one with an image).  The image source has the project name pre-pended to ensure that it loads the image from the correct project.  The TouchDrag and TouchScale properties are enabled to allow you to manipulate the items.




<Grid Background="Black">
<touch:TouchElement TouchDrag="True" TouchScale="True" HorizontalAlignment="Left">
<Rectangle Fill="Red" Width="200" Height="200" />
</touch:TouchElement>

<touch:TouchElement TouchDrag="True" TouchScale="True" HorizontalAlignment="Right">
<Rectangle Fill="Blue" Width="200" Height="200" />
</touch:TouchElement>

<touch:TouchElement TouchDrag="True" TouchScale="True" HorizontalAlignment="Center" VerticalAlignment="Bottom">
<Image Source="/TouchApp;component/Rocks.jpg" Width="200" Height="200" />
</touch:TouchElement>
</Grid>





In MainPage.xaml.cs, add the following code to initialize the TouchProcessor object.




TouchProcessor.RootElement = this;
TouchProcessor.IsTouchEnabled = true;





One thing to note is that the Touch functionality doesn’t work in Windowless mode.  By default, Windowless mode is set to false and it is a feature that should be used with caution.  For more information on the limitations of Windowless mode, check out this article.



Compile and run the application.  As mentioned before, you will need the required hardware to recognized the Touch events.  You can drag the three items across the screen as well as scale them to different sizes.  The item is also brought to the front of the screen after release the touch point.



The demo of this version can be found here. 



Screenshot



4. Wrapping Up



Thanks for checking out this article.  Stay tuned for my next blog post, where I introduce more customization for the TouchLibrary, including Gesture support, parented selection, and touch events in a ChildWindow.



http://www.silverlighttoys.com/Articles.aspx?Article=SilverlightMultiTouch

Monday, 4 January 2010

Data-Tier Applications in SQL Server 2008 R2

In a previous post, I discussed some of the new multi-server management capabilities in SQL Server 2008 R2. One of the new features I conspicuously side-stepped covering in that post is the concept of a data-tier application – and that’s what I want to describe in this post.

Data-tier applications provide a useful way to encapsulate all of the logical and physical components of an application that need to be deployed and managed as a unit on a SQL Server instance. For example, consider a typical business application. It probably consists of a number of tiers, including a presentation tier (which might be a Windows Form application or an ASP.NET Web application), a middle-tier (for example a library of .NET assemblies that provide objects to manage the business logic of the application), and a data-tier. The data-tier consists primarily of a logical database (and all the schemas, tables, views, and so on it contains) but it also includes server-level objects (such as any logins that the middle-tier uses to connect to the database server) and the physical database and log files used to store the database.

In the past, deploying or migrating the data-tier of an application has involved examining the database to find its server-level dependencies and physical storage properties, moving the database from its test/staging server to the production server (via backup and restore, SSIS, or a Transact-SQL script to recreate the database schema and data – taking into account any differences in physical storage media), and creating a script to recreate any server-level objects used by the database.

In SQL Server 2008 R2, this task has been simplified through the concept of data-tier applications. Software developers using Visual Studio 2010 will be able to create data-tier applications that encapsulate the entire data tier, or alternatively you can use new wizards in SQL Server Management Studio to create a data-tier application from an existing database, and deploy a data-tier application to a new database.

To create a data-tier application from an existing database, right-click the database you want to package and start the data-tier extraction wizard as shown in the following screenshot.

Picture1

This opens the following wizard screen:

Picture2

The first step is to set the properties of the data-tier application (note that the wizard uses the abbreviation “DAC” – technically, this stands for “Data Tier Application Component”, which you can think of as a unit of deployment, or a deployable package for a data-tier application. The term “data-tier application” is usually taken to mean a deployed instance of a DAC.

Picture3

As well as standard properties such as a name, version, and description for your data-tier application, you specify the file location where the DAC package should be created.

Picture4

The wizard then examines the database and its dependencies, and lists the objects that will included in the DAC. In the November CTP, not all database objects are supported in DACs – for example, you can’t include columns with spatial data types such as geometry or geography. The list of supported objects will no doubt expand over time. In this example, the wizard has identified the database objects included in the database, and also the users and associated logins that are required.

Picture5

Finally, the wizard builds the package for the DAC. The package itself is a single file with the extension .dacpac, as shown here:

Picture6

This file is actually a zip archive that contains a number of XML files describing the components of the DAC. If you append a.zip extension to the filename, you can examine these files as shown here:

Picture7

To deploy the data-tier application defined in the DAC, right-click the server you want to deploy it to and click Deploy Data-tier Application:

Picture8

This starts another wizard, as shown here:

Picture9

The first step is to select the DAC package file you want to deploy:

Picture10

Then you can change the database name and file locations if desired.

Picture11

The wizard summarizes the settings, …

Picture12 

…,and then deploys the data-tier application to the server.

Picture13

You can then use SQL Server Management Studio to confirm that the database and any dependent objects has been deployed. In this case, you can see that the MyAppLogin login has been recreated on the target server along with the database.

Picture14

This ability to treat the entire data-tier as a single, encapsulated package should simplify database application deployment and management significantly.