在上一篇中介绍了一下razor文件中,js与c#之间的相互调用,但WinForm和Blazor混合中,没有真正与WinForm进行交互,本篇来说明一下。
WinForm中混合Blazor是通过ServiceCollection来完成的,如果想WinForm和Blazor交互,可以通过向ServiceCollection注入一个中介服务来达到互相调用,大体思路是定义一个服务,这个服务里有方法和事件,方法被调用,触发订阅者。比如调用方是WinForm的话,订阅者就是对应js的方法了;如果调用方是js,那订阅事件的就是WinForm里的方法了。
WinForm是不可能直接调用到JS的,主要通过IJSRuntime来调用js方法,同样,js也不能直接调WinForm,是通过js调razor中方法,razor方法再调用WinForm来实现,总体上就是razor中的C#层,是中间桥梁。razor中的 C#与WinForm就是通过注入ServiceCollection中的事件服务来互通协作(有点绕,多读几次)。
本例中定义了一个IEventHub接口和EventHub一个实现类完成封装。在这组服务中,既有CSharp的调用方法,又有JS调用方法,有CSharp事件,也有JS事件,代码如下:
IEventHub接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WinFormsBlazor01 { public delegate Task<object> EventHubHandlerAsync<TEventArgs>(object sender, TEventArgs? eventArgs); public interface IEventHub { string? EventName { get; set; } event EventHubHandlerAsync<object?[]>? OnCallJSAsync; Task<object> CallJSAsync(string eventName, params object?[]? eventArgs); event EventHubHandlerAsync<object?[]>? OnCallCSharpAsync; Task<object> CallCSharpAsync(string eventName, params object?[]? eventArgs); } }
EvnetHub类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WinFormsBlazor01 { public class EventHub : IEventHub { public string? EventName { get; set; } public event EventHubHandlerAsync<object?[]>? OnCallJSAsync; public async Task<object> CallJSAsync(string eventName, params object?[]? eventArgs) { if (OnCallJSAsync != null) { EventName = eventName; return await OnCallJSAsync(this, eventArgs); } return await Task.FromResult(""); } public event EventHubHandlerAsync<object?[]>? OnCallCSharpAsync; public async Task<object> CallCSharpAsync(string eventName, params object?[]? eventArgs) { if (OnCallCSharpAsync != null) { EventName = eventName; return await OnCallCSharpAsync(this, eventArgs); } return ""; } } }
再定义一个BlazorService类型,用来统一创建ServiceCollection:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.WebView.WindowsForms; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; namespace WinFormsBlazor01.Razors { public class BlazorService { public static IEventHub CretaeBlazorService<IService, Service, RazorPage>(BlazorWebView blazorWebView) where IService : class where Service : class, IService where RazorPage : IComponent { var services = new ServiceCollection(); services.AddWindowsFormsBlazorWebView(); services.AddSingleton<IService, Service>(); var eventHub = new EventHub(); services.AddSingleton<IEventHub>(eventHub); blazorWebView.HostPage = "wwwroot\\index.html"; blazorWebView.Services = services.BuildServiceProvider(); blazorWebView.RootComponents.Add<RazorPage>("#app"); return eventHub; } public static IEventHub CretaeBlazorService<RazorPage>(BlazorWebView blazorWebView) where RazorPage : IComponent { var services = new ServiceCollection(); services.AddWindowsFormsBlazorWebView(); var eventHub = new EventHub(); services.AddSingleton<IEventHub>(eventHub); blazorWebView.HostPage = "wwwroot\\index.html"; blazorWebView.Services = services.BuildServiceProvider(); blazorWebView.RootComponents.Add<RazorPage>("#app"); return eventHub; } } }
为了使很多个razor页面都有这个能力,还需要封装一个ComponentBase子类,来充当每个razor的父类,这样可以省略在每个razor中订单事件,代码如下:
using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WinFormsBlazor01.Razors { public abstract class EventComponent : ComponentBase, IDisposable { protected DotNetObjectReference<EventComponent>? dotNetHelper; protected override async Task OnInitializedAsync() { IEventHub? eventHub = null; IJSRuntime? js = null; foreach (var pro in this.GetType().GetProperties(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)) { if (typeof(IEventHub).IsInstanceOfType(pro.GetValue(this, new object[0]))) { eventHub = pro.GetValue(this, new object[0]) as IEventHub; } if (typeof(IJSRuntime).IsInstanceOfType(pro.GetValue(this, new object[0]))) { js = pro.GetValue(this, new object[0]) as IJSRuntime; } } if (eventHub != null && js != null) { eventHub.OnCallJSAsync += CallAsync; async Task<object> CallAsync(object sender, object?[]? eventArgs) { var eventhub = sender as EventHub; return await js.InvokeAsync<object>(eventhub?.EventName!, eventArgs); } } if (js != null) { dotNetHelper = DotNetObjectReference.Create(this); await js.InvokeVoidAsync("CallHelpers.DotNetHelper", dotNetHelper); } base.OnInitialized(); } public void Dispose() { dotNetHelper?.Dispose(); } } }
有了razor page在的父类了,怎么继承呢?定义一个_Imports.razor,来当所有razor的共公引用,这里需经继承EventComponent,同时引用注入IEventHub和IJSRuntime两个接口的子对像:
@using Microsoft.AspNetCore.Components.Web @using Microsoft.JSInterop; @inherits EventComponent @inject IEventHub eventHub @inject IJSRuntime js
所有基础工作准备好了,接下来试验一把,在index.html(这里不清楚查看前一篇《WinForm混合Blazor(上)》)添加js代码:
function showtitle(title) { document.getElementById("title").innerText = "JS接到WinForm数据:" + title return title } class CallHelpers { static dotNetHelper; static DotNetHelper(value) { CallHelpers.dotNetHelper = value; } static async callForm2(name) { const msg = await CallHelpers.dotNetHelper.invokeMethodAsync('CallForm2', name); alert(`JS接到WinForm的返回值: "${msg}"`); } } window.CallHelpers = CallHelpers;
razor页面文件如下:
@using Microsoft.AspNetCore.Components.Web <div class="row"> <h2>我是HTML窗体</h2> </div> <div class="row"> <div class="col-12"> <div class="input-group mb-3"> <input type="text" class="form-control" id="FindName" placeholder="请输入信息" @bind="InputName" aria-describedby="button-addon2"> <button class="btn btn-outline-secondary" type="button" @onclick="CallForm1" id="button-addon2">razorC#触发WinForm事件</button> <button class="btn btn-outline-secondary" type="button" onclick="CallHelpers.callForm2(document.getElementById('FindName').value)" id="button-addon2">JS触发WinForm事件</button> </div> </div> </div> <div class="row"> <h3 id="title"></h3> </div> @code { private string? InputName="ABCDEFG123456"; #region 方法一,用@bind调razor中C#方法,再调用WinForm中方法 async void CallForm1() { var result = await eventHub.CallCSharpAsync("clientclick", InputName); MessageBox.Show("razor中C#接到的返回结果:" + result.ToString()); } #endregion #region 方法二,通过js方法调用razor中C#方法,再调用WinForm中方法 /// <summary> /// 这个方法是js中调过来的 /// </summary> /// <param name="name"></param> /// <returns></returns> [JSInvokable] public async Task<object> CallForm2(string name) { var result = await eventHub.CallCSharpAsync("clientclick", name ); return result; } #endregion }
WinForm中定义如下:
WinForm窗体Designer文件
namespace WinFormsBlazor01 { partial class AddForm { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.addBlazorWebView = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView(); this.panel1 = new System.Windows.Forms.Panel(); this.txtNo = new System.Windows.Forms.TextBox(); this.labBackMessage = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.labMessage = new System.Windows.Forms.Label(); this.button1 = new System.Windows.Forms.Button(); this.panel1.SuspendLayout(); this.SuspendLayout(); // // addBlazorWebView // this.addBlazorWebView.Dock = System.Windows.Forms.DockStyle.Fill; this.addBlazorWebView.Location = new System.Drawing.Point(0, 176); this.addBlazorWebView.Name = "addBlazorWebView"; this.addBlazorWebView.Size = new System.Drawing.Size(967, 414); this.addBlazorWebView.TabIndex = 1; this.addBlazorWebView.Text = "queryBlazorWebView"; // // panel1 // this.panel1.Controls.Add(this.txtNo); this.panel1.Controls.Add(this.labBackMessage); this.panel1.Controls.Add(this.label1); this.panel1.Controls.Add(this.labMessage); this.panel1.Controls.Add(this.button1); this.panel1.Dock = System.Windows.Forms.DockStyle.Top; this.panel1.Location = new System.Drawing.Point(0, 0); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(967, 176); this.panel1.TabIndex = 2; // // txtNo // this.txtNo.Location = new System.Drawing.Point(423, 39); this.txtNo.Name = "txtNo"; this.txtNo.Size = new System.Drawing.Size(362, 23); this.txtNo.TabIndex = 6; // // labBackMessage // this.labBackMessage.AutoSize = true; this.labBackMessage.Location = new System.Drawing.Point(423, 111); this.labBackMessage.Name = "labBackMessage"; this.labBackMessage.Size = new System.Drawing.Size(73, 17); this.labBackMessage.TabIndex = 5; this.labBackMessage.Text = "-------------"; // // label1 // this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font("Microsoft YaHei UI", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.label1.Location = new System.Drawing.Point(12, 9); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(198, 30); this.label1.TabIndex = 4; this.label1.Text = "我是WinForm窗体"; // // labMessage // this.labMessage.AutoSize = true; this.labMessage.ForeColor = System.Drawing.Color.Red; this.labMessage.Location = new System.Drawing.Point(38, 91); this.labMessage.Name = "labMessage"; this.labMessage.Size = new System.Drawing.Size(116, 17); this.labMessage.TabIndex = 3; this.labMessage.Text = "============"; // // button1 // this.button1.Location = new System.Drawing.Point(817, 82); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(121, 34); this.button1.TabIndex = 2; this.button1.Text = "触发Html事件"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // AddForm // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(967, 590); this.Controls.Add(this.addBlazorWebView); this.Controls.Add(this.panel1); this.Name = "AddForm"; this.Text = "AddForm"; this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.ResumeLayout(false); } #endregion private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView addBlazorWebView; private Panel panel1; private Button button1; private Label labMessage; private Label label1; private Label labBackMessage; private TextBox txtNo; } }
窗体cs文件
using System; using System.Threading.Tasks; using System.Windows.Forms; using WinFormsBlazor01.Razors; namespace WinFormsBlazor01 { public partial class AddForm : Form { IEventHub _eventHub; public AddForm() { InitializeComponent(); _eventHub = BlazorService.CretaeBlazorService<Add>(addBlazorWebView); _eventHub.OnCallCSharpAsync += EventHub_OnCallCSharpAsync; txtNo.Text = Guid.NewGuid().ToString("N").ToUpper(); } private async Task<object> EventHub_OnCallCSharpAsync(object sender, object?[]? eventArgs) { var eventHub = sender as EventHub; if (eventHub?.EventName == "clientclick" && eventArgs != null && eventArgs.Length > 0) { labMessage.Text = "JS事件传过来的参数:" + eventArgs?[0]?.ToString()!; } return await Task.FromResult(eventArgs?[0]?.ToString()!); } private async void button1_Click(object sender, EventArgs e) { var result = await _eventHub.CallJSAsync("showtitle", txtNo.Text); labBackMessage.Text = "WinForm调用JS返回值:" + result.ToString(); } } }
查看结果:
JS调用WinForm,用@bind方式
JS调用WinForm,用js方法调用
WinForm方法调用JS
附代码:
https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/Blazor/WinFormsBlazor01
想要更快更方便的了解相关知识,可以关注微信公众号
标签:System,混合,eventHub,new,using,Blazor,Drawing,WinForm From: https://www.cnblogs.com/axzxs2001/p/16950060.html