以前、Blazor Hybridについて簡単に説明したが、razorコンポーネントとホスト間のやりとりについて、もう少し詳しく説明してみる。(記事のコメントにも少々文書で書いたが、今回は簡単な実例で説明してみる。)
まず、ホスト,コンポーネント双方で使用する、インターフェースとその実装を以下のような感じで作成する。
public delegate void OnClientEvent(object sender, EventArgs e);
public interface IHostInterface {
public string Message { get; set; }
public int Count { get; set; }
/// <summary>
/// コンポーネントのリフレッシュ等を行なうメソッド
/// (コンポーネント側でセットする)
/// </summary>
/// <value></value>
public Action ComponentRefresh { get; set; }
/// <summary>
/// コンポーネントでイベントが発生した時に呼ばれるホスト側イベントハンドラ
/// </summary>
public event OnClientEvent ComponentEvent;
/// <summary>
/// コンポーネント側からイベントを発生させるメソッド
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void ExecClientEvent(object sender, EventArgs e);
}
public class HostInterface : IHostInterface {
public string Message { get; set; } = null!;
public int Count { get; set; } = 0;
public Action ComponentRefresh { get; set; } = null!;
public event OnClientEvent ComponentEvent = null!;
public void ExecClientEvent(object sender, EventArgs e) {
ComponentEvent.Invoke(sender,e);
}
}
コンポーネント側では、このインターフェースを@injectでSingletonオブジェクトとして、使用できるようにする。
下記の例では、インターフェイス中のCounterプロパティをホストとの共有データとして使用し、ホスト側のトリガで、コンポーネントの更新が行えるよう、ComponentRefreshでStateHasChangedメソッドを呼び出せるようにしている。
また、コンポーネント側のイベントを、ホストにも知らせることができるよう、イベント発生時に、ホスト側のイベントハンドラを呼び出している。
@inject IHostInterface HostIF
<h1>Counter</h1>
<p>Current count: @HostIF.Count</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
protected override void OnInitialized() {
// Componentの再描画メソッドを設定
HostIF.ComponentRefresh = (Action)(()=>StateHasChanged());
}
private void IncrementCount() {
// カウントアップした後、ホスト側へイベント通知
HostIF.Count++;
HostIF.ExecClientEvent(this,new EventArgs());
}
}
ホスト側では、コンストラクタで、ComponentEventにハンドラを設定することにより、コンポーネントで状態変更が有った場合の処理を定義できる。
private HostInterface HostIF;
public Form1()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
// Singletonオブジェクトの作成
HostIF = new HostInterface();
// コンポーネントから呼び出されるイベントハンドラ
HostIF.ComponentEvent += On_ClientEvent;
// Singletonオブジェクトをコンポーネントサービスに追加
services.AddSingleton<IHostInterface>(HostIF);
blView.HostPage = @"wwwroot\index.html";
blView.Services = services.BuildServiceProvider();
blView.RootComponents.Add<Counter>("#app");
}
/// <summary>
/// インクリメントボタンクリックイベントハンドラ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void On_btnIncrement_Click(object sender, EventArgs e) {
// Singletonのカウンタをインクリメント
HostIF.Count++;
lblMessage.Text = $"カウント:{HostIF.Count}";
// コンポーネント表示を更新
HostIF.ComponentRefresh();
}
/// <summary>
/// コンポーネントイベントハンドラ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void On_ClientEvent(object sender, EventArgs e) {
// ホストの表示を更新
lblMessage.Text = $"カウント:{HostIF.Count}";
}
実行すると以下のように、ホストとコンポーネント間の同期が取れていることが分かる。
何かに使えるかも知れないので、メモ。