You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Maui:从蓝牙事件自动更新Blazor UI,无需手动调用更新方法

解决.NET Maui Blazor蓝牙事件UI自动更新问题

核心原因

Android蓝牙广播接收器的事件在后台线程触发,而Blazor组件的状态变更需要在UI线程执行;同时Blazor的自动状态检测仅在组件生命周期方法或UI事件回调中生效,后台线程的状态变更无法自动触发UI更新。

解决方案

方案1:让蓝牙服务在UI线程触发事件

修改BluetoothService,将事件回调切换到UI线程执行,组件中无需手动调用InvokeAsync和StateHasChanged:

// 修改BluetoothService中的事件处理逻辑
private void BluetoothReceiver_DeviceFound(object? sender, Platforms.Android.CustomCode.EventArgs.DeviceFoundEventArgs e)
{
    if (e.Device?.Name is string name)
    {
        // 切换到UI线程执行回调
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            await deviceFound(name);
        });
    }
}

private void BluetoothReceiver_DiscoveryFinished(object? sender, EventArgs e)
{
    IsDiscovering = false;
    // 切换到UI线程执行回调
    MainThread.BeginInvokeOnMainThread(async () =>
    {
        await discoveryFinished();
    });
}

然后简化Razor页面的回调方法:

private Task OnDeviceDiscovered(string deviceName)
{
    devices.Add(deviceName);
    return Task.CompletedTask;
}

private Task OnDiscoveryFinished()
{
    isDiscovering = false;
    return Task.CompletedTask;
}

方案2:封装Blazor组件基类自动处理状态变更

创建一个基类组件,封装状态更新逻辑,让页面继承该基类以减少重复代码:

public class BaseComponent : ComponentBase
{
    protected async Task UpdateState(Action action)
    {
        await InvokeAsync(() =>
        {
            action();
            StateHasChanged();
        });
    }
}

Razor页面继承基类并使用封装方法:

@inherits BaseComponent
@inject IBluetoothService bluetoothService;

<!-- 页面内容保持不变 -->

@code
{
    private bool isDiscovering = false;
    private ObservableCollection<string> devices = new();

    private async Task StartDiscovery()
    {
        isDiscovering = true;
        await bluetoothService.StartDiscovery(OnDeviceDiscovered, OnDiscoveryFinished);
    }

    private async Task OnDeviceDiscovered(string deviceName)
    {
        await UpdateState(() => devices.Add(deviceName));
    }

    private async Task OnDiscoveryFinished()
    {
        await UpdateState(() => isDiscovering = false);
    }

    private void StopDiscovery()
    {
        isDiscovering = false;
    }
}

方案3:使用IComponentContext触发状态变更(.NET 7+)

在组件中注入IComponentContext,利用其自动处理UI线程调度和状态变更:

@inject IBluetoothService bluetoothService;
@inject IComponentContext ComponentContext;

<!-- 页面内容保持不变 -->

@code
{
    private bool isDiscovering = false;
    private ObservableCollection<string> devices = new();

    private async Task StartDiscovery()
    {
        isDiscovering = true;
        await bluetoothService.StartDiscovery(OnDeviceDiscovered, OnDiscoveryFinished);
    }

    private async Task OnDeviceDiscovered(string deviceName)
    {
        await ComponentContext.InvokeAsync(() =>
        {
            devices.Add(deviceName);
        });
    }

    private async Task OnDiscoveryFinished()
    {
        await ComponentContext.InvokeAsync(() =>
        {
            isDiscovering = false;
        });
    }

    private void StopDiscovery()
    {
        isDiscovering = false;
    }
}

额外优化提示

  • 当前BluetoothService仅申请了权限,需补充调用bluetoothAdapter.StartDiscovery()启动扫描
  • Blazor对ObservableCollection的自动更新支持有限,结合UI线程触发变更更可靠

内容的提问来源于stack exchange,提问作者Pieterjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:17:32