In my previous post, I showed how to host WCF services. In the kiosk application that I am working on, I have, as previously mentioned, decided to not have IIS run locally on the machine. I have also decided host the Silverlight application locally. I do not want it to be fetched from the server for different reasons. So how can I serve up a Silverlight application without a webserver? Well…I am already hosting WCF services in a windows application…why not let the windows application serve up the Silverlight application as well?
WCF actually gives us this possibility very easily, and here is how it is done. I’m going to re-use the application from the previous post, so if you haven’t read it, I recommend doing so…
I start off by adding my XAP-file as well as the TestPage.html file to the console project by using the “Add Existing Item” dialog. The TestPage.html will be available in the build location of the Silverlight project. It is the file created when debugging the project. Next, I set the Build Action to embedded Resource.
Now that both of the files are embedded in the application, it is time to actually serve them up. I have decided to do it using the SilverlightService that is already available. So I change the ISilverlightService interface by adding 2 new methods. One called GetHtmlPage() and one called GetXapFile(). I also set OperationContract and WebGet attributes in the methods
[ServiceContract]
public interface ISilverlightService
{
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetClientAccessPolicy();
[OperationContract, WebGet(UriTemplate = "/index.htm")]
Stream GetHtmlPage();
[OperationContract, WebGet(UriTemplate = "/DarksideCookie.WCFHosting.SilverlightClient.xap")]
Stream GetXapFile();
}
The UriTemplate’s used by the WebGet decides what Url’s to respond to. So the one for the GetHtmlPage() method can be anything, but i use index.htm as it is standard. But the one used for the Xap-file, needs to correspond to what is in the Html code. In this case, the Silverlight object tag looks like this
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="DarksideCookie.WCFHosting.SilverlightClient.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
So, the Xap file will be requested at the same location as the Html file, thus making the UriTemplate look as above.
Implementing the interface is simple. It is more or less identical to the GetClientAccessPolicy() method, except for the name of the resource streams…
public class SilverlightService : ISilverlightService
{
public System.IO.Stream GetClientAccessPolicy()
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DarksideCookie.WCFHosting.ServiceConsole.ClientAccessPolicy.xml");
}
public System.IO.Stream GetHtmlPage()
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DarksideCookie.WCFHosting.ServiceConsole.TestPage.html");
}
public System.IO.Stream GetXapFile()
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DarksideCookie.WCFHosting.ServiceConsole.DarksideCookie.WCFHosting.SilverlightClient.xap");
}
}
That’s it. If you run the console app and then browse to http://localhost:8731/index.htm, the browser will be served up an Html page and a Silverlight control nicely without the need for a webserver. Or rather…I guess I have just created a mini-webserver using WCF.
In the Ticket Direct kiosk application, I am actually taking the whole thing a step further. In that application I am hosting the Silverlight application just like this, but I skip the web browser part by hosting that in the windows application as well. So it is becomes a all-in-one application. A simplified version could look like this… Remember this is a VERY simplified version with a lot of things that should be done differently, but it shows the idea…
Add a new Windows Forms project to the solution. And to save time, just add a reference to the Consol project. The console will not actually be used, but it exposes all of the service classes needed. The correct way would be to recreate the services in the windows project, but that would take to long… Or even better, the services should have been defined in its own project…but they aren’t…
Start off by adding a reference to System.ServiceModel. Then, in the Program.cs file, add a method called StartServices(). It should be static and take no arguments and return void. In the method, create 2 new ServiceHost objects to host the SilverlightService and the TimeService. Next, at the start of the Main() method, spawn a new thread using the StartServices() method as the thread start and start it. At the end of the method, kill it. The implementation should look something like this
static class Program
{
[STAThread]
static void Main()
{
Thread t = new Thread(new ThreadStart(StartServices));
t.Start();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
t.Abort();
}
private static void StartServices()
{
ServiceHost agHost = new ServiceHost(typeof(SilverlightService));
ServiceHost host = new ServiceHost(typeof(TimeService));
agHost.Open();
host.Open();
}
}
That should be enough to host the services.
Next, we need to add a web browser control to the window. Just drag a new web browser onto the form and you are done. In the forms Load event, use the web browsers Navigate() method to get it to browse to the correct place, in this case http://localhost:8731/index.htm.
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("http://localhost:8731/index.htm");
}
Finally, we need to migrate the app.config file form the console project. Or rather…just copy it since there isn’t an app.config file in the Windows Forms project.
When that is done, the application is done. If you run the application, you will see a Silverlight application inside the browser window. It can be sent around as any other application…well…it needs administrative privileges to open the service ports…but except for that…
In the kiosk case, the window that is opened, opens in full screen mode, taking over the entire screen. And all that is needed is that one executable file. Well…I need a bit more in form of external assemblies and so…but it is true XCopy deployment. All services are embedded in the application….
Hope you have use for the idea…
Source: Windows Hosted Silverlight.zip (170.09 kb)
Cheers!