使用 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 的回答,我才能让它工作。希望它能帮助到别人。
我的几个 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 的回答,我才能让它工作。希望它能帮助到别人。