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

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

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

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

【问题讨论】:

    标签: listview xamarin xamarin.ios xamarin.forms


    【解决方案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,但在我看来,我需要获取实际的单元格对象才能做到这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多