使用 Xamarin.Forms 更新 ListView

Updating a ListView with Xamarin.Forms

我的几个 Xamarin Forms 应用程序中的列表视图存在问题。一种形式在选项卡式页面设置中,另一种形式是普通内容页面(不同的应用程序)

我有一个class这样的

public class SomeClass
{
    public string StringOne {get;set;}
    public string StringTwo {get;set;}
    public int IntOne {get;set;}
}

在我的内容页面中,我设置了一个 ObservableCollection 并添加了一些数据。然后我告诉列表 SomeClass 是我的 ItemSource。这会在我的所有设备上正确生成 ListView。

问题是,当我更改其中一个属性时,ListView 上没有任何变化(所以如果说我在 Observable 中有 3 个对象并删除一个,列表仍然显示 3 - 或者如果我更改 属性 在我的第二个对象中,ListView 上的第二个项目也没有改变。

我也尝试通过使用标准列表解决问题并在 class 中实现 INotifyChanged。同样,当 List 更改时,ListView 不会更改。

我知道数据已经改变了,好像我对对象做了改变,出来又进去,UI里面的数据已经改变了。

我是不是做错了什么或者这是我需要放入 Bugzilla 中的错误?

不绑定实现INotifyPropertyChanged接口不会改变

示例代码:

public class ObservableProperty : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class SomeClass:ObservableProperty
{
    string stringOne;
    string stringTwo;
    int intOne;
    public string StringOne 
    {
        get{return stringOne;}
        set
        {
            stringOne = value;
            OnPropertyChanged("StringOne");
        }
    }

    public string StringTwo 
    {
        get{ return stringTwo;}
        set
        {
            stringTwo = value;
            OnPropertyChanged("StringTwo");
        }
    }

    public int IntOne 
    {
        get{ return intOne;}
        set
        {
            intOne = value;
            OnPropertyChanged("IntOne");
        }
    }
}

public class MainVM:ObservableProperty
{
    ObservableCollection<SomeClass> items;

    public ObservableCollection<SomeClass> items
    {
        get{return items;}
        set
        {
            items = value;
            OnPropertyChanged("Items");
        }
    }

    public MainVM()
    {
        Items = new ObservableCollection<SomeClass>();

        Items.Add(new SomeClass(){StringOne = "123", StringTwo = "test", IntOne =12});
    }

    public void CallMeForChangingProperty()
    {
        SomeClass item = Items[0];
        item.StringOne = "Test1";
    }
}


public class MainView
{
    public MainView()
    {
        this.BindingContext=  new MainVM()
    }
} 


< ListView ItemsSource="{Binding Items}" RowHeight="120">
 < ListView.ItemTemplate>
   < DataTemplate>
     < ViewCell>
       < ViewCell.View>
        < StackLayout>
            < Label Text= "StringOne" />
            < Label Text= "StringTwo" />
            < Label Text= "IntOne" />
        </ StackLayout>
       </ ViewCell.View>
     </ ViewCell>
   </ DataTemplate>
 </ ListView.ItemTemplate>
</ ListView>

@eakgul 给出的答案对我来说就像一个魅力。 我会在这里附上我已经实现的东西,也许它可以帮助某人。

您必须同时将 INotifyPropertyChanged 设置为 ObservableColection 及其对象。

我有一个带有 INotifyPropertyChanged 的​​ BaseViewModel,如下所示:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protected void SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals( backingField, value)) return;
        backingField = value;
        OnPropertyChanged(propertyName);
    }
}

在我的 BluetoothPage.xaml 上,首先我将 bindincontext 设置为我的 BluetoothPageViewModel.cs 并设置 ListView ItemsSource 及其绑定标签:

<ContentPage.BindingContext>
    <viewmodel:BluetoothPageViewModel/>
</ContentPage.BindingContext>

<ContentPage.Content>
    <StackLayout Padding="5,10">

        <Button x:Name="Scan_Devices_Button"
                Command="{Binding SearchNew_Button_Clicked}"/>
        
        <ListView x:Name="DevicesList"
                  ItemsSource="{Binding BluetoothDevices}"
                  SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                  IsPullToRefreshEnabled="True">
            
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Label Grid.Column="0"
                                       Text="{Binding device.Device.NativeDevice.Name}"/>
                                <Label Grid.Column="1"
                                       Text="{Binding device.Device.NativeDevice.Address, StringFormat='ID: {0}'}"/>
                            </Grid>
                            
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Label Grid.Column="0"
                                       Text="{Binding device.Rssi, StringFormat='Power: {0:F2}dbm'}"/> 
                                <Label Grid.Column="1"
                                       Text="{Binding distance, StringFormat='Distance: {0:F2}m'}"/>
                            </Grid>
                        </StackLayout>
                    </ViewCell>
                    
                </DataTemplate>
            </ListView.ItemTemplate>
            
        </ListView>
    </StackLayout>
</ContentPage.Content>
然后,在我的 BluetoothPageViewModel.cs 中,我使用 BaseViewModel 扩展它并使用 INotifyPropertyChanged 声明 ItemsSource BluetoothDevices。此时,每次我更改 ObservableCollection BluetoothDevices 上的项目时,ListView 都会更新。但是,如果我对 ObservableCollection 中的项目进行了更改,则什么也不会发生! 因此,您必须将 INotifyPropertyChanged 设置为它的值。 以下是我的 BluetoothPageViewModel,它在 PageModel BluetoothPageModel.cs 中使用 class BluetoothPageModel

BluetoothPageViewModel:

public class BluetoothPageViewModel : BaseViewModel
{
    public BluetoothPageViewModel()
    {
        SearchNew_Button_Clicked = new Command(NewDevices_Button_Clicked_Event);
        Scan_Devices_Button_BgColor = "#D6D7D7";
        Scan_Devices_Button_Text = "Scan nearby devices";
    }


    #region Declarations

    public List<IDevice> iDeviceList = new List<IDevice>();
    public ObservableCollection<BluetoothPageModel> _bluetoothDevices = new ObservableCollection<BluetoothPageModel>();
    public BluetoothPageModel _selectedItem;

    public ObservableCollection<BluetoothPageModel> BluetoothDevices
    {
        get { return _bluetoothDevices; }
        set { SetProperty(ref _bluetoothDevices, value); }
    }
    public BluetoothPageModel SelectedItem
    {
        get { return _selectedItem; }
        set { SetProperty(ref _selectedItem, value); }
    }

    public ICommand SearchNew_Button_Clicked { get; private set; }

    #endregion

    #region Functions

    private void NewDevices_Button_Clicked_Event(object obj)
    {
        // discover some devices
        if (!CrossBleAdapter.Current.IsScanning)
        {
            BluetoothDevices.Clear();
            iDeviceList.Clear();
            var scanner = CrossBleAdapter.Current.Scan().Subscribe(scanResult =>
            {
                if (!iDeviceList.Contains(scanResult.Device))
                {
                    iDeviceList.Add(scanResult.Device);
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        BluetoothDevices.Add(new BluetoothPageModel
                        {
                            device = scanResult,
                            distance = Math.Pow(10, ((-68 - scanResult.Rssi) / 31.1474))
                        });
                    });
                }
                else
                {
                    int ind = iDeviceList.IndexOf(scanResult.Device);


                    Device.BeginInvokeOnMainThread(() =>
                    {
                        BluetoothDevices[ind].device = scanResult;
                        BluetoothDevices[ind].distance = Math.Pow(10, ((-68 - scanResult.Rssi) / 31.1474));
                    });
                }
            });
        }
        else
        {
            CrossBleAdapter.Current.StopScan(); //When you want to stop scanning
        }
    }
    #endregion
}

最后,当您更改 BluetoothPageModel 属性 的 属性 时能够更新数据 class:

public class BluetoothPageModel:BaseViewModel
{
    public IScanResult _device;
    public double _distance;
    public IScanResult device 
    {
        get { return _device; }
        set { SetProperty(ref _device, value); }
    }
    public double distance
    {
        get { return _distance; }
        set { SetProperty(ref _distance, value); }
    }
}

多亏了 eakgul 的回答,我才能让它工作。希望它能帮助到别人。