Xamarin.Forms:获取列表视图的所有单元格/项目

Posted

技术标签:

【中文标题】Xamarin.Forms:获取列表视图的所有单元格/项目【英文标题】:Xamarin.Forms: Get all cells/items of a listview 【发布时间】:2016-06-21 07:47:42 【问题描述】:

我想对ListView 中显示的所有单元格进行一些更改。因此,我想获取所有单元格或项目。例如

this.listView.ItemSource.Items
上述代码中的

Items 不存在。此外,我在ListViewItemSource 上没有找到任何可以做到这一点的选项。是否可以获得所有单元格/项目?如果不是,我还必须在加载单元格后更改哪些其他选项?例如。用于更改文本或布局。

【问题讨论】:

【参考方案1】:

我可能迟到了,但这里有另一种使用 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&lt;Cell&gt; 并遍历您的(视图)单元格。

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 是否为特定模型类,则将行减少到

if (cell.BindingContext != null)

【讨论】:

抱歉这个愚蠢的问题,MyViewClass 到底指的是什么? 如果我没猜错的话,比如标签/开关/按钮。 是的,MyViewClass 是您添加到列表中并现在要查找的任何类的占位符。 我喜欢这种方法,到目前为止我遇到的唯一问题是使用分组列表视图时。 “单元格”将仅包含组标题。还是很整洁 @MarkusMichel,是否有可能拥有 ViewCell 的高度?似乎总是-1【参考方案2】:

通常,您不会直接触摸您的细胞。相反,如果可能,您会尝试通过数据绑定来完成所有事情。

让我们使用 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>

在您的 Page 中,您将 ViewModel 设置为 BindingContext。

public partial class Page1 : ContentPage

    public Page1()
    
        InitializeComponent();
        BindingContext = new Page1ViewModel();
    

并且您的 ViewModel 在 ObservableCollection 中包含您的 Items MyItems,这意味着如果您添加或删除 Items,您的视图会更新。此属性绑定为您 List 的 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()。然后第二个单元格的标签将更新为"Hello World",因为它绑定到标题(请参阅 XAML Text="Binding Title")。

这一切背后的一般思想称为 MvvM 模式,您希望将视图与视图逻辑和模型(数据/服务,...)分开。

【讨论】:

我想这个答案对提出问题的人有所帮助,但在我的情况下,单元格的内容已经改变,这会影响它的高度。在 ios 中,高度似乎不会自动增长,因此单元格中的新单词会与下一个单元格中的单词重叠。我想在刚刚更改的单元格上使用 Cell.ForceUpdateSize,但在我看来,我需要获取实际的单元格对象才能做到这一点。

以上是关于Xamarin.Forms:获取列表视图的所有单元格/项目的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin.forms MVVM。列表视图仍然为空

Xamarin.forms 中的响应式网格

Xamarin Forms 如何制作可拖动的列表视图

使用绑定时滚动 Xamarin Forms 列表视图

用于 Xamarin.Forms 的带有标头 (LongListSelector) 的 ListView

设置列表视图项模板的背景颜色和列表背景颜色时,Xamarin Forms Listview 选定项背景颜色丢失