Listview 不会根据 ObservableCollection 属性进行更新

Posted

技术标签:

【中文标题】Listview 不会根据 ObservableCollection 属性进行更新【英文标题】:Listview is not updating based on ObservableCollection properties 【发布时间】:2021-07-31 23:31:51 【问题描述】:

我有一个 BLE 应用程序,它连接到设备并旨在在列表视图中动态接收设备状态。我已将本机设备类实现为模型,并且我的视图模型在 MainViewModel 类中是单独的。我已经在这两个类上实现了 INotifyPropretyChanged,但是在我连接到它之后设备的状态没有改变。它应该是 Disconnected、Connecting、Connected、Disconnected 并且我有一个来自这个 BLE 插件的 Name DeviceState 属性,它保存了这个值 + 我已经为它实现了一个 INotifyPropretyChanged。我已经尝试过没有 TwoWay 和 NotifyCollectionChanged 等。我成功地获得了设备名称和状态(断开连接),但是当我连接到它时,状态应该改变并且它没有。它成功连接,我在另一个没有 MVVM 方法的应用程序上尝试过它,它在连接后获取状态,但我必须循环遍历它们并将它们添加到单独的集合中,这不是很有效。如有任何帮助,将不胜感激,在此先感谢您!

class NativeDevice : INotifyPropertyChanged
    
        private string deviceNameValue = String.Empty;
        public event PropertyChangedEventHandler PropertyChanged;
        private DeviceState states;


        public NativeDevice(string name, DeviceState stated)
        
            deviceNameValue = name;
            states = stated;
        
        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        
            var handler = PropertyChanged;
            if (handler != null)
            
                handler(this, new PropertyChangedEventArgs(propertyName));
            
        
        public string Name
        
            get
            
                return this.deviceNameValue;
            

            set
            
                if (value != this.deviceNameValue)
                
                    this.deviceNameValue = value;
                    OnPropertyChanged();
                
            
        
        
        public DeviceState States
        
            get
            
                return states;
            

            set
            
                this.states = value;
                OnPropertyChanged();
            
        
       


    
class MainViewModel : INotifyPropertyChanged
    
        public ObservableCollection<IDevice> BluetoothDevices  get; set; 
        public ObservableCollection<NativeDevice> devicesFound;
        public ObservableCollection<NativeDevice> DevicesFound  get  return 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);
            DevicesFound = new ObservableCollection<NativeDevice>();
            BluetoothDevices = new ObservableCollection<IDevice>();
            _bluetoothAdapter = CrossBluetoothLE.Current.Adapter;
            _bluetoothAdapter.DeviceDiscovered += (s, a) =>
            
                BluetoothDevices.Add(a.Device);
            ;
            ConnectToDevices = new AsyncCommand(ConnectAsync);
            CancellationToken token = source.Token;
            devicesFound = new ObservableCollection<NativeDevice>();
        



        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        
            var handler = PropertyChanged;
            if (handler != null)
            
                handler(this, new PropertyChangedEventArgs(propertyName));
            
        
        
        async Task PerformScan
         await _bluetoothAdapter.StartScanningForDevicesAsync();

            foreach (var item in BluetoothDevices)
            
                if (item.Name == "GP")
                
                    DevicesFound.Add(new NativeDevice(item.Name, item.State));
                
            
            await _bluetoothAdapter.StopScanningForDevicesAsync();
        
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:bleappmodelview="clr-namespace:BleAppModelView.ViewModels" xmlns:bleappmodelview1="clr-namespace:BleAppModelView.Model" x:DataType="bleappmodelview:MainViewModel"
             x:Class="BleAppModelView.MainPage">

    <ContentPage.BindingContext>
        <bleappmodelview:MainViewModel/>
    </ContentPage.BindingContext>

    <StackLayout>
       
        <Button x:DataType="bleappmodelview:MainViewModel"
                Text="Scan"
                Command="Binding ScanForDevices"
                Margin="10" />
        <ListView  ItemsSource="Binding DevicesFound">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label x:DataType="bleappmodelview1:NativeDevice" Text="Binding Name, Mode=TwoWay"/>
                            <Label x:DataType="bleappmodelview1:NativeDevice" Text="Binding States, Mode=TwoWay" TextColor="Red"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Button x:DataType="bleappmodelview:MainViewModel"
                Text="Connect" 
                Command="Binding ConnectToDevices"
                Margin="10"/>
    </StackLayout>


</ContentPage>
 public partial class MainPage : ContentPage
    
        public MainPage()
        
            InitializeComponent();
            BindingContext = new MainViewModel();
        
    
namespace Plugin.BLE.Abstractions

    public enum DeviceState
    
        Disconnected = 0,
        Connecting = 1,
        Connected = 2,
        Limited = 3
    

 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);
                
            
        

【问题讨论】:

我不清楚问题是什么。您是说添加新设备时它不会显示正确的状态吗?或者在添加它之后,当状态改变时它不会更新?此外,您同时初始化 devicesFoundDevicesFound,您应该只更新公共属性,而不是同时更新。 当我连接时,状态应该从已断开连接更改为正在连接并已连接,但它仍处于断开连接状态,因此它不会更新属性。首次添加设备时初始状态正确 我在您的代码中看不到任何会在初始连接后更新状态的内容 我有一个连接设备的方法。一旦它连接状态应该改变,它的连接正确但状态没有改变,我以为我将它正确绑定到属性 但是您没有发布该代码 【参考方案1】:

当连接状态改变时,你需要更新你的 UI 绑定的对象

有很多不同的方法来解决这个问题,一个简单的方法是

  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);

                // find the matching device in DevicesFound
                var d = DevicesFound.Where(x => x.Name == device.Name).FirstOrDefault();
                d.States = DeviceState.Connected;
            
        
    

【讨论】:

以上是关于Listview 不会根据 ObservableCollection 属性进行更新的主要内容,如果未能解决你的问题,请参考以下文章

2.5 ListView

在“ListView”中拖放

UWP ListView下模板宽度问题

ListView如何刷新?

显示Title和隐藏Title的ListView

Android:自定义 ListView 不会立即填充