以前、Blazor Hybridの例で、Windows FormsとBlazorを試してみたが、今回はMAUIで試してみた。
MAUIの場合も、Windows Formsと同様にMAUI~Blazor間の連携が可能。
共有する、Singletonインスタンスを以下のような感じで作成し、MauiProgram.cs内でサービス登録することで、相互で同じインスタンスを共有できる。
namespace MAUIBlApp;
public interface IHostIF {
/// <summary>
/// 共有データ
/// </summary>
/// <value>カウント値</value>
public int Count { get; set; }
/// <summary>
/// クライアントイベントハンドラ
/// </summary>
public event EventHandler ComponentEvent;
/// <summary>
/// クライアント側からイベント発生
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void FireClientEvent(object sender, EventArgs e);
/// <summary>
/// ホストイベントハンドラ
/// </summary> <summary>
///
/// </summary>
public event EventHandler HostEvent;
//
/// <summary>
/// ホスト側からイベント発生
/// </summary>
/// <value></value>
public void FireHostEvent(object sender, EventArgs e);
}
/// <summary>
/// IHostIFの実装
/// </summary>
public class HostInterface : IHostIF {
public int Count { get; set; } = 0;
public event EventHandler ComponentEvent = null!;
public void FireClientEvent(object sender, EventArgs e) {
ComponentEvent?.Invoke(sender,e);
}
public event EventHandler HostEvent = null!;
public void FireHostEvent(object sender, EventArgs e) {
HostEvent?.Invoke(sender,e);
}
}
using Microsoft.Extensions.Logging;
namespace MAUIBlApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
HostInterface hst = new HostInterface();
builder.Services.AddSingleton<IHostIF>(hst);
builder.Services.AddMauiBlazorWebView();
・・・
namespace MAUIBlApp;
public partial class App : Application
{
public App(IHostIF hif)
{
InitializeComponent();
// MainPageのロード(IHostIF(Singleton)インスタンスを渡す)
MainPage = new MainPage(hif);
}
}
namespace MAUIBlApp;
public partial class MainPage : ContentPage
{
private readonly IHostIF _hostif;
/// <summary>
/// コンストラクタ(IHostIFのインスタンスを取得)
/// </summary>
/// <param name="hostif"></param>
public MainPage(IHostIF hostif)
{
InitializeComponent();
_hostif = hostif;
// Client(Blazor Component)イベントハンドラ設定
_hostif.ComponentEvent += OnClientEvent;
lblNumber.Text = _hostif.Count.ToString();
}
/// <summary>
/// ボタンクリック時処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnBtnClick(object sender, EventArgs e) {
// インターフェースのカウントアップ
_hostif.Count++;
lblNumber.Text = _hostif.Count.ToString();
// Client(Blazor Component)へイベント通知
_hostif.FireHostEvent(sender,e);
}
/// <summary>
/// Client(Blazor Component)イベントハンドラ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnClientEvent(object? sender, EventArgs e) {
lblNumber.Text = _hostif.Count.ToString();
}
}
@page "/"
@inject IHostIF HostInterface
<h2>Counter</h2>
<p role="status">Current count:
@if (Numbers != null && Numbers.Count != 0) {
foreach(var v in Numbers) {
string imgname = $"/images/number_{v}.png";
<img src="@imgname" style="width:42px;height:49px"/>
}
}
</p>
<br/>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
/// <summary>
/// カウンタのイメージIndex
/// </summary>
/// <returns></returns>
protected List<int> Numbers = new () {0};
/// <summary>
/// Initialize
/// </summary>
protected override void OnInitialized() {
// Hostイベントのハンドラ設定
HostInterface.HostEvent += async (_,_) => await InvokeAsync(()=>{CreateNumberImages();StateHasChanged();});
}
/// <summary>
/// ボタンイベント
/// </summary>
private void IncrementCount()
{
// Interfaceのカウントをインクリメント
HostInterface.Count++;
// カウンタ⇒イメージ番号の作成
CreateNumberImages();
// ホストに通知
HostInterface.FireClientEvent(this,new EventArgs());
}
/// <summary>
/// イメージ番号の生成
/// </summary>
private void CreateNumberImages() {
Numbers = new();
string s = HostInterface.Count.ToString();
for(int i=0;i < s.Length; i++) {
Numbers.Add(Convert.ToInt32(s[i].ToString()));
}
}
}
以下のように、MAUI側からでもBlazorコンポーネント側からでも同じインスタンスを参照していることが分かる。
最初、MainPageの呼出でコンパイルエラーが出ていて悩んだが、よくよくエラーを見ると、MainPage.xaml.csでは無く、App.xaml.csでMainPageインスタンスを作成するときに、インターフェイスインスタンスを渡していないエラーだった。
Appのコンストラクタにインターフェイスインスタンスをパラメータで指定することにより、サービス登録されたSingletonインスタンスを取得できるので、これをMainPage作成時のパラメータとして渡すことで解決できた。
※MAUIをVS Codeで作成するなら「.NET MAUI拡張」を入れておくと便利。
Hostからの更新を直接呼出じゃなくて、Eventを介した方がキレイかなと思う今日この頃。
Hostからのカウント更新時の処理をEvent化しました。