开发者

How can I host a Silverlight 4 application in a WPF 4 application?

I have a Silverlight 4 application that I want to double as a Kiosk application. It has to be full screen, touch-enabled, and most i开发者_运维技巧mportantly have full trust. I was thinking of doing this using a WPF application to host the Silverlight XAP. I am aware of the WPF/XNA SilverlightViewport, but it seems that this solution doesn't provide communication between WPF and Silverlight.

This would really save me a whole lot of time. I would not have to maintain two distinct applications that do the exact same thing, and I wouldn't have to deploy to hundreds of kiosks every-time I make a change.


I know it's an old question, but I do have a solution.

I do this exact scenario. I host a Silverlight app in a WPF host. I use self hosted WCF to deliver duplex net tcp services. To do this, you have to also have a HTTP hosted clientaccesspolicy.xml, which I also self host from the same service.

I hope I can explain enough to get the ball rolling. I'll include some samples where I can, however due to the project I can't share everything.

Firstly, some considerations: 1) net tcp is not secure between Silverlight and WPF as Silverlight (as of version 4 - just testing version 5) cannot do the secure connections, so you have to roll your own encryption if you are to use this.

2) There are two methods to doing duplex WCF, one is with HTTP. The HTTP method 'polls' the service to see if any commands are coming from the host for the client. This inherently makes it much slower than net tcp. The other is with net tcp (as stated earlier). Only http method supports encryption out of the box.

3) When you host the Silverlight app in the WPF host, when you start to debug, you will not be able to debug your Silverlight app, as the debugger will not connect to the (as it sees it) external Silverlight application, only the WPF. To get around this, you have to 'activate' the attachment to the Silverlight application when you start the WPF host.

Now for the code:

Remote Service Interface:

using System.ServiceModel;

namespace RemoteService
{
    [ServiceContract(CallbackContract = typeof(IRemoteCallbackService))]
    public interface IRemoteService
    {
        [OperationContract]
        void TestCallback();

        // TODO: Add your service operations here
    }

    [ServiceContract]
    public interface IRemoteCallbackService
    {
        [OperationContract(IsOneWay = true)]
        void OnTestCallbackComplete();

        // TODO: Add your service operations here
    }
}

Your policy listener interface:

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

namespace RemoteService
{
    [ServiceContract]
    public interface IPolicyRetriever
    {
        [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
        Stream GetSilverlightPolicy();

        [OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]
        Stream GetFlashPolicy();
    }
}

The actual service implementation:

using System.Text;

namespace RemoteService
{
    public class RemoteService : IRemoteService, IPolicyRetriever
    {
        IRemoteCallbackService client = null;

        public void TestCallback()
        {
            client = OperationContext.Current.GetCallbackChannel<IRemoteCallbackService>();
            client.OnTestCallbackComplete();
        }

        #region Cross Domain Policy Implementation

        private Stream StringToStream(string result)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
            return new MemoryStream(Encoding.UTF8.GetBytes(result));
        }

        //<grant-to>
        //<resource path="/" include-subpaths="true"/>
        //<socket-resource port="4502-4534" protocol="tcp" />
        //</grant-to>

        Stream IPolicyRetriever.GetSilverlightPolicy()
        {
            string result = @"<?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""/><socket-resource port=""4502-4534"" protocol=""tcp"" /></grant-to></policy></cross-domain-access></access-policy>";
            return StringToStream(result);
        }

        Stream IPolicyRetriever.GetFlashPolicy()
        {
            string result = @"<?xml version=""1.0""?><!DOCTYPE cross-domain-policy SYSTEM ""http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd""><cross-domain-policy><allow-access-from domain=""*"" /></cross-domain-policy>";
            return StringToStream(result);
        }

        #endregion Cross Domain Policy Implementation
    }
}

The above should be in separate classes in one dll project, and then referenced by your WPF host.

And to host it in a console app (you need to convert this to WPF):

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using LocalService;
using RemoteService;

namespace Host
{
    internal class Program
    {
        /// <summary>
        /// Main host entry point, this starts our listeners
        /// </summary>
        /// <param name="args">The args.</param>
        private static void Main(string[] args)
        {
            // Start our listeners
            StartListeners(80, 4504, true);
        }

        /// <summary>
        /// Starts the listeners.
        /// </summary>
        private static void StartListeners(int HttpPort = 80, int NetTcpPort = 4504, bool StartRemoteService = true)
        {
            Console.WriteLine("Starting Policy Listener");

            string policyaddress = "http://" + Environment.MachineName + ":" + HttpPort.ToString();
            string locallistener = "net.tcp://" + Environment.MachineName + ":" + NetTcpPort.ToString();

            // Start our policy listener and (if required) our remote http service:
            using (System.ServiceModel.ServiceHost policyandremoteservicehost = new System.ServiceModel.ServiceHost(typeof(RemoteService.RemoteService), new Uri(policyaddress)))
            {
                policyandremoteservicehost.AddServiceEndpoint(typeof(IPolicyRetriever), new System.ServiceModel.WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());

                // if we are to start our remote service here too, then add that endpoint in:
                if (StartRemoteService)
                {
                    policyandremoteservicehost.AddServiceEndpoint(typeof(IRemoteService), new System.ServiceModel.PollingDuplexHttpBinding(), "RemoteService");
                }

                ServiceMetadataBehavior psmb = new ServiceMetadataBehavior();
                psmb.HttpGetEnabled = true;
                psmb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
                policyandremoteservicehost.Description.Behaviors.Add(psmb);
                policyandremoteservicehost.Open();


                Console.WriteLine("WCF Host Running...");
                Console.WriteLine("Press <enter> to shutdown");
                Console.ReadLine();
                Console.WriteLine("Closing connections, please wait");
                policyandremoteservicehost.Close();
                Console.WriteLine("Closed policy and remote services");
            }
        }
    }
}

Which should give you something to work on. The startremoteservice bool can be used to just make it a policy file listener, which then will allow you to connect to a remote service without that remote service having to host a policy file.

Remember, that Silverlight can only connect to HTTP/HTTPS ports, and TCP ports within the range of 4502-4534. The included clientaccesspolicy.xml is quite unrestricted.

I hope this is of use to someone :) .

Feel free to ask me questions if you want, I'll keep a look out.

A couple of points of note: You can serve your hosted xap from this solution, and therefore do not require IIS. You can also run your Silverlight xap out of browser and still use these services.

The debugging answer is here: Debugging Silverlight hosted in WPF


Your best bet would be to take a three tiered architecture approach by utilizing WCF/RIA Services and handling all of your business logic on the server side.

By keeping your client thin and focused mostly on Views and View logic, then swapping out different clients should be as easy as swapping out any other component.

On that note if you utilize Silverlight for your client then you can run it from the web OR as a standalone application, meaning that their are few reasons you would want to use WPF as well.


I have exactly the same requirements. My solution uses WPF to host Silverlight inside the web browser control. The communication between them is setup using WCF Services that are ALSO hosted by WPF in one nice self-contained package. You can setup ClickOnce for WPF components to streamline updates. I leverage WPF to extend Silverlight on the client machine (without a need for elevated trust) by exposing WCF Services for SL to consume (ex. GetComputerName, ...). The only issue is that you are using WCF RIA Services which cannot be hosted outside of IIS. Hope it helps

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜