Xamarin.Forms: 获取一个listview的所有cells/items
Xamarin.Forms: Get all cells/items of a listview
我想对 ListView 中显示的所有单元格进行一些更改。因此我想获取所有单元格或项目。例如
this.listView.ItemSource.Items
上述代码中的 Items 不存在。此外,我没有在 ListView
或 ItemSource
上找到任何可以执行此操作的选项。有没有可能得到所有cells/items?如果不是,我还需要哪些其他选项来更改单元格加载后的外观?例如。用于更改文本或布局。
通常情况下,您不会直接触摸您的细胞。相反,如果可能的话,您会尝试通过数据绑定来完成所有事情。
让我们使用 XAML 中定义的以下页面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App6.Page1">
<ListView x:Name="myList"
BackgroundColor="Gray"
ItemsSource="{Binding MyItems}"
HasUnevenRows="true"
RowHeight="-1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
Padding="15, 10, 10, 10"
BackgroundColor="White">
<Label Text="{Binding Title}"
FontSize="18"
TextColor="Black"
VerticalOptions="StartAndExpand"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
在您的页面中,您将 ViewModel 设置为 BindingContext。
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
BindingContext = new Page1ViewModel();
}
}
并且您的 ViewModel 包含 ObservableCollection 中的项目 MyItems
,这意味着如果您添加或删除项目,您的视图会更新。此 属性 绑定为您列表的 ItemsSource(参见 XAML:ItemsSource="{Binding MyItems}"
)。
class Page1ViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyItem> _myItems = new ObservableCollection<MyItem>();
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<MyItem> MyItems
{
get { return _myItems; }
set
{
_myItems = value;
OnPropertyChanged();
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Page1ViewModel()
{
MyItems.Add(new MyItem { Title = "Test" });
MyItems.Add(new MyItem { Title = "Test2" });
MyItems.Add(new MyItem { Title = "Test3" });
MyItems.Add(new MyItem { Title = "Test4" });
MyItems.Add(new MyItem { Title = "Test5" });
}
public void ChangeItem()
{
MyItems[1].Title = "Hello World";
}
}
对于每个项目,您都有一个对象代表 一个 单元格的数据。此项的类型实现 INotifyPropertyChanged
。这意味着,它会通知 属性 已更改。数据绑定机制注册到此事件,并在引发时更新视图。
public class MyItem : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged(); // Notify, that Title has been changed
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
如果您现在更改其中一项,例如通过调用 ChangeItem()
。然后第二个单元格的 Label 将更新为 "Hello World"
,因为它绑定到 Title(参见 XAML Text="{Binding Title}"
)。
这一切背后的总体思想称为 MvvM 模式,您希望将视图与视图逻辑和模型分开(data/services,...)。
我可能迟到了,但这是使用 System.Reflection 的另一种方法。无论如何,我建议仅在无法进行数据绑定的情况下使用它,并作为最后的选择。
对于给定的 ListView
<ListView x:Name="connectionsListView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemSelected="OnConnectionSelect">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="40">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<customs:MyViewClass Grid.Column="0"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<ContentView />
</ListView.Footer>
</ListView>
您可以通过抓取 "TemplatedItems" 属性 来爬取 ListView,
将其转换为 ITemplatedItemsList<Cell>
并遍历您的 (View) 单元格。
IEnumerable<PropertyInfo> pInfos = (connectionsListView as ItemsView<Cell>).GetType().GetRuntimeProperties();
var templatedItems = pInfos.FirstOrDefault(info => info.Name == "TemplatedItems");
if (templatedItems != null)
{
var cells = templatedItems.GetValue(connectionsListView);
foreach (ViewCell cell in cells as Xamarin.Forms.ITemplatedItemsList<Xamarin.Forms.Cell>)
{
if (cell.BindingContext != null && cell.BindingContext is MyModelClass)
{
MyViewClass target = (cell.View as Grid).Children.OfType<MyViewClass>().FirstOrDefault();
if (target != null)
{
//do whatever you want to do with your item...
}
}
}
}
更新:
因为有人问,if (cell.BindingContext != null && cell.BindingContext is MyModelClass) 基本上是一种防止访问不存在的视图的保护措施,例如,如果 ListView 尚未填充。
如果不需要检查 BindingContext 是否为特定模型 class,则将行缩减为
就足够了
if (cell.BindingContext != null)
我想对 ListView 中显示的所有单元格进行一些更改。因此我想获取所有单元格或项目。例如
this.listView.ItemSource.Items
上述代码中的 Items 不存在。此外,我没有在 ListView
或 ItemSource
上找到任何可以执行此操作的选项。有没有可能得到所有cells/items?如果不是,我还需要哪些其他选项来更改单元格加载后的外观?例如。用于更改文本或布局。
通常情况下,您不会直接触摸您的细胞。相反,如果可能的话,您会尝试通过数据绑定来完成所有事情。
让我们使用 XAML 中定义的以下页面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App6.Page1">
<ListView x:Name="myList"
BackgroundColor="Gray"
ItemsSource="{Binding MyItems}"
HasUnevenRows="true"
RowHeight="-1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
Padding="15, 10, 10, 10"
BackgroundColor="White">
<Label Text="{Binding Title}"
FontSize="18"
TextColor="Black"
VerticalOptions="StartAndExpand"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
在您的页面中,您将 ViewModel 设置为 BindingContext。
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
BindingContext = new Page1ViewModel();
}
}
并且您的 ViewModel 包含 ObservableCollection 中的项目 MyItems
,这意味着如果您添加或删除项目,您的视图会更新。此 属性 绑定为您列表的 ItemsSource(参见 XAML:ItemsSource="{Binding MyItems}"
)。
class Page1ViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyItem> _myItems = new ObservableCollection<MyItem>();
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<MyItem> MyItems
{
get { return _myItems; }
set
{
_myItems = value;
OnPropertyChanged();
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Page1ViewModel()
{
MyItems.Add(new MyItem { Title = "Test" });
MyItems.Add(new MyItem { Title = "Test2" });
MyItems.Add(new MyItem { Title = "Test3" });
MyItems.Add(new MyItem { Title = "Test4" });
MyItems.Add(new MyItem { Title = "Test5" });
}
public void ChangeItem()
{
MyItems[1].Title = "Hello World";
}
}
对于每个项目,您都有一个对象代表 一个 单元格的数据。此项的类型实现 INotifyPropertyChanged
。这意味着,它会通知 属性 已更改。数据绑定机制注册到此事件,并在引发时更新视图。
public class MyItem : INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged(); // Notify, that Title has been changed
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
如果您现在更改其中一项,例如通过调用 ChangeItem()
。然后第二个单元格的 Label 将更新为 "Hello World"
,因为它绑定到 Title(参见 XAML Text="{Binding Title}"
)。
这一切背后的总体思想称为 MvvM 模式,您希望将视图与视图逻辑和模型分开(data/services,...)。
我可能迟到了,但这是使用 System.Reflection 的另一种方法。无论如何,我建议仅在无法进行数据绑定的情况下使用它,并作为最后的选择。
对于给定的 ListView
<ListView x:Name="connectionsListView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemSelected="OnConnectionSelect">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="40">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<customs:MyViewClass Grid.Column="0"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<ContentView />
</ListView.Footer>
</ListView>
您可以通过抓取 "TemplatedItems" 属性 来爬取 ListView,
将其转换为 ITemplatedItemsList<Cell>
并遍历您的 (View) 单元格。
IEnumerable<PropertyInfo> pInfos = (connectionsListView as ItemsView<Cell>).GetType().GetRuntimeProperties();
var templatedItems = pInfos.FirstOrDefault(info => info.Name == "TemplatedItems");
if (templatedItems != null)
{
var cells = templatedItems.GetValue(connectionsListView);
foreach (ViewCell cell in cells as Xamarin.Forms.ITemplatedItemsList<Xamarin.Forms.Cell>)
{
if (cell.BindingContext != null && cell.BindingContext is MyModelClass)
{
MyViewClass target = (cell.View as Grid).Children.OfType<MyViewClass>().FirstOrDefault();
if (target != null)
{
//do whatever you want to do with your item...
}
}
}
}
更新: 因为有人问,if (cell.BindingContext != null && cell.BindingContext is MyModelClass) 基本上是一种防止访问不存在的视图的保护措施,例如,如果 ListView 尚未填充。
如果不需要检查 BindingContext 是否为特定模型 class,则将行缩减为
就足够了if (cell.BindingContext != null)