WPF + SelfHost 实现窗体自宿主(API,API和窗体通信)
前言
今天研究了在 WPF 中使用 SelfHost 自宿主。 具体的功能是,在 WPF 中使用自宿主服务,外部调用服务的 API,在 API 里面操作窗体的显示等。
技术点
- 在 WPF 中集成 SelfHost
- 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 默认是没有这个类的,需要自己手动添加这样的一个类。
这个类的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /// <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 进行测试。
1 2 3 4 5 6 7 8 | public class HomeController : ApiController { [HttpGet] public object Get() { return new { code = 1, msg = "OK HomeController" }; } } |
运行项目在浏览器输入对应的地址即可。
二、API 和窗体交互
由于 API 是另外定义的类。不能直接操作窗体,新建窗体实例、传递窗体都有问题。
这里用的是“事件跨类调用”,参考了“C#通过事件跨类调用WPF主窗口中的控件”。
新建一个类继承“EventArgs”:
1 2 3 4 5 6 7 8 9 10 | public class MessageArgs : EventArgs { public MessageArgs( string comMessage) { this .ComMessage = comMessage; } public string ComMessage { get ; set ; } } |
在 ApiController 中定义事件、触发事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 = "成功播放!" }; } } |
在窗体中注册上面的事件,以及里面的具体逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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和窗体通信,也查了看了一些方法。最终选的这些思路。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?