• WPF + SelfHost 实现窗体自宿主(API,API和窗体通信)


    前言

    今天研究了在 WPF 中使用 SelfHost 自宿主。 具体的功能是,在 WPF 中使用自宿主服务,外部调用服务的 API,在 API 里面操作窗体的显示等。

    技术点

    1. 在 WPF 中集成 SelfHost
    2. API 和窗体间交互

    一、集成 SelfHost

    现在已有的资料中,使用 SelfHost 做自宿主服务的基本都是用控制台实现 WebAPI 的功能,或者在 WinFrom 中集成。WFP 和这些还是有挺大的区别。 我这里是参考了这个文章:Self-Host 
    具体的步骤如下: 
    1. 添加引用包 
    在 NuGet 中添加 “Microsoft.AspNet.WebApi.SelfHost”和“Microsoft.AspNet.WebApi.Cors”库,其他依赖的类库基本都会添加进来 
    2. 初始化服务 
    新建类 “InitConfig”在类里添加初始化自宿主的一些设置。 
    具体代码如下: 

            public static HttpSelfHostConfiguration InitSelfHostConfig(string baseAddress)
            {
                // 配置 http 服务的路由
                HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(baseAddress);
                config.Routes.MapHttpRoute(
                    "API Default", "api/{controller}/{id}",
                    new { id = RouteParameter.Optional }
                );
    
                // 设置跨域
                var cors = new EnableCorsAttribute("*", "*", "*"); //跨域允许设置
                config.EnableCors(cors);
    
                config.Formatters
                   .XmlFormatter.SupportedMediaTypes.Clear();
    
                //默认返回 json
                config.Formatters
                    .JsonFormatter.MediaTypeMappings.Add(
                    new QueryStringMapping("datatype", "json", "application/json"));
    
                //返回格式选择
                config.Formatters
                    .XmlFormatter.MediaTypeMappings.Add(
                    new QueryStringMapping("datatype", "xml", "application/xml"));
    
                //json 序列化设置
                config.Formatters
                    .JsonFormatter.SerializerSettings = new
                    Newtonsoft.Json.JsonSerializerSettings()
                    {
                        //NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
                        DateFormatString = "yyyy-MM-dd HH:mm:ss" //设置时间日期格式化
                    };
                return config;
            }

      这里返回的会在新建服务的时候调用

    3. 添加 Program 类

    WPF 默认是没有这个类的,需要自己手动添加这样的一个类。

    这个类的代码:

            /// <summary>
            /// 应用程序的主入口点。
            /// </summary>
            [STAThread]
            static void Main(string[] args)
            {
                string baseAddress = string.Format("http://{0}:{1}/",
                       System.Configuration.ConfigurationManager.AppSettings.Get("Domain"),
                       System.Configuration.ConfigurationManager.AppSettings.Get("APIPort"));
                using (var server = new HttpSelfHostServer(InitConfig.InitSelfHostConfig(baseAddress)))
                {
                    server.OpenAsync().Wait();
                    Console.WriteLine(String.Format("host 已启动:{0}", baseAddress));
                    App app = new App();
                    app.Run();
                }
            }
    

      其中 baseAddress 是拼接的服务地址,同时在“属性”里设置项目的启动对象为“Program”。

    后面添加一些 API 进行测试。

        public class HomeController : ApiController
        {
            [HttpGet]
            public object Get()
            {
                return new { code = 1, msg = "OK HomeController" };
            }
        }
    

      运行项目在浏览器输入对应的地址即可。

    二、API 和窗体交互

    由于 API 是另外定义的类。不能直接操作窗体,新建窗体实例、传递窗体都有问题。

    这里用的是“事件跨类调用”,参考了“C#通过事件跨类调用WPF主窗口中的控件”。

    新建一个类继承“EventArgs”:

        public class MessageArgs : EventArgs
        {
            public MessageArgs(string comMessage)
            {
                this.ComMessage = comMessage;
            }
    
            public string ComMessage { get; set; }
    
        }
    

      在 ApiController 中定义事件、触发事件:

        public class PlayController : ApiController
        {
    
            public static event EventHandler<MessageArgs> PartEvent; //定义在 PlayController 中的一个事件,参数是MessageArgs对象
    
    
            public static void InFunction(string comMessage)
            {
                var messageArg = new MessageArgs(comMessage);
                PartEvent?.Invoke(null, messageArg);  //触发事件,执行所有注册过的函数
            }
    
            [HttpGet]
            public object Play(string comd)
            {
                InFunction(comd);
                return new { code = 0, msg = "成功播放!" };
            }
        }
    

      在窗体中注册上面的事件,以及里面的具体逻辑:

            public MainWindow()
            {
                InitializeComponent();
    
                PlayController.PartEvent += OnStep;  //将该类中的函数注册到Monitor静态类的PartEvent事件中。
            }
    
            /// <summary>
            /// 订阅 Monitor 的PartEvent事件,当触发PartEvent事件时(可能并不在类MainWindow对象中),被注册的函数就行做出相应的响应。
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="message"></param>
            public void OnStep(Object sender, MessageArgs message)
            {
                Application.Current.Dispatcher.Invoke(new Action(() =>
                    {
                        DoCommand(message.ComMessage);
                    }));
            }
    

      这样,API 接可以和窗体进行通信。

    总结

    到这里,整个的功能都已经完成。

    在自己预研的时候,也是一步一步的。从集成,一点点的改进。

    到API和窗体通信,也查了看了一些方法。最终选的这些思路。

  • 相关阅读:
    print 参数
    note
    action标签的属性说明
    Cannot load JDBC driver class 'oracle.jdbc.driver.OracleDriver'
    润乾报表
    javax.naming.NameNotFoundException: Name ZKING is not bound in this Context 的问题
    Myeclipse2013安装svn插件
    Myeclipse2013的优化设置
    Myeclipse解析.classpath文件
    Struts
  • 原文地址:https://www.cnblogs.com/zhurong/p/10672128.html
Copyright © 2020-2023  润新知