【问题标题】:How can I host a Silverlight 4 application in a WPF 4 application?如何在 WPF 4 应用程序中托管 Silverlight 4 应用程序?
【发布时间】:2011-06-13 14:23:33
【问题描述】:

我有一个 Silverlight 4 应用程序,我想兼作 Kiosk 应用程序。它必须是全屏的、支持触控的,最重要的是完全信任。我正在考虑使用 WPF 应用程序来托管 Silverlight XAP。我知道WPF/XNA SilverlightViewport,但似乎此解决方案不提供 WPF 和 Silverlight 之间的通信。

这真的会为我节省很多时间。我不必维护两个执行完全相同操作的不同应用程序,也不必每次进行更改时都部署到数百个信息亭。

【问题讨论】:

    标签: wpf silverlight


    【解决方案1】:

    我知道这是一个老问题,但我确实有解决方案。

    我做了这个确切的场景。我在 WPF 主机中托管 Silverlight 应用程序。我使用自托管 WCF 来提供双工网络 tcp 服务。为此,您还必须有一个 HTTP 托管的 clientaccesspolicy.xml,我也从同一服务自行托管。

    我希望我能解释得足以让事情顺利进行。我会尽可能包含一些示例,但是由于项目的原因,我无法分享所有内容。

    首先,一些注意事项: 1) Silverlight 和 WPF 之间的 net tcp 不安全,因为 Silverlight(从第 4 版开始 - 仅测试第 5 版)无法进行安全连接,因此如果要使用它,您必须自己进行加密。

    2) 做双工WCF有两种方法,一种是使用HTTP。 HTTP 方法“轮询”服务以查看是否有任何命令来自客户端的主机。这本质上使它比 net tcp 慢得多。另一种是使用 net tcp(如前所述)。只有 http 方法支持开箱即用的加密。

    3) 当您在 WPF 主机中托管 Silverlight 应用程序时,当您开始调试时,您将无法调试您的 Silverlight 应用程序,因为调试器不会连接到(如它所见)外部 Silverlight 应用程序,只有 WPF。要解决此问题,您必须在启动 WPF 主机时“激活”到 Silverlight 应用程序的附件。

    现在是代码:

    远程服务接口:

    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
        }
    }
    

    您的策略侦听器接口:

    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();
        }
    }
    

    实际的服务实现:

    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
        }
    }
    

    以上内容应位于一个 dll 项目中的不同类中,然后由您的 WPF 主机引用。

    并将其托管在控制台应用程序中(您需要将其转换为 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");
                }
            }
        }
    }
    

    这应该会给你一些工作。 startremoteservice bool 可用于将其设置为策略文件侦听器,这样您就可以连接到远程服务,而无需远程服务托管策略文件。

    请记住,Silverlight 只能连接到 HTTP/HTTPS 端口,以及 4502-4534 范围内的 TCP 端口。包含的 clientaccesspolicy.xml 是完全不受限制的。

    我希望这对某人有用:)。

    如果你愿意,请随时问我问题,我会留意的。

    注意几点:您可以通过此解决方案为您托管的 xap 提供服务,因此不需要 IIS。您可以在浏览器之外运行 Silverlight xap 并仍然使用这些服务。

    调试答案在这里:Debugging Silverlight hosted in WPF

    【讨论】:

      【解决方案2】:

      最好的办法是采用三层架构方法,利用 WCF/RIA 服务并在服务器端处理所有业务逻辑。

      通过保持您的客户端精简并主要关注视图和视图逻辑,然后换出不同的客户端应该像换出任何其他组件一样容易。

      关于这一点,如果您将 Silverlight 用于您的客户端,那么您可以从 Web 或作为独立应用程序运行它,这意味着它们也是您想要使用 WPF 的几个原因。

      【讨论】:

      • @maple,我的业务逻辑被抽象出来了。我正在使用 WCF/RIA 服务。我的表示层是我想要重用的。它相当复杂(因为这是最终用户,而不是内部业务应用程序)。
      • 很好,但您还没有解释为什么必须同时使用 WPF 和 Silverlight。主要问题是您将有数百个不同的 Kiosk 应用程序需要在进行更改时进行部署,Silverlight 会为您解决该问题。一个带有一些小 javascripts 的标准网页就足以承载 XAP 文件,并且这个 XAP 文件将始终是最新的,基于部署到 IIS 的内容。 (续...)
      • Silverlight 无法扩展的地方,您可以使用 Prism 来填补空白。 msdn.microsoft.com/en-us/magazine/dd943055.aspx。该框架有助于弥补 Silverlight 技术的不足。
      • @maple,我需要 WPF,因为我必须对信息亭具有完全信任权限,并且因为我需要将用户锁定在信息亭的操作系统之外(即全屏)。你给了我架构解决方案,我已经考虑过了,我正在寻找方法。我不会重写我的应用程序来解决这个问题。
      • 如果 WPF 和 Silverlight 应用程序之间存在某种通信机制,那么我不知道。使用 WCF/RIA 服务构建一个允许两者相互通信的通信接口应该不会太牵强。您提出的基本问题是 WPF 应用程序在 .NET 运行时环境下运行,而 XAP 插件在完全不同的 Silverlight 运行时环境下运行。 (续...)
      【解决方案3】:

      我有完全相同的要求。我的解决方案使用 WPF 在 Web 浏览器控件中托管 Silverlight。它们之间的通信是使用 WCF 服务设置的,这些服务也由 WPF 托管在一个漂亮的自包含包中。您可以为 WPF 组件设置 ClickOnce 以简化更新。我利用 WPF 通过公开 WCF 服务以供 SL 使用(例如 GetComputerName,...),在客户端计算机上扩展 Silverlight(无需提升信任)。唯一的问题是您使用的 WCF RIA 服务无法在 IIS 之外托管。希望对你有帮助

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多