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

Xamarin.Forms MVVM架构下BLE设备状态变更时ListView未同步更新问题

问题根因

  • 你创建NativeDevice实例时仅在扫描阶段赋值了一次初始状态,后续BLE插件的IDevice实例状态发生变更时,没有同步到对应的NativeDevice的States属性,导致UI无法感知到状态变化。
  • 未订阅BLE插件IAdapter提供的设备状态变更事件,无法收到状态更新的回调触发同步逻辑。
  • 存在小的代码缺陷:MainViewModel构造函数重复初始化设备集合、PerformScan方法语法缺失方法括号。

解决方案

1. 扩展NativeDevice类,新增设备唯一标识字段用于匹配对应BLE设备

class NativeDevice : INotifyPropertyChanged
{
    private string deviceNameValue = String.Empty;
    private DeviceState states;
    // 新增设备唯一标识,用于匹配BLE插件返回的IDevice
    public Guid DeviceId { get; }

    public event PropertyChangedEventHandler PropertyChanged;

    public NativeDevice(Guid deviceId, string name, DeviceState stated)
    {
        DeviceId = deviceId;
        deviceNameValue = name;
        states = stated;
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    
    public string Name
    {
        get => deviceNameValue;
        set
        {
            if (value != deviceNameValue)
            {
                deviceNameValue = value;
                OnPropertyChanged();
            }
        }
    }
    
    public DeviceState States
    {
        get => states;
        set
        {
            if (value != states)
            {
                states = value;
                OnPropertyChanged();
            }
        }
    }
}

2. 修改MainViewModel,订阅状态变更事件、修正代码缺陷

class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<IDevice> BluetoothDevices { get; set; }
    private ObservableCollection<NativeDevice> devicesFound;
    public ObservableCollection<NativeDevice> DevicesFound 
    { 
        get => devicesFound; 
        set 
        { 
            devicesFound = value; 
            OnPropertyChanged(); 
        } 
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly IAdapter _bluetoothAdapter;
    public AsyncCommand ScanForDevices { get; }
    public AsyncCommand ConnectToDevices { get; }

    CancellationTokenSource source = new CancellationTokenSource();
    public MainViewModel()
    {
        ScanForDevices = new AsyncCommand(PerformScanAsync);
        BluetoothDevices = new ObservableCollection<IDevice>();
        // 移除重复初始化,仅在这里初始化一次
        DevicesFound = new ObservableCollection<NativeDevice>();
        _bluetoothAdapter = CrossBluetoothLE.Current.Adapter;
        
        // 订阅设备发现事件
        _bluetoothAdapter.DeviceDiscovered += (s, a) =>
        {
            BluetoothDevices.Add(a.Device);
        };

        // 新增订阅设备状态变更事件
        _bluetoothAdapter.DeviceStateChanged += (s, a) =>
        {
            // 匹配到对应的NativeDevice,更新状态
            var targetDevice = DevicesFound.FirstOrDefault(d => d.DeviceId == a.Device.Id);
            if (targetDevice != null)
            {
                targetDevice.States = a.Device.State;
            }
        };

        ConnectToDevices = new AsyncCommand(ConnectAsync);
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    
    // 修正方法语法
    async Task PerformScanAsync()
    {
        // 扫描前清空已有设备,避免重复添加
        BluetoothDevices.Clear();
        DevicesFound.Clear();
        await _bluetoothAdapter.StartScanningForDevicesAsync();
        foreach (var item in BluetoothDevices)
        {
            if (item.Name == "GP")
            {
                // 传入设备ID
                DevicesFound.Add(new NativeDevice(item.Id, item.Name, item.State));
            }
        }
        await _bluetoothAdapter.StopScanningForDevicesAsync();
    }

    async Task ConnectAsync()
    {
        foreach (var device in BluetoothDevices)
        {
            if (device.Name == "GP")
            {
                var parameters = new ConnectParameters(forceBleTransport: true);
                await _bluetoothAdapter.ConnectToDeviceAsync(device, parameters, source.Token);
            }
        }
    }
}

3. 优化XAML绑定,状态展示无需双向绑定,改为默认的单向绑定即可

<Label x:DataType="bleappmodelview1:NativeDevice" Text="{Binding Name}"/>
<Label x:DataType="bleappmodelview1:NativeDevice" Text="{Binding States}" TextColor="Red"/>

修改完成后,蓝牙设备状态发生任何变更时,插件都会触发DeviceStateChanged事件,回调中会自动同步到对应的NativeDevice实例,触发属性变更通知后UI就会自动更新展示最新状态。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 08:06:03