Archive for the ‘Silverlight’ Category
Silverlight Metro Theme
The WP7 currently has a tiny market share so writing apps to target both the WP7 and the desktop using the same code base makes a great deal of sense to me. Especially for tools such as Master Key which has as much validity (if not more) on the desktop as it does on my phone.
I picked up a copy of the Metro Theme from a project on Codeplex and copied one of the Master Key XMLA pages over. It worked pretty well but in the end I’m left in the position of having to do a great deal of work to make the page fit into a browser window rather than the phone.
http://metrotheme.codeplex.com/
There are a number of issues. Firstly the fonts and lines used to render on the WP7 screen look huge when viewed in normal Windows resolution; secondly the theme is incomplete and has missing features. While all this can be fixed by modifying the theme it’s a lot of work.
Secondly, and obviously, the browser environment is significantly different to that of the WP7. The hardware back and close buttons have to be provided in Silverlight and handled in the browser. From a browser UX point of view these buttons would typically be at the top of the screen. Handling ‘back’ is less of a problem as the browser supports this. However the browser also provides ‘forwards’ as well and the user can also pick an item from the navigation history and jump directly there, neither of which are possible in WP7.
Having experimented with directly porting the WP7 app and finding it a less than compelling outcome I searched around for the Silverlight theme to use. After some experimentation I picked up a Window 7 theme that the Silverlight dev team released (see Tim Heuer’s blog http://timheuer.com/blog) and have used this as the basis for the Windows version.
The project template also provides a navigation framework which is really useful and this integrates into the browser navigation history providing back/forward support.
It was pretty simple to take a WP7 page and convert it to Win7, for the most part the XAML simply pasted directly and only needed to be lightly manipulated to fit. Over the space of a couple of days I had pretty much replicated the App, however…
However, it was apparent pretty quickly that the integration into the browser navigation was not going to work, the history just kept getting bigger and worse I could jump forward when it was illogical to do so. I spent a lot of time trying to make it work but in the end it was just too confusing from a UX point of view. I switched it off and suddenly the navigation became a whole lot more logical as the framework is tabbed
The ApplicationBar in WP7 is a great little control and works really well on the phone. It’s location at the bottom of the screen near to the where your fingers lay makes for a great UX.
I considered briefly writing a version of this for Silverlight but decided against it as the more normal windows layout is better and the Ribbon interface is clean and well accepted.
In the end I decided that the simplest approach was to revert from having ApplicationBar based button back to a more traditional approach with the buttons embedded into the UI. This is perfectly valid when there are so few buttons required to make the App work.
It was apparent as soon as I started to think about a Silverlight version of Master Key that combining the item selection and its content into a single page would be the best approach; so for WP7 the user is shown the Vault Entries as a list:
And when an item in the list is selected is is displayed on a separate screen:
This works really well – the UI is easy to use and quite clear in its intent.
Combining the two screens and modifying the UI slightly gives this screen:
Master Key on WP7 relies on the phone timeout kicking in to prevent unauthorised access, on my phone it’s set to a minute after which it will tombstone and the user has to re-enter the password to regain access to the information when the app is reactivated.
In the browser environment this has to be done within the page by running a timer that will prevent access if there is no activity. I’ve added a progress bar at the bottom of the screen – after 30 seconds of inactivity the page will close and revert to the login screen. Job done!
The core functionality of Master Key for WP7 has moved without change to the Silverlight version. I’ve not had to touch a line of code in the data access layer to make it work and this includes the WCF service code (though to be fair I added the service reference to the new project and it auto-generated the appropriate code).
You don’t need to be too eagle eyed to see that the two vaults are the same – I simply loaded the demo vault into both versions of the App from the web service.
So the end result is that I now have a WP7 and Windows version of Master Key and the vault files are interchangeable between the two. Also I now have a good idea as to how much additional effort has to be put in to convert between the two platforms. I have a bit of work still outstanding to be able to launch the desktop version from the MasterKey service but I don’t anticipate this taking a lot of time.
One thing – I am pretty frustrated with myself for using the Windows 7 theme rather than Cosmopolitan as this is the closest the the WP7 Metro theme. I was hoping I could simply swap themes but this failed on my first attempt to replace the Windows 7 theme. I’ll have to spend some time working this out when I have a chance.
Starting an WP7 app with alternate start page
I have had to change the method for starting Master Key in order to fit in with the way that the WP7 page navigation works. On the face of it the issue is pretty trivial and one that users would typically only hit once or twice during their use of the App.
WP7 uses a browser based metaphor for moving between pages in an application. So you can move from page to page and the history is retained on a stack, pressing the back button returns to the previous page. This can seem confusing, Page1 can invoke Page2 and this can in turn invoke Page1. However it will NOT be the original Page1 but a new instance of it. Pressing back on this last page will will return to Page2 and pressing back again will go to the first instance of Page1 (pressing back one more time will close the App).
Master Key requires the user to enter the master key password in order to access the secured information. However when the user first starts Master Key the user has to enter the password that will be used on subsequent visits and I have a different UI for this.
I originally wrote the code so that the MainPage.xaml checked to see if the users vault (the list of passwords) already exists and if it does not then the page re-directs in the OnNavigatedTo event in MainPage.xaml to a password entry page called EnterPassword.xaml where the user enters the master key.
If the user enters a valid master key and presses ‘ok’ then the vault is set up and the app is ready to use.
However if the user hits the ‘back’ button before entering a valid password then the App is left in a state of limbo and will hit the same OnNavigatedTo event in the MainPage.xaml which will re-invoke the EnterPassword.xaml page again.
I could write code to trap this cycle but wanted to look at the alternatives; one of which is to have different start pages. I.e. when the vault exists then the MainPage.xaml will be the start page, and if the vault does not exist then EnterPassword.xaml should be the start page.
This is best achieved using Uri remapping by adding the code to the App.xaml.cs Application_Launching event:
private void Application_Launching(object sender, LaunchingEventArgs e)
{
DAL.LoadVault();
UriMapper mapper = Resources["uriMapper"] as UriMapper;
RootFrame.UriMapper = mapper;
// Update the mapper as appropriate
if (DAL.VaultExists)
mapper.UriMappings[0].MappedUri =
new Uri("/MainPage.xaml", UriKind.Relative);
else
mapper.UriMappings[0].MappedUri =
new Uri("/EnterPassword.xaml?type=newvault", UriKind.Relative);
}
The App.xaml has to be modified to have a default UriMapper added to it (note the addition of the xmlns:navigation namespace).
<Application
x:Class="WPVault.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:navigation="clr-namespace:System.Windows.Navigation;assembly=Microsoft.Phone"
>
<!–Application Resources–>
<Application.Resources>
<navigation:UriMapper x:Name="uriMapper">
<navigation:UriMapping Uri="/MainPage.xaml" />
</navigation:UriMapper>
</Application.Resources>
It works and I can see uses for it in other Apps; however it comes at a cost that ruled it out for me as the back button behaviour is to return to the page that was navigated from. This means that once the user has entered the master key password and started to make entries then hitting the back button will return to the EnterPassword.xaml page again which is not a great UX.
I ended up combining the Open and New Vault pages into MainPage.xaml as there is a lot of overlap in their functionality – I then simply drive changes into the UI in the OnNavigatedTo event as appropriate to what the user is doing. Thus the ‘new vault’ info is shown when the vault does not exist and then when the user returns back to this page again and the vault does exist the ‘open vault’ page is shown.
Build Version numbers
A quick note on Visual Studio Build Version numbers as there is some quite confusing notes around and it’s taken me a few hours to get this process working end to end.
Version numbers are vital when building software that is to be released to the outside world and automating at least part of the process ensures that you can pretty much keep track of what is out there.
The default process provides a build number like this: 1.3.4138.18913.
This number represents the following information: Major.Minor.Build.Revision
So the build is version 1.3 and the build number is 4138 and the revision number is 18913.
The build information is maintained in AssemblyInfo.cs
// 1.0 – Password Vault
// 1.1 – first version released to MSFT
// 1.2 – renamed to Master Key
// 1.3 – added remote save/load and refactor UX
[assembly: AssemblyVersion("1.3.*")]
The auto generation of the build/revision only occurs when you remove either AssemblyVersion or AssemblyFileVersion; I have only worked with the former.
I have to set the major/minor build numbers and I include a comment to track the changes. I only change these when there is a big change to the app and will also set a minor version prior to each release.
I use VS TS Source Control and as part of the development process I label the each change to the Minor build when I have checked the updates in. This allows me to quickly pull a version from source control when I have an issue reported to me.
Each time I build the solution the Build and Revision numbers will change. The Build is actually the number of days since 31/12/1999 and so only changes when the date changes. The Revision is the number of seconds since midnight/2.
Getting this info into an About Box where it’s visible to the user is the next task. There are a bunch of ways to do this but the simplest is the following:
System.Reflection.Assembly execAssembly = System.Reflection.Assembly.GetExecutingAssembly(); string[] version = execAssembly.FullName.Split(','); textBlockVersion.Text = version[1];
The output looks like this:
Using Silverlight with a Windows Service – Part 2
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!
Using Silverlight with a Windows Service – Part 1
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’. 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.
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:
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…