metadevblog

Windows Phone 7 App Development

Archive for March 2011

WP7 accessing a Windows Service DataService

leave a comment »

This application took 2 minutes to write…

Now that I have demonstrated how Silverlight can access a DataService that is hosted by a WindowsService the final example is to show that it will work on a WP7.

I created a new Windows Phone application and added a Button and Textbox.

I added a Service Reference to the DataService to the Solution.

Finally I pretty much copied the code from the Silverlight application in the previous post.

using WPService.DataService;
using System.Xml.Linq;

namespace WPService
{
    public partial class MainPage : PhoneApplicationPage
    {
        DataServiceClient dataService = null;

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            dataService = new DataServiceClient();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                dataService.GetDateTimeCompleted += new EventHandler<GetDateTimeCompletedEventArgs>(dataService_GetDateTimeCompleted);
                dataService.GetDateTimeAsync();
            }
            catch (Exception ex)
            {
                textBlock1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }

        void dataService_GetDateTimeCompleted(object sender, DataService.GetDateTimeCompletedEventArgs e)
        {
            try
            {
                XElement xResult = (XElement)e.Result;

                string date = xResult.Element("Date").Value;
                string time = xResult.Element("Time").Value;

                textBlock1.Text = string.Format("The date is: {0}\nThe time is: {1}", date, time);

            }
            catch (Exception ex)
            {
                textBlock1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }
    }
}

 

I clicked the Button!

image

Says it all!

Advertisements

Written by metadevblog

March 29, 2011 at 10:00 pm

Posted in WCF, WP7

Tagged with , ,

Using Silverlight with a Windows Service – Part 2

with one comment

In order to get the Silverlight version of the application to work the Windows Service has to be modified so that it can provide the necessary Silverlight clientaccesspolicy.xml file or Flash policy file crossdomain.xml from root domain of the service.

This is accomplished by adding a WebService to the WindowService running on the same port as the existing Data Service and using a URL rewriter to provide the correct xml file to the Silverlight application.

First add empty IWebService.cs and WebService.cs classes.

The IWebService.cs should be replaced with this code:

using System.ServiceModel;
using System.IO;
using System.ServiceModel.Web;

namespace ConsoleApplicationWCF
{
    [ServiceContract]
    public interface IWebServer
    {
        [OperationContract, WebGet(UriTemplate = "ClientAccessPolicy.xml")]
        Stream GetClientAccessPolicy();
    }
}

 

Note: You will need to add a reference to System.ServiceModel.Web which can only be added when the dialog is filtered by .NET 4.0 Framework (and not the Client framework).

The key difference between this interface and the one use for the DataService is the WebGet() that has been added to the Operation Contract and it will invoke the GetClientAccessPolicy() method when a GET request matches the template.  The more typical use of this pattern is to respond to more generic request pattern, however it is ideal for handling the Silverlight request.

http://localhost:1234/clientaccesspolicy.xml

The code to handle the request is also very simple:

using System.IO;
using System.ServiceModel.Web;
using System.Reflection;

namespace ConsoleApplicationWCF
{
    public class WebServer : IWebServer
    {
        public const string AssemblyRootPath = "ConsoleApplicationWCF.";

        public Stream GetClientAccessPolicy()
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
            return Assembly.GetExecutingAssembly().
               GetManifestResourceStream(AssemblyRootPath + "ClientAccessPolicy.xml");
        }
    }
}

 

The AssemblyRootPath is the namespace for the application.  This should be ConsoleApplicationWCF but can be confirmed by looking at the Default Namespace in the Projects Properties.

Add the following XML as a ClientAccessPolicy.xml file to the project and make sure that the Build Action for the file is set to ‘Embedded Resource’.  When the project is built the xml file will be built into the binary file.

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

 

When the method is invoked the file data is returned to the caller.

The final part of the process is to create a web server endpoint to handle this contracted request.

   <behaviors>
    <endpointBehaviors>
      <behavior name="WebServerEndpointBehavior">
        <webHttp />
      </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
      <behavior name="WebServerBehavior">
        <serviceMetadata httpGetEnabled="false" />
        <serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="true" />
      </behavior>
        ...
    </serviceBehaviors>
   </behaviors>

 

Notice that there is an endpointBehaviour and a serviceBehaviour. 

These two behaviours are used in the definition of the WebServer service:

    <service behaviorConfiguration="WebServerBehavior" name="WebServer">
      <endpoint address="" behaviorConfiguration="WebServerEndpointBehavior"
        binding="webHttpBinding" contract="IWebServer">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:1234/" />
        </baseAddresses>
      </host>
    </service>

 

Notice that the <service> behaviourConfiguration uses the WebServerBehaviour and the <endpoint> behaviourConfiguration uses the WebServerEndpointBehaviour.  I know that this looks simple but it’s driven me mad while configuring WCF previously – the trick I think is to make sure the names are really reflecting the configuration!

The binding for the WebServer is ‘webHttpBinding’ which will respond to the HTTP GET method that Silverlight makes (and notice that the GetClientAccessPolicy() outgoing content type is ‘text/html’

Here is the full service configuration for both the WebService and the DataService:

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WebServerEndpointBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="WebServerBehavior">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="true"/>
        </behavior>
        <behavior name="DataServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="WebServerBehavior" name="ConsoleApplicationWCF.WebServer">
        <endpoint address="" behaviorConfiguration="WebServerEndpointBehavior" 
             binding="webHttpBinding" contract="ConsoleApplicationWCF.IWebServer">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/"/>
          </baseAddresses>
        </host>
      </service>
      <service behaviorConfiguration="DataServiceBehavior" name="ConsoleApplicationWCF.DataService">
        <endpoint address="" binding="basicHttpBinding" contract="ConsoleApplicationWCF.IDataService">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/DataService"/>
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

And now when the Silverlight application runs and the button is pressed it will request the client access policy file, validate it and then assuming its all OK will fetch the date and time. Awesome!

image

Written by metadevblog

March 28, 2011 at 10:58 pm

Posted in Silverlight, WCF

Tagged with ,

WCF–Testing the DataService

leave a comment »

The quickest way to test that the Data Service is working is to create a simple Windows Forms based application as this will be able to connect without encountering any of the cross domain issues that a Silverlight application will hit.

Once this test has completed then the WCF service will be changed so that a Silverlight application can be created.

Create a new Windows Forms based application in VS2010.  Its best to develop the application in a separate instance of VS2010 rather than adding new project to the solution because then the DataService can be left running while the client is being developed.

Ensure that the ConsoleApplicationWCF is running and the DataService console window is displayed.

Add a button and a label to the Form1 that will have been created automatically by VS.

In the Project Solution right click on References and select Add Service Reference.  Enter the address of the DataService (e.g. http://localhost:1234/DataService).  Click the Advanced button and tick the ‘Generate asynchronous operations’ checkbox.  Asynchronous operations are slightly more tricky to use but is the only option when using Silverlight so it may as well be implemented now.

Once the service reference has been created (and you can see that the process creates quite a bit of code and support files if you ‘show all files’ in the project Solution explorer) it can be used with relative ease.

One of the really great things about WCF is how much work the Microsoft developers have put into hiding the complexity of what is a very complex process.

The code pretty much writes itself, you only have to remember a couple of names in order to get it working.

The starting point when using the service reference is to create a reference to the service code that has been used, the will be called DataServiceClient.

DataServiceClient dataService = null;

 

Adding this will report an error.  Right clicking on DataServiceClient and selecting Resolve will add the correct using statement to the class (it should be  “using WindowsFormsWCF.DataService”).

Using the event driven asynchronous access pattern that is exposed via the code that has been generated during the creation of the service reference exposes the functionality of WCF.  VS2010 IntelliSense will virtually fill out the code to access the service.  So the button click code should look like this:

        private void button1_Click(object sender, EventArgs e)
        {
            dataService.GetDateTimeCompleted += new EventHandler
                    <GetDateTimeCompletedEventArgs>(dataService_GetDateTimeCompleted);
            dataService.GetDateTimeAsync();
        }


The GetDateTimeCompleted delegate simply needs an appropriate event handler to be added and IntelliSense will event generate the name of the handler “dataService_GetDateTimeCompleted” all that needs to be done is add it to the code.

        void dataService_GetDateTimeCompleted(object sender, DataService.GetDateTimeCompletedEventArgs e)
        {
            try
            {
                XElement xResult = (XElement)e.Result;

                string date = xResult.Element("Date").Value;
                string time = xResult.Element("Time").Value;

                label1.Text = string.Format("The date is: {0}\nThe time is: {1}", date, time);

            }
            catch (Exception ex)
            {
                label1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }


The only bit of work that has be hand cranked is to remember that the event handler will have the following parameters:

(object sender, DataService.GetDateTimeCompletedEventArgs e)

The e.Result will be returning an XElement, as this was defined in the original service.  I’m simply grabbing the Date and Time elements from the XML, formatting them and showing the result in the label.

image

The code for the application is:

using System;
using System.Windows.Forms;
using WindowsFormsWCF.DataService;
using System.Xml.Linq;

namespace WindowsFormsWCF
{
    public partial class Form1 : Form
    {
        DataServiceClient dataService = null;

        public Form1()
        {
            InitializeComponent();

            dataService = new DataServiceClient();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            dataService.GetDateTimeCompleted += new EventHandler<GetDateTimeCompletedEventArgs>(dataService_GetDateTimeCompleted);
            dataService.GetDateTimeAsync();
        }

        void dataService_GetDateTimeCompleted(object sender, DataService.GetDateTimeCompletedEventArgs e)
        {
            try
            {
                XElement xResult = (XElement)e.Result;

                string date = xResult.Element("Date").Value;
                string time = xResult.Element("Time").Value;

                label1.Text = string.Format("The date is: {0}\nThe time is: {1}", date, time);

            }
            catch (Exception ex)
            {
                label1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }
    }
}

 

Transforming this code to Silverlight is pretty simple.  Create a new Silverlight Application project in VS2010 (remove the checkmark for hosting in a new web site).

In the MainPage.xaml add a Button and a TextBox.  Create the Service Reference and then paste in the following code:

using System;
using System.Windows;
using System.Windows.Controls;
using SilverlightApplicationWCF.DataService;
using System.Xml.Linq;

namespace SilverlightApplicationWCF
{
    public partial class MainPage : UserControl
    {
        DataServiceClient dataService = null;

        public MainPage()
        {
            InitializeComponent();

            dataService = new DataServiceClient();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                dataService.GetDateTimeCompleted += new EventHandler<GetDateTimeCompletedEventArgs>(dataService_GetDateTimeCompleted);
                dataService.GetDateTimeAsync();
            }
            catch (Exception ex)
            {
                textBlock1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }

        void dataService_GetDateTimeCompleted(object sender, DataService.GetDateTimeCompletedEventArgs e)
        {
            try
            {
                XElement xResult = (XElement)e.Result;

                string date = xResult.Element("Date").Value;
                string time = xResult.Element("Time").Value;

                textBlock1.Text = string.Format("The date is: {0}\nThe time is: {1}", date, time);

            }
            catch (Exception ex)
            {
                textBlock1.Text = string.Format("Error occurred: {0}", ex.Message);
            }
        }
    }
}


Apart from a few name differences its the same code!

Run the application and you will get a warning about debugging issues when using web services (you can see what’s coming soon…).  Ignore the warning and continue to run the application and while the Silverlight form will display, as soon as you click the button an cross-domain error will be thrown:

image

The next post will show how this is solved quite elegantly.

Written by metadevblog

March 24, 2011 at 11:07 pm

Posted in WCF

Tagged with ,

WCF–Service Behaviours, Bindings and Contracts

leave a comment »

WCF is now a very mature technology and one that I have used a great deal over the last few years, in fact it has been core to most of my recent work both professionally and with personal development projects.

I can’t however be the only one to have struggled with getting the triumvirate of behaviours, bindings and contracts to work together when creating a web service without at least tearing some of my hair out.

I don’t know if there is something different in WCF 4.0 or its just ‘clicked’ for me finally but I managed to create two web services in a hurry last night and get them working by actually understanding the XML.  So while it is still fresh in my mind I’m going to write it down…

One of the pitfalls with using WCF is the number of different parts that need to be created before it can be tested end to end.  There are also some ‘gotchas’ that need to be avoided along the way.

Exposing data via WCF requires a data service.  I typically use XML for both sending and receiving data to/from services, so before any description of setting up WCF can be given it is first of all necessary to create a data service that will provide some information.

The data service is a pre-requisite because WCF binds to a service interface that will be exposed at the service endpoint (an example HTTP service endpoint is http://localhost:1234/DataService which will be described below).

In order to expose the service endpoint the service provider is hosted within a ServiceHost object.  ServiceHost is a core windows technology that is also a core part of IIS.  ServiceHost can be hosted within any persistent application, even a console application, but it provides excellent functionality when used with a Windows Service.

I have used Visual Studio 2010 and .NET 4.0, all the code is in c#.

The first part of this article will cover the creation of the data service provider.  The configuration of the service will follow and then I will create a simple application to access the service.

The next part of this article to follow will add another service so that Silverlight can be used to access the data service.

ConsoleApplicationWCF.sin


Create a new console application in VS2010; make sure you call it ConsoleApplicationWCF.

IDataService.cs


Add a new class called IDataService.cs and then paste this code in (you will probably get some errors reported which can be cleared by adding System.XML.Linq.dll and System.ServiceModel.dll to the projects references)

using System.ServiceModel;
using System.Xml.Linq;

namespace ConsoleApplicationWCF
{
    [ServiceContract]
    public interface IDataService
    {
        [OperationContract]
        XElement GetDateTime();
    }
}

This will create the DataService interface which will be referenced in the WCF service endpoint contract as ConsoleApplicationWCF.IDataService.  Note that it is necessary to include the namespace when identifying the endpoint in the definition.

The [ServiceContract] and [OperationsContract] are code decorations that are used during compilation to build the service interface.  The [ServiceContract] decoration identified IDataService as the contract that will be used in the service definition.  The [OperationContract] will allow the GetDataTime() method to be accessed via the service.

DataService.cs

Add a new class called DataService.cs and then paste in this code to implement the interface.  Notice that the service is returning a Linq XElement.

using System;
using System.Xml.Linq;

namespace ConsoleApplicationWCF
{
    public class DataService : IDataService
    {
        public XElement GetDateTime()
        {
            XElement result = new XElement("Data");
            result.Add(new XElement("Date", DateTime.Now.Date.ToString()));
            result.Add(new XElement("Time", DateTime.Now.TimeOfDay.ToString()));

            return result;
        }
    }
}

Program.cs

Now replace the Program.cs stub code that was generated when the project was created with this code:

using System; 
using System.ServiceModel; 
namespace ConsoleApplicationWCF 
{ 
    class Program 
    { 
        private static ServiceHost dataService;

        static void Main(string[] args) 
        { 
            try 
            { 
                dataService = new ServiceHost(typeof(DataService)); 
                dataService.Open(); 
            } 
            catch (Exception e) 
            { 
                Console.WriteLine("Unable to start DataService:" + e.ToString()); 
            }

            Console.WriteLine("Service started at http://localhost:1234/DataService"); 
            Console.ReadLine();
        } 
    } 
} 

The WCF DataService is hosted directly in the Console by ServiceHost. When implementing this within a Windows Service the code can be pretty much lifted verbatim and put into the OnStart event which will then start the WCF DataService when the Windows Service starts.

 

App.config

The key to getting WCF to work is a correct service configuration file.  Add an empty App.config file to the project and then paste the following XML into the file.  This configuration is about as simple as can be defined for a basic HTTP data service.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DataServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="DataServiceBehavior" name="ConsoleApplicationWCF.DataService">
        <endpoint address="" binding="basicHttpBinding" contract="ConsoleApplicationWCF.IDataService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1234/DataService" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>


The actual WCF definition is within the <system.serviceModel> element but it is being wrapped within a <configuration> element that is used by App.Config which the console application can access directly as part of its implementation structure.

The <system.serviceModel> element contains two main regions which allow for multiple <behaviours> and <services> to be defined.  The <behaviour> define how a <service> will function.

The service <behaviour> for a WCF data service requires that httpGet is enabled.  I also enable includeExceptionDetailInFaults on the basis that you can never have too much information about what has gone wrong and you can always switch it off later.

Creating the <service> itself is usually where it all goes wrong as there are so many parts to it; any one of which will cause an error (or worse no service).

The <service> element contains one or more <endpoint> elements and a <host> element.

<service>   
  <endpoint />   
  <host /> 
</service>

Note that a service can have multiple endpoint definitions.  This is important as the endpoints provide different data depending on their configuration.

The service behaviour uses the behaviourConfiguration to map a behaviour to the service using the behaviour attribute name.

<behavior name="DataServiceBehavior"> 
<service behaviorConfiguration="DataServiceBehavior"  …

The name of the service should be the same as the name in the interface and should include the namespace.

<service behaviorConfiguration="DataServiceBehavior" name="ConsoleApplicationWCF.DataService">

The service exposes two endpoints one is used to access the data, the other is used get the metadata that describes the service.  This second service is vital as it simplifies the whole process of making use of the service by auto-generating a while bunch of plumbing files that hide away most of the complexity of WCF.

The first <endpoint> is for the data service.  It is exposing the service as HTTP and it is using the IDataService contract that has been defined. The namespace should be used in the definition of the contract.

<endpoint address="" binding="basicHttpBinding" contract="ConsoleApplicationWCF.IDataService">

The dns specifies “localhost”.  This is perfectly valid but it should not be confused with using the name of your machine.  You should change both the dns and the actual endpoint if you want to use a machine name.

The second endpoint is for getting the service metadata that will be used to generate the WCF service support files in a client application.  The service metadata can be accessed by invoking the service endpoint in a browser along with the ?wsdl parameter (the parameter triggers access to the MEX endpoint).  Eg

http://localhost:1234/DataService?wsdl

Finally the address of the service can be defined.  I tend to use the same name as the service contract for my services and also leave them open ended – ie no terminating /

That’s about it for this posting.  The code should compile and run and will show the following console:

image

Written by metadevblog

March 23, 2011 at 9:22 am

Posted in WCF

Tagged with ,

Using Silverlight with a Windows Service – Part 1

leave a comment »

One of the big projects I have been involved in over the last couple of years had a requirement to support   tasks that could last for many hours with connections to other services that had to be kept open for the task duration.

While there are any number of possible solutions, the technology I chose to implement the application in was based around using Windows Services.  Windows Services are both very powerful but also pretty simple and while they provide a defined interface for installation and for being started and stopped they don’t have any other out of the box interfaces which can make communicating with them somewhat problematic… 

While the ability to execute very long running tasks concurrently was a primary requirement the application had to also undertake a lot of other operations and I needed an interface to expose these services.  Again there are a whole raft of different approaches that could be used but I chose WCF and specifically HTTP due to its ubiquity.

The final solution has a Windows Service based application installed on all the servers in the farm and provides a very resilient, distributed network that is scaled out simply adding additional servers running the application.   The services communicate with each other using the HTTP SOAP interface which is largely transparent to my application code as it is managed by WCF.

While most of the application is automated there is a need for administrative services that require a UI and for this I wrote a simple Windows Form application which had a WCF Service Reference to the exposed HTTP services. 

The principle UI for users was implemented using ASP.NET within SharePoint 2007.  With ASP the browser client is primarily responsible for UI and rendering while the server side code can access data and make service calls.

Requirements for a second version of the application are being put together and it is clear that the existing UI is going to need significant extensions to cover new features and while ASP.NET can handle the task I find it quite slow to author complex UI compared to Windows Forms or Silverlight.  However it has become apparent very quickly that there is an issue with Silverlight, a simple test application threw the following exception while attempting to make a service call to the Windows Service HTTP endpoint:

"An error occurred while trying to make a request to URI ‘http://server:8000/application&#8217;. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details."

This diagram shows what is being attempted.  The Silverlight XAP file is being loaded from a web server that is running on one port and then attempting to access a HTTP service running on a different port.  Note that Silverlight treats requests on different ports on the same server as being in different domains.

image

 

Silverlight has inbuilt security to prevent it making unauthorised connections to remote services which could be used by malicious programmers to gain access to secure information or launch hacking attacks.  An explanation of the security process can be found here:

http://msdn.microsoft.com/en-us/library/cc645032(VS.95).aspx

In order to support cross domain access Silverlight has to request one of two policy files that have to be present on the remote server.  Silverlight will first of all request and parse its policy file: clientaccesspolicy.xml and if this is not present or is invalid it will fall back and request the Flash policy file: crossdomain.xml if this is also not present or invalid then access to the service is denied.

For conventional web servers the provision of these policy files is relatively simple.  All that has to be done is create the correct policy file and drop it at the right location on the server (at the domain root).  Silverlight will then request and parse the file.

Another option is to avoid the cross domain issue altogether by hosting the Silverlight XAP file on the same domain as the HTTP web service:

image

The problem is that WCF on Windows Service does not have the capability to serve up this file automatically and it is necessary to write a specific service to provide it and the service has to be configured correctly so that the browser can request the silverlight xap file.

Having now completed the proof of concept it turns out that both solutions can be implemented with the same code.  I’ll post some details of this in a while…

Written by metadevblog

March 22, 2011 at 4:48 pm

Posted in Silverlight

Tagged with ,

WP7 Portrait to Landscape Reorientation

leave a comment »

I thought I was pretty close to being done with the first version of Password Vault but have now discovered the joys of having to cope with reorienting the display from my chosen default Portrait view (where it looks good) to Landscape (which is not enabled by default) means that the display remains in Portrait mode when the device is rotated. The default setting in the XAML when the new page is Portrait is:

SupportedOrientations="Portrait" Orientation="Portrait"

Simply changing this to the following will at least allow the reorientation to occur and the screen will re-render when the orientation of the device changes:

SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"

However the display looks terrible! All my careful positioning of content to make it look good in Portrait mode results in a jumble of missing content and ugly layout in Landscape.

It is worth noticing that the there is an option to change the orientation when you right click on the page layout.  It simply flips the Orientation setting above.

It takes quite a bit of additional effort to rework the page so that it can handle the reorientation however in some respects this additional effort is quite rewarding as it can simplify the page.

I have chosen to implement a compromise solution which is to optimise the page layout for Portrait mode and then rework it to make a ‘best fit’ for Landscape.  I also wanted to avoid using code to manipulate XAML during the orient event.

There are two controls that can be used to help with the layout, StackPanel and WrapPanel (the WrapPanel is part of the Windows.Phone.Contols.Toolkit.dll that has to be referenced in the project and also included as a namespace in the XMAL.

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

The StackPanel has an Orientation property which will stack content controls either horizontally or vertically.

The WrapPanel is quite useful as the page can be designed so that it will work well in both orientations without having to resort to code to change the layout.

In this example a WrapPanel is used so that the prompt text and data entry look lay out nicely in both orientations.  I achieved this by setting the Width of the text box in the Portrait mode to the fixed size of the grid. 

image

When the orientation is changed to Landscape there is enough room to put the text box next to the prompt.

image

<toolkit:WrapPanel x:Name="StackPanelPassword" >
    <TextBlock Height="30" Name="textBlock2" Margin="12,0,0,0" Text="enter password:" Width="180" HorizontalAlignment="Left" />
    <TextBox Height="72" Name="textBoxPassword" Text="" Width="444" />
</toolkit:WrapPanel>

It has taken a few hours to sort out the layout for all the pages so that they can handle both orientations dynamically.  In doing so I have learnt quite a bit about the XAML and also tidied up the layout so that it is far less reliant on absolute positioning and now makes much greater use of the inherent capabilities of the layout containers.

See also:

http://blogs.msdn.com/b/ptorr/archive/2010/03/27/strategies-for-dealing-with-orientation-changes.aspx

http://stackoverflow.com/questions/2226841/resolution-issue-in-silverlight/2237110#2237110

Written by metadevblog

March 16, 2011 at 11:30 pm

Posted in WP7

Tagged with

WP7 Emulator

leave a comment »

I’ve been using the WP7 Emulator for a while now and its pretty useful.  It starts up quickly and on my dev machine is pretty responsive.  The Emulator is pretty limited in its functionality though – I wanted to test a send email code fragment but was unable to do so as it is not possible to set up email accounts.

A couple of useful bits of information though:

1. Press PgUp to use the dev machine keyboard for input – it’s much quicker and far more convenient than pecking around on the Emulator keyboard.

2. Download an unlocked copy of the Emulator for a richer UX.  However it takes around 2x to boot as the standard Emulator and still does not allow me to set up an email account; it does however let you test an application within the wider phone UI context.

I have both the official Emulator and the unlocked Emulator in the same directory.  I can then simply rename the one I want to use and its kicked off when I run my application in VS2010.

C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Emulation\Images

There are various blogs that explain how to unlock the Emulator but if all your after is the binary then I downloaded mine from here:

http://www.multiupload.com/TGBSSOXCO7

I was a bit dubious about the site to start with but it works (I don’t have an account and just took the time hit on it downloading slowly).

Written by metadevblog

March 15, 2011 at 8:36 am

Posted in WP7

Tagged with ,